/* ═══════════════════════════════════════════════════════════════════
   SwimLab — design tokens
   Concept: DEPTH. The page is a descent through water. Surfaces run
   deep → ice → white → deep, and that rhythm is what carries the design
   in the absence of photography (client assets pending).
   ═══════════════════════════════════════════════════════════════════ */

:root {
  /* ── Brand ─────────────────────────────────────────────────────────
     From the SwimLab logo: navy body, blue→turquoise gradient wordmark. */
  --navy: #0b2a5b;
  --blue: #1ca9e8;
  --turquoise: #2bd4d9;

  /* Contrast-critical. Both were darkened off the raw brand values to
     clear WCAG AA and were re-verified. DO NOT lighten either one. */
  --blue-deep: #0c6ba8;  /* link/number text on white & ice — 5.4:1 */
  --whatsapp: #0f7a37;   /* white-on-green — 5.44:1 */

  /* ── Water depth ───────────────────────────────────────────────────
     The dark end of the page. Less saturated and colder than the old
     #0a1f42 navy, which read as "corporate" rather than "underwater". */
  --deep: #0a1a2b;
  --deep-2: #06111d;     /* deepest — footer floor */
  --deep-3: #102a3f;     /* raised surface on deep (cards, fields) */

  /* ── Light surfaces ────────────────────────────────────────────────
     --ice replaces the old grey #f7f9fc. That grey is the single most
     recognisable "generated template" tell; the cold blue tint is what
     makes the light half of the page look art-directed. */
  --ice: #eef7fb;
  --ice-2: #f7fcfd;
  --white: #ffffff;

  /* ── Neutrals ──────────────────────────────────────────────────── */
  --grey-100: #eef2f7;
  --grey-200: #dde4ec;
  --grey-400: #97a3b4;
  --grey-600: #5b6b7f;
  --grey-800: #26313f;

  /* The warm sand accent that used to sit on the featured-price chip is
     retired. Against the navy card it read as a stray yellow rather than
     as a deliberate warm note, so the chip is plain white now. The page is
     blue and white, with no third hue anywhere. */

  /* ── Semantic ──────────────────────────────────────────────────── */
  --ink: var(--deep);
  --muted: var(--grey-600);
  --surface: var(--white);
  --surface-alt: var(--ice);
  --border: var(--grey-200);
  --accent: var(--blue);
  --accent-deep: var(--blue-deep);
  --accent-2: var(--turquoise);
  --success: #1a9e6b;

  /* On-dark text ramp. Every step here is measured against --deep-2 (the
     darkest surface any of them sits on) and clears WCAG AA at 4.5:1.
     --on-deep-faint was 0.38 and measured 3.53:1 on the hero stat labels
     and the footer meta line; 0.6 is the lowest value that passes. */
  --on-deep: rgba(255, 255, 255, 0.94);
  --on-deep-muted: rgba(255, 255, 255, 0.68);
  --on-deep-faint: rgba(255, 255, 255, 0.6);
  --hairline-deep: rgba(255, 255, 255, 0.14);

  /* ── Type ──────────────────────────────────────────────────────────
     Display: Newsreader, pinned wght 400 / opsz 40. Only ever w400 —
     the elegance comes from a light serif at scale, never from bolding.
     Emphasis is carried by the italic and by colour instead.
     Labels: system mono. Zero bytes, and every target platform ships a
     good one (iOS/macOS → SF Mono, Android → Roboto Mono). A third
     webfont family cannot be justified on a sub-3s mobile budget. */
  /* The *-Fallback families are metric-matched faces declared in index.html;
     they must sit immediately after the webfont so the swap causes no reflow. */
  --font-sans: "Inter", "Inter Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-display: "Newsreader", "Newsreader Fallback", Georgia, "Times New Roman", serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono", Menlo, Consolas, monospace;

  /* Fluid scale. Display tops out at 4.25rem — larger starts to break
     the longest Romanian strings at desktop width. */
  --fs-display: clamp(2.35rem, 1.6rem + 3.4vw, 4.25rem);
  --fs-h2: clamp(1.85rem, 1.4rem + 2vw, 2.9rem);
  --fs-h3: clamp(1.2rem, 1.08rem + 0.5vw, 1.45rem);
  --fs-stat: clamp(1.6rem, 1.3rem + 1.4vw, 2.4rem);
  --fs-body: 1.0625rem;
  --fs-sm: 0.9375rem;
  --fs-xs: 0.84rem;
  --fs-label: 0.72rem;

  --lh-display: 1.04;
  --lh-h2: 1.08;
  --lh-body: 1.6;

  /* Mono eyebrow treatment — one place to tune every label on the page. */
  --ls-label: 0.16em;

  /* ── Space ─────────────────────────────────────────────────────── */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;
  --space-10: 128px;

  /* ── Shape ─────────────────────────────────────────────────────── */
  --radius-sm: 10px;
  --radius-md: 16px;
  --radius-lg: 24px;
  --radius-xl: 32px;
  --radius-pill: 999px;

  /* Shadows are cast in the water colour, never in neutral black. */
  --shadow-sm: 0 1px 3px rgba(10, 26, 43, 0.07);
  --shadow-md: 0 10px 30px rgba(10, 26, 43, 0.10);
  --shadow-lg: 0 24px 60px rgba(10, 26, 43, 0.16);
  --shadow-deep: 0 30px 70px rgba(6, 17, 29, 0.45);

  /* ── Layout ────────────────────────────────────────────────────── */
  --container: 1180px;
  --measure: 62ch;
  --gutter: 20px;
  --bar-h: 64px;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

@media (min-width: 768px) {
  :root { --gutter: 40px; --bar-h: 76px; }
}
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  min-height: 100dvh;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  font-family: var(--font-sans);
  color: var(--ink);
  background: var(--surface);
}

img, picture, svg { display: block; max-width: 100%; }

input, button, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }

a { color: inherit; text-decoration: none; }

ul, ol { list-style: none; padding: 0; }

h1, h2, h3, h4 { text-wrap: balance; font-weight: 700; }
p { text-wrap: pretty; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
/* ═══════════════════════════════════════════════════════════════════
   Layout — sections, hero, and the page's structural systems
   ═══════════════════════════════════════════════════════════════════ */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Narrow measure for reading columns inside a full-width section. */
.container--narrow { max-width: 860px; }

section { padding-block: var(--space-8); position: relative; }

/* Every CTA on the page is an anchor jump to #formular, and the brand bar
   is fixed — without this the target lands underneath it. */
section[id] { scroll-margin-top: calc(var(--bar-h) + var(--space-4)); }

@media (min-width: 768px) {
  section { padding-block: var(--space-10); }
}

/* ── Surfaces ───────────────────────────────────────────────────────
   The page descends: deep → ice → white → ice → white → deep.
   Borders between sections are gone; the surface change IS the divider. */
.s-white { background: var(--surface); }
.s-ice { background: var(--surface-alt); }
.s-deep { background: var(--deep); color: var(--on-deep); }
.s-deep h2, .s-deep h3 { color: var(--on-deep); }
.s-deep p { color: var(--on-deep-muted); }

/* ── Section heads ──────────────────────────────────────────────────
   Left-aligned, always. The old centred-H2-on-every-section pattern is
   the loudest "generated page" signal there is.
   Widths are in rem, not ch: `ch` here resolves against the container's
   inherited 17px body font rather than the 46px display face, which
   crushed every heading into a three-line column. */
.section-head { margin-bottom: var(--space-7); max-width: 30rem; }
.section-head--wide { max-width: 40rem; }

.section-head h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  letter-spacing: -0.015em;
  color: var(--navy);
  text-wrap: balance;
  /* Romanian capitals carry tall diacritics (Î, Ș, Ț). At line-height 1.08
     they reach into the eyebrow above, so the heading is pushed down. */
  margin-top: var(--space-4);
}

/* The signature move: one phrase per heading set in italic + accent
   blue. Carried by <em>, so it degrades to plain italic without CSS. */
.section-head h2 em,
.hero h1 em,
.closing h2 em {
  font-style: italic;
  color: var(--accent-deep);
  font-weight: 400;
}
.s-deep .section-head h2 em,
.hero h1 em,
.closing h2 em { color: var(--blue); }

.s-deep .section-head h2 { color: var(--on-deep); }

/* .lede appears in two shapes: NESTED inside .section-head (the FAQ and the
   form aside) and as a SIBLING of it inside .head-split (sections 01, 02, 04).
   This selector used to be `.section-head .lede`, which silently missed every
   sibling case. The supporting paragraph in the three head-split sections was
   therefore rendering with no lede styling at all: 16px instead of 17, full
   ink instead of muted grey, line-height 1.5 instead of 1.6, and no measure
   cap, so it ran the entire column width. That, not the gap below it, is what
   made those two sections look wrong. Keep this selector unqualified. */
.lede {
  margin-top: var(--space-4);
  color: var(--muted);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  max-width: 46ch;
}
/* Beats `.s-deep p` (0,1,1), which would otherwise win on the deep sections. */
.s-deep .lede { color: var(--on-deep-muted); }
.lede a { color: var(--accent-deep); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }
.s-deep .lede a { color: var(--blue); }

/* Head + supporting column, side by side on desktop.
   The split owns the gap to whatever follows it, and .section-head's own
   margin is zeroed at EVERY width. Previously the margin was only zeroed
   above 900px and nothing replaced it, so on desktop the h2 and the lede
   sat flush on the card border / step rule below them: the italic descender
   of "ședințele" actually touched the card edge. Below 900px the opposite
   bug applied, the head's 48px margin stacking on the 24px grid gap.
   The bottom margin is wider than a plain .section-head's --space-7 on
   purpose: these headings sit directly above a hairline or a card border, and
   the Romanian descenders (ș, ț in italic) need the extra optical clearance. */
.head-split {
  display: grid;
  gap: var(--space-6);
  /* Same 48px that `.section-head` gives section 03, at every width. This is
     deliberately NOT larger: sections 01, 02 and 04 sit in the same vertical
     rhythm as 03, and a head-split that opened wider than a plain section
     head made those sections read as a different page. Earlier passes tried
     96px and 128px here; both broke the rhythm. */
  margin-bottom: var(--space-7);
}
.head-split .section-head { margin-bottom: 0; }
@media (min-width: 900px) {
  .head-split {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-8);
    /* start, not end. `end` bottom-aligned the heading block and the lede so
       both landed hard on the rule below, with dead space stacked above the
       lede. Aligning to the top instead lets the supporting paragraph begin
       with the head and leaves the whitespace where it belongs, underneath. */
    align-items: start;
  }
  /* Drop the right-hand column by exactly one eyebrow line.
     Without this the lede sat 17px BELOW the eyebrow and 17px ABOVE the h2:
     level with neither, which is what made the two columns read as unaligned
     (measured on 2026-09-01, all three splits, at 1440px).
     `.section-head` stacks eyebrow + h2's own --space-4 top margin; the lede
     only has that same --space-4, so it is short by one eyebrow line box.
     An earlier pass refused a fixed nudge here because --fs-h2 is FLUID and
     any offset measured against the heading drifts across the breakpoint
     range. That reasoning does not apply to this offset: it is measured
     against the EYEBROW, and --fs-label is a fixed 0.72rem, so 1.5 line
     heights of it is a constant 1.08rem at every viewport width. Derive it,
     never hardcode 17px — if --fs-label ever changes this follows it. */
  .head-split > :not(.section-head) {
    padding-top: calc(var(--fs-label) * 1.5);  /* 1.5 = .eyebrow's inherited body line-height */
  }
}

/* ═══════════════════════════════════════════════════════════════════
   HERO — photographed water
   The water is now shot, not built. Four layers, back to front:
     .hero__water    depth gradient, paints first, covers the decode gap
     .hero__photo    the photograph, the LCP element
     .hero__caustics one faint drifting net, for surface shimmer only
     .hero__scrim    contrast guarantee for the type
   The engineered light shafts and the second caustic net are deleted, not
   disabled: the photograph supplies both, and layering synthetic ones over
   real ones read as haze. Motion is still transform-only, so it stays on
   the compositor.
   ═══════════════════════════════════════════════════════════════════ */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  isolation: isolate;
  color: var(--on-deep);
  background: var(--deep-2);
}

/* Reserve for the consent card, applied in the very first layout via the
   `consent-pending` class the head script sets. It exists so the fixed card
   can never cover the hero's CTA or stat rail — the original bug — but,
   unlike the JS-measured version it replaces, it costs no layout shift.
   Values are measured against the real card at each width, rounded up. */
.consent-pending .hero { padding-bottom: var(--consent-reserve); }
:root { --consent-reserve: 180px; }
@media (min-width: 480px) { :root { --consent-reserve: 168px; } }
@media (min-width: 760px) { :root { --consent-reserve: 158px; } }
/* The reserve only works while the hero is exactly 100svh: it relies on
   `justify-content: flex-end` having free space to push content up out of the
   card's way. On desktop the hero stack overflowed 100svh (909px at 1440x839),
   so there was no free space, the reserve became inert, and the card sat on top
   of the "6 luni" and "100 lei" rail items on every first visit. Reclaiming the
   hero's oversized top padding puts the stack back under 100svh, which
   re-engages flex-end and lets the reserve do its job. 140px of empty water sat
   between the brand bar and the eyebrow, so this costs nothing visually. */
@media (min-width: 1024px) { .hero { padding-top: var(--space-7); } }

/* Dismissing is a user action, so the reflow when this class is removed is
   attributed to input and is excluded from CLS. */
.hero { transition: none; }

.hero__media { position: absolute; inset: 0; z-index: 0; }

/* Layer 2 — the photograph. Real water, surface and light shafts at the top,
   absorbed to near-black at the bottom, which is exactly where the headline,
   the CTA and the stat rail sit. It is a real image element, not a
   background-image, so it can carry srcset + fetchpriority and be preloaded:
   this is the LCP element and a CSS background cannot be prioritised the
   same way.
   The synthetic shafts and the second caustic net that used to live here are
   gone. The photo has real ones; drawing more on top read as haze. */
.hero__photo {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.hero__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* 45% keeps the surface line in frame on wide screens and lands the
     bright shaft band behind the headline on tall phone viewports. */
  object-position: center 45%;
  display: block;
}

/* Layer 1 — depth. Paints under the photo, so a slow image decode shows
   deep water rather than a white flash. Also the fallback if the photo 404s. */
.hero__water {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(110% 70% at 24% -12%, #175a86 0%, rgba(23, 90, 134, 0) 56%),
    radial-gradient(85% 55% at 84% 0%, #135f7d 0%, rgba(19, 95, 125, 0) 52%),
    linear-gradient(178deg, #0b2d47 0%, #081524 100%);
  background-color: #081524;
}

/* Layer 3 — caustics.
   feTurbulence type="turbulence" produces absolute-value noise, whose
   creases form the filament net that reads as pool caustics. A steeply
   NEGATIVE alpha coefficient in feColorMatrix keeps only those creases,
   so the output is thin bright lines rather than blobs. (fractalNoise
   with a positive coefficient was tried first and rendered as clouds.)
   The result is rasterised ONCE as a data URI — animating baseFrequency
   would re-run the filter every frame and destroy the frame budget, so
   the finished bitmap is translated on the compositor instead.
   Now that a real photograph sits underneath, this layer's job changed:
   it no longer HAS to look like water, it only has to keep the surface
   shimmering. Opacity dropped 0.38 -> 0.12 accordingly. Anything higher
   read as dirt on the lens rather than as light on water. */
.hero__caustics {
  position: absolute;
  inset: -30%;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='560' height='560'%3E%3Cfilter id='c' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='turbulence' baseFrequency='0.018 0.05' numOctaves='2' seed='11' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.62 0 0 0 0 0.9 0 0 0 0 1 -7.5 0 0 0 0.86'/%3E%3C/filter%3E%3Crect width='560' height='560' filter='url(%23c)'/%3E%3C/svg%3E");
  background-size: 560px 560px;
  opacity: 0.12;
  mix-blend-mode: screen;
  will-change: transform;
  /* Pooled toward the upper right, where the photograph's own surface
     light already is, so the two reinforce instead of fighting. */
  -webkit-mask-image: radial-gradient(85% 70% at 72% 26%, rgba(0,0,0,1) 0%, rgba(0,0,0,0.45) 52%, rgba(0,0,0,0) 84%);
  mask-image: radial-gradient(85% 70% at 72% 26%, rgba(0,0,0,1) 0%, rgba(0,0,0,0.45) 52%, rgba(0,0,0,0) 84%);
}

/* Layer 4 — scrim. Guarantees text contrast regardless of what is underneath.
   The radial is deliberately off-centre: it pools darkness behind the
   left-aligned headline block so the type sits on calm water while the right
   side stays lively.
   The ramp is the load-bearing part. The hero's copy block starts at roughly
   15% of the section height, so the gradient stays light only to 15% (where
   the photograph's surface and light shafts are, and where nothing but the
   brand bar sits) and then climbs hard. An earlier pass held it light to 48%
   to show more of the photo, which put the eyebrow and the trust list on
   bright turquoise water and made both close to unreadable.
   The radial is wide enough (96%) to reach the right-hand trust column too;
   at 72% it only covered the headline and left that list on bare water. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(96% 66% at 38% 66%, rgba(6, 17, 29, 0.70) 0%, rgba(6, 17, 29, 0) 78%),
    linear-gradient(180deg, rgba(6, 17, 29, 0.62) 0%, rgba(6, 17, 29, 0.20) 14%, rgba(6, 17, 29, 0.56) 26%, rgba(6, 17, 29, 0.64) 40%, rgba(6, 17, 29, 0.70) 58%, rgba(6, 17, 29, 0.80) 80%, rgba(6, 17, 29, 0.92) 100%);
}

.hero__content {
  position: relative;
  z-index: 2;
  width: 100%;
  padding-top: calc(var(--bar-h) + var(--space-3));
  padding-bottom: var(--space-5);
  margin-top: auto;
}

.hero h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: -0.02em;
  max-width: 16ch;
  margin-top: var(--space-4);
  text-wrap: balance;
}

.hero__subtitle {
  margin-top: var(--space-4);
  max-width: 46ch;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--on-deep-muted);
}

/* Actions are left-aligned with the headline. The previous build
   centred .contact-links inside a full-width container, which is why
   the phone/WhatsApp buttons drifted right and read as broken. */
.hero__actions {
  margin-top: var(--space-5);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

/* ── Hero stat rail ─────────────────────────────────────────────────
   Hairline-divided facts along the floor of the hero. Every figure here
   is already a client-supplied claim from the brief — nothing invented. */
.hero__rail {
  position: relative;
  z-index: 2;
  border-top: 1px solid var(--hairline-deep);
  margin-top: var(--space-5);
}
.rail-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
.rail-item {
  padding: var(--space-3) var(--space-4) var(--space-4) 0;
  border-left: 1px solid var(--hairline-deep);
  padding-left: var(--space-4);
}
.rail-item:first-child { border-left: 0; padding-left: 0; }
.rail-item__figure {
  font-family: var(--font-display);
  font-size: var(--fs-stat);
  line-height: 1;
  color: var(--on-deep);
  font-weight: 400;
}
.rail-item__label {
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--on-deep-faint);
  line-height: 1.35;
}
@media (max-width: 560px) {
  .rail-item__label { font-size: 0.62rem; letter-spacing: 0.1em; }
  .rail-item { padding-inline: var(--space-3) var(--space-2); }
  .rail-item:first-child { padding-left: 0; }
}

/* Phones: the hero has to hold headline, subtitle, CTA, two contact
   buttons, the stat rail AND a full-width consent card inside ~844px.
   Density is tightened here rather than in the desktop composition, which
   has the room. Measured target: everything clears the consent card on a
   390×844 viewport. */
@media (max-width: 560px) {
  .hero__content { padding-top: var(--bar-h); padding-bottom: var(--space-4); }
  .hero h1 { margin-top: var(--space-3); }
  .hero__subtitle { margin-top: var(--space-3); }
  .hero__actions { margin-top: var(--space-4); }
  .hero__rail { margin-top: var(--space-4); }
  .rail-item { padding-block: var(--space-3) var(--space-3); }
  .rail-item__figure { font-size: 1.4rem; }
}

/* Short viewports (landscape phones, small laptops) cannot hold headline,
   CTA, rail and the consent card at once. The rail is the supporting act,
   so it is what gives way — the headline and the CTA never do. */
@media (max-height: 680px) {
  .hero__rail { display: none; }
  .hero { min-height: auto; }
  .hero__content { padding-bottom: var(--space-6); }
}

/* Waterline closing the hero — a single lit hairline, brightest under the
   headline. A beaded lane-rope was tried here and read as clip-art. */
.hero__rope {
  position: relative;
  z-index: 2;
  height: 1px;
  background: linear-gradient(90deg,
    rgba(28, 169, 232, 0) 0%,
    rgba(28, 169, 232, 0.55) 18%,
    rgba(120, 220, 255, 0.75) 42%,
    rgba(28, 169, 232, 0.3) 72%,
    rgba(28, 169, 232, 0) 100%);
}

/* ═══════════════════════════════════════════════════════════════════
   TRUST RAIL
   ═══════════════════════════════════════════════════════════════════ */
/* ── Hero trust facts ───────────────────────────────────────────────
   These four were a standalone .trust-bar section below the hero. On a
   1440x839 desktop that put them ~70px under the fold while 649px of hero
   sat empty to the right of the headline, so they now fill that column and
   read as part of the first screen. Below 1024px nothing moves sideways:
   they stay a two-up band under the copy, exactly as before. */
/* Hidden below 1024px, where the .trust-bar band below the hero shows these
   same four facts instead. Keeping it out of the hero on small screens is what
   preserves the --consent-reserve budget: inside, it pushed the stat rail
   under the consent card on a first visit. */
.hero__trust { display: none; }

.hero__trust {
  position: relative;
  z-index: 2;
  list-style: none;
  margin: var(--space-5) 0 0;
  padding: 0;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}
.hero__trust li {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4) var(--space-4) 0;
  border-left: 1px solid var(--hairline-deep);
  padding-left: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--on-deep-muted);
}
.hero__trust li:nth-child(odd) { border-left: 0; padding-left: 0; }
.hero__trust svg { width: 16px; height: 16px; flex: none; stroke: var(--blue); fill: none; stroke-width: 1.6; }

/* Source order is copy → actions → trust; mobile renders exactly that, so the
   CTA still sits directly under the subtitle where it always did. */
.hero__content { display: flex; flex-direction: column; }
.hero__copy    { order: 1; }
.hero__actions { order: 2; }
.hero__trust   { order: 3; }

/* Desktop: copy and trust share row 1, the CTA row spans both columns beneath.
   Letting the actions span is what keeps the primary CTA, phone and WhatsApp
   on ONE line: confining them to the narrowed left column wrapped them onto a
   second row and pushed the stat rail off the fold. `align-items: end` bottom
   -aligns the trust list with the subtitle instead of floating it mid-air. */
@media (min-width: 1024px) {
  .hero__content {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(250px, 330px);
    column-gap: var(--space-7);
    align-items: end;
  }
  .hero__copy    { grid-column: 1; grid-row: 1; }
  .hero__trust   { grid-column: 2; grid-row: 1; }
  .hero__actions { grid-column: 1 / -1; grid-row: 2; }
  .hero__trust {
    display: grid;
    grid-template-columns: 1fr;
    margin-top: 0;
    padding-left: var(--space-5);
    border-left: 1px solid var(--hairline-deep);
  }
  .hero__trust li {
    border-left: 0;
    padding: var(--space-3) 0;
  }
  .hero__trust li:nth-child(odd) { padding-left: 0; }

  /* The mobile band is redundant once the hero column is showing. */
  .trust-bar { display: none; }
}

/* ── Trust band (below 1024px) ──────────────────────────────────────
   The small-screen home for the same four facts. Sits under the hero so the
   hero's own height budget, and therefore the consent reserve, is untouched. */
.trust-bar { background: var(--deep-2); color: var(--on-deep); padding-block: var(--space-6); }
.trust-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}
.trust-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4) var(--space-4) 0;
  border-left: 1px solid var(--hairline-deep);
  padding-left: var(--space-4);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--on-deep-muted);
}
.trust-item:nth-child(odd) { border-left: 0; padding-left: 0; }
.trust-item svg { width: 16px; height: 16px; flex: none; stroke: var(--blue); fill: none; stroke-width: 1.6; }

/* ═══════════════════════════════════════════════════════════════════
   AUDIENCE CARDS ("Pentru cine")
   Each card carries a lane-rope ribbon where a photo will eventually
   go. It is honest decoration — it does not pretend to be data — and
   swapping in a real image later is a single-element replacement.
   ═══════════════════════════════════════════════════════════════════ */
.cards-grid { display: grid; gap: var(--space-5); grid-template-columns: 1fr; }
@media (min-width: 820px) { .cards-grid { grid-template-columns: repeat(3, 1fr); } }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s var(--ease-out);
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }

/* Photo slot.
   Each card shows the water its group actually works in, getting deeper
   down the row: shallow for the babies, deepest for the adults. That is
   the page's depth idea doing real work — an earlier pass put a beaded
   lane-rope graphic here and it read as clip-art.
   A 2026-08-31 pass swapped in real client photographs here and Denis
   rejected them on 2026-09-01: the three usable frames were crops of the
   same gallery images, and at 124px tall they read as thumbnails of the
   Galerie section rather than as three distinct audiences. Reverted to the
   engineered water verbatim from deploy cbdcf45b. The generated files are
   still in assets/cards/ and build-images.py still knows how to rebuild
   them; nothing references them.
   To swap in real photography later, drop a background-image on
   .card__media; the ::after caustic layer is what gives way. */
.card__media {
  height: 124px;
  border-radius: var(--radius-md);
  margin-bottom: var(--space-5);
  overflow: hidden;
  position: relative;
  background: linear-gradient(168deg, #2a89bd 0%, #12496f 100%);
}
.card--b .card__media { background: linear-gradient(168deg, #17698f 0%, #0c3757 100%); }
.card--c .card__media { background: linear-gradient(168deg, #0f4a6d 0%, #071c2e 100%); }

.card__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='460' height='460'%3E%3Cfilter id='c' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='turbulence' baseFrequency='0.026 0.058' numOctaves='2' seed='5' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.62 0 0 0 0 0.9 0 0 0 0 1 -7.5 0 0 0 0.86'/%3E%3C/filter%3E%3Crect width='460' height='460' filter='url(%23c)'/%3E%3C/svg%3E");
  background-size: 300px 300px;
  opacity: 0.34;
  mix-blend-mode: screen;
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgba(0,0,0,0.25) 78%, rgba(0,0,0,0) 100%);
  mask-image: linear-gradient(180deg, rgba(0,0,0,1) 0%, rgba(0,0,0,0.25) 78%, rgba(0,0,0,0) 100%);
}

/* Waterline — a soft band of surface light, sitting lower as the water
   gets deeper. Rendered as a blurred glow rather than a 1px rule: at one
   pixel it read as a scratch across the artwork. */
.card__media::before {
  content: "";
  position: absolute;
  left: -5%; right: -5%; top: 20%;
  height: 12px;
  z-index: 1;
  background: linear-gradient(180deg, rgba(190, 235, 255, 0) 0%, rgba(190, 235, 255, 0.34) 50%, rgba(190, 235, 255, 0) 100%);
  filter: blur(3px);
}
.card--b .card__media::before { top: 40%; }
.card--c .card__media::before { top: 58%; }

.card__age {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent-deep);
  margin-bottom: var(--space-3);
}
.card h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-h3);
  line-height: 1.2;
  color: var(--navy);
  margin-bottom: var(--space-3);
  letter-spacing: -0.01em;
}
.card p { color: var(--muted); font-size: var(--fs-sm); line-height: var(--lh-body); }

/* Supporting notes under the cards */
.note-block {
  margin-top: var(--space-6);
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
  border-left: 2px solid var(--accent);
  padding: var(--space-2) 0 var(--space-2) var(--space-5);
}
.note-block__icon { flex: none; width: 22px; height: 22px; color: var(--accent-deep); margin-top: 2px; }
.note-block p { color: var(--grey-800); font-size: var(--fs-sm); line-height: var(--lh-body); max-width: var(--measure); }

/* Sans, not mono. Mono is reserved for short labels; a full sentence set
   in it reads as terminal output rather than as a footnote. */
.note-plain {
  margin-top: var(--space-5);
  color: var(--muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  max-width: var(--measure);
}

/* ═══════════════════════════════════════════════════════════════════
   STEPS — a lane, not four blobs
   ═══════════════════════════════════════════════════════════════════ */
.steps { display: grid; gap: var(--space-6); grid-template-columns: 1fr; }
@media (min-width: 820px) { .steps { grid-template-columns: repeat(4, 1fr); gap: 0; } }

.step {
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
  position: relative;
}
@media (min-width: 820px) {
  .step { padding-right: var(--space-5); }
}
.step__num {
  font-family: var(--font-display);
  font-size: 2rem;
  line-height: 1;
  /* --accent on --ice measured 2.45:1, under the 3:1 large-text floor.
     --blue-deep is the contrast-verified blue. */
  color: var(--accent-deep);
  display: block;
  margin-bottom: var(--space-4);
}
.s-deep .step__num { color: var(--blue); }
.s-deep .step { border-top-color: var(--hairline-deep); }
.step h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-h3);
  line-height: 1.2;
  color: var(--navy);
  margin-bottom: var(--space-3);
}
.s-deep .step h3 { color: var(--on-deep); }
.step p { color: var(--muted); font-size: var(--fs-sm); line-height: var(--lh-body); }
.s-deep .step p { color: var(--on-deep-muted); }

/* ═══════════════════════════════════════════════════════════════════
   PRICING
   ═══════════════════════════════════════════════════════════════════ */
.price-layout { display: grid; gap: var(--space-6); }
@media (min-width: 980px) {
  .price-layout { grid-template-columns: 1.05fr 1fr; gap: var(--space-8); align-items: start; }
}

/* Featured — the only dark card in a light section. */
.price-featured {
  position: relative;
  background: var(--deep);
  color: var(--on-deep);
  border-radius: var(--radius-xl);
  padding: var(--space-7) var(--space-6) var(--space-6);
  box-shadow: var(--shadow-deep);
  overflow: hidden;
  isolation: isolate;
}
.price-featured::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(100% 70% at 20% 0%, rgba(28, 169, 232, 0.30) 0%, rgba(28, 169, 232, 0) 62%);
  z-index: -1;
}
/* White, not the old sand. #0a1a2b on #fff measures 17.1:1, and white is the
   only chip colour that reads as part of the brand rather than as a third hue. */
.price-chip {
  position: absolute;
  top: var(--space-5);
  right: var(--space-5);
  background: var(--white);
  color: var(--deep);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 7px 13px;
  border-radius: var(--radius-pill);
  font-weight: 500;
}
.price-featured__label {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--blue);
}
.price-featured__amount {
  font-family: var(--font-display);
  font-size: clamp(3rem, 2.2rem + 3.4vw, 4.6rem);
  line-height: 1;
  margin-top: var(--space-4);
  letter-spacing: -0.02em;
}
.price-featured__amount span {
  font-size: 0.34em;
  font-family: var(--font-sans);
  color: var(--on-deep-muted);
  letter-spacing: 0;
  margin-left: 2px;
}
.price-featured p.note {
  margin-top: var(--space-4);
  color: var(--on-deep-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  max-width: 34ch;
}
.price-featured .btn-primary { margin-top: var(--space-6); }

.price-includes {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--hairline-deep);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.price-includes li {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  font-size: var(--fs-sm);
  color: var(--on-deep-muted);
  line-height: 1.45;
}
.price-includes svg { width: 15px; height: 15px; flex: none; margin-top: 4px; stroke: var(--blue); fill: none; stroke-width: 2.4; }

/* Package list — hairline rows, not boxes. */
.price-list { display: flex; flex-direction: column; }
.price-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-5) 0;
  border-bottom: 1px solid var(--border);
}
.price-row:first-child { border-top: 1px solid var(--border); }
.price-row__name {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  color: var(--navy);
  font-weight: 400;
}
.price-row__meta {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--muted);
  margin-top: var(--space-2);
}
.price-row__amount {
  font-family: var(--font-display);
  font-size: var(--fs-stat);
  color: var(--accent-deep);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.price-more { margin-top: var(--space-6); }
.price-more summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent-deep);
  cursor: pointer;
  list-style: none;
  padding: var(--space-2) 0;
}
.price-more summary::-webkit-details-marker { display: none; }
.price-more summary::after { content: "+"; font-size: 1.1rem; line-height: 1; transition: transform 0.25s var(--ease); }
.price-more[open] summary::after { transform: rotate(45deg); }

.price-table-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
  margin-top: var(--space-6);
}
@media (min-width: 700px) { .price-table-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .price-table-grid { grid-template-columns: repeat(4, 1fr); } }

.price-table-group h3 {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--navy);
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--accent);
}
.price-table-group ul { display: flex; flex-direction: column; gap: var(--space-3); }
.price-table-group li {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--fs-sm);
  color: var(--muted);
}
.price-table-group li b {
  color: var(--navy);
  white-space: nowrap;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.price-table-group .group-note {
  color: var(--muted);
  font-size: var(--fs-xs);
  margin-bottom: var(--space-4);
  line-height: 1.5;
}
.price-duration-note {
  margin-top: var(--space-6);
  color: var(--muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  max-width: var(--measure);
}

/* ═══════════════════════════════════════════════════════════════════
   FAQ — hairline rows, asymmetric layout. No boxes.
   ═══════════════════════════════════════════════════════════════════ */
.faq-layout { display: grid; gap: var(--space-6); }
@media (min-width: 960px) {
  .faq-layout { grid-template-columns: 0.8fr 1.2fr; gap: var(--space-8); align-items: start; }
  .faq-layout .section-head { position: sticky; top: calc(var(--bar-h) + var(--space-5)); margin-bottom: 0; }
}

.faq-list { display: flex; flex-direction: column; }
.faq-item { border-bottom: 1px solid var(--border); }
.faq-item:first-child { border-top: 1px solid var(--border); }

.faq-item summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-5);
  padding: var(--space-5) 0;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 400;
  line-height: 1.32;
  color: var(--navy);
  transition: color 0.2s var(--ease);
}
.faq-item summary:hover { color: var(--accent-deep); }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "";
  flex: none;
  width: 13px;
  height: 13px;
  margin-top: 7px;
  background:
    linear-gradient(var(--accent-deep), var(--accent-deep)) center/100% 1.5px no-repeat,
    linear-gradient(var(--accent-deep), var(--accent-deep)) center/1.5px 100% no-repeat;
  transition: transform 0.28s var(--ease);
}
.faq-item[open] summary::after { transform: rotate(135deg); }
.faq-item p {
  padding-bottom: var(--space-5);
  padding-right: var(--space-7);
  color: var(--muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  max-width: var(--measure);
}

/* ═══════════════════════════════════════════════════════════════════
   TESTIMONIALS — an honest reserved state
   The client's brief says to reserve the space ("se rezervă spațiul").
   It does NOT say to fake it: the previous build showed ★★★★★ above
   placeholder text, which costs trust on a conversion page. The stars
   are gone; what remains reads as deliberately awaiting content.
   ═══════════════════════════════════════════════════════════════════ */
.testi-grid { display: grid; gap: var(--space-5); grid-template-columns: 1fr; }
@media (min-width: 820px) { .testi-grid { grid-template-columns: repeat(3, 1fr); } }

.testi-card {
  border: 1px dashed var(--grey-200);
  border-radius: var(--radius-lg);
  padding: var(--space-6) var(--space-5);
  background: transparent;
  display: flex;
  flex-direction: column;
  min-height: 200px;
}
/* --grey-400 measured 2.56:1 on white. Muted has to be legible too: a
   placeholder nobody can read is not a lighter placeholder, it is a
   broken one. --grey-600 keeps the recessive read and clears AA. */
.testi-card__slot {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-4);
}
.testi-card .quote {
  font-family: var(--font-display);
  font-size: 1.2rem;
  line-height: 1.45;
  color: var(--muted);
  font-style: italic;
}
.testi-card__author {
  margin-top: auto;
  padding-top: var(--space-5);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ═══════════════════════════════════════════════════════════════════
   FORM — the money section
   ═══════════════════════════════════════════════════════════════════ */
.form-layout { display: grid; gap: var(--space-7); }
@media (min-width: 960px) {
  .form-layout { grid-template-columns: 1fr 1.05fr; gap: var(--space-9); align-items: start; }
}

/* rem, not ch — same trap as .section-head: `ch` resolves against the
   inherited body font, not the display face, and crushed this to 3 lines. */
.form-aside .section-head { max-width: 28rem; }
.form-points {
  margin-top: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.form-points li {
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
  padding-top: var(--space-4);
  border-top: 1px solid var(--hairline-deep);
  color: var(--on-deep-muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
}
.form-points .n {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.08em;
  color: var(--blue);
  flex: none;
  padding-top: 3px;
}

.form-card {
  background: var(--white);
  border-radius: var(--radius-xl);
  padding: var(--space-6);
  box-shadow: var(--shadow-deep);
  color: var(--ink);
}
@media (min-width: 560px) { .form-card { padding: var(--space-7); } }

/* The card is a LIGHT island inside a dark section. `.s-deep p` (0,1,1)
   outranks `.form-consent` (0,1,0), which rendered the consent line and
   the phone line as white text on a white card — invisible. Everything
   inside the card re-asserts its light-surface colours. */
.s-deep .form-card p { color: var(--muted); }
.s-deep .form-card h2,
.s-deep .form-card h3 { color: var(--navy); }

.field { margin-bottom: var(--space-4); }
.field label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--grey-600);
  margin-bottom: var(--space-2);
}
.field input, .field select {
  width: 100%;
  padding: 15px 16px;
  border: 1px solid var(--grey-200);
  border-radius: var(--radius-sm);
  background: var(--ice-2);
  font-size: 1rem;
  color: var(--ink);
  min-height: 52px;
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease), background 0.2s var(--ease);
  appearance: none;
}
.field select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%235b6b7f' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  padding-right: 42px;
}
.field input:focus, .field select:focus {
  outline: none;
  background: var(--white);
  border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(28, 169, 232, 0.16);
}
.field-row { display: grid; gap: var(--space-4); }
@media (min-width: 480px) { .field-row { grid-template-columns: 1fr 1fr; } }
.field-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-consent {
  font-size: var(--fs-xs);
  color: var(--muted);
  margin-block: var(--space-5);
  line-height: 1.55;
}
.form-consent a { color: var(--accent-deep); text-decoration: underline; }

.form-sub {
  margin-top: var(--space-5);
  color: var(--muted);
  font-size: var(--fs-sm);
  line-height: var(--lh-body);
  text-align: center;
}
.form-sub a { color: var(--accent-deep); font-weight: 600; }

.form-status {
  margin-top: var(--space-4);
  padding: var(--space-4);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
  line-height: 1.5;
  display: none;
}
.form-status.is-visible { display: block; }
.form-status.ok { background: #e7f7f0; color: #0d6b48; border: 1px solid #b8ebd6; }
.form-status.err { background: #fdeeee; color: #a11e17; border: 1px solid #f6c6c4; }

/* ═══════════════════════════════════════════════════════════════════
   LOCATION
   ═══════════════════════════════════════════════════════════════════ */
.loc-grid { display: grid; gap: var(--space-6); grid-template-columns: 1fr; }
@media (min-width: 900px) { .loc-grid { grid-template-columns: 1.15fr 1fr; gap: var(--space-8); align-items: start; } }

.loc-map {
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--grey-100);
  aspect-ratio: 4/3;
}
.loc-map iframe { width: 100%; height: 100%; border: 0; }

.loc-info dt {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent-deep);
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}
.loc-info dt:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.loc-info dd { color: var(--grey-800); font-size: var(--fs-sm); margin-top: var(--space-3); line-height: 1.65; }

.access-box {
  margin-top: var(--space-7);
  border-left: 2px solid var(--accent);
  padding: var(--space-2) 0 var(--space-2) var(--space-5);
}
.access-box h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-h3);
  color: var(--navy);
  margin-bottom: var(--space-3);
}
.access-box p { color: var(--grey-800); font-size: var(--fs-sm); line-height: 1.7; max-width: var(--measure); }
.access-box p + p { margin-top: var(--space-3); }

/* ═══════════════════════════════════════════════════════════════════
   CLOSING CTA — the page dives back down
   ═══════════════════════════════════════════════════════════════════ */
.closing {
  position: relative;
  background: var(--deep);
  color: var(--on-deep);
  overflow: hidden;
  isolation: isolate;
  text-align: center;
  padding-block: var(--space-9);
}
@media (min-width: 768px) { .closing { padding-block: var(--space-10); } }

/* Layers, back to front: water (-4), photo (-3), caustics (-2), scrim (-1).
   The closing band is the floor of the page's descent, so its photograph is
   the DEEP crop: surface ripple along the very top, calm dark water below.
   The hero is the same water seen from just under the surface; this is the
   same water seen from further down. That is the whole reason there are two
   different frames rather than one reused twice. */
.closing__water {
  position: absolute;
  inset: 0;
  z-index: -4;
  background:
    radial-gradient(90% 70% at 50% -10%, #1a5f8f 0%, rgba(26, 95, 143, 0) 62%),
    linear-gradient(180deg, #0d2437 0%, #06111d 100%);
}
.closing__photo {
  position: absolute;
  inset: 0;
  z-index: -3;
  overflow: hidden;
}
.closing__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* 34%, not centre: the upper part of this crop still carries the surface
     ripple and its texture. Lower down the frame is close to flat black and
     the photograph stops being visible as a photograph at all. */
  object-position: center 34%;
  display: block;
}
/* The centred white heading and body sit dead in the middle of this band, so
   the scrim pools there rather than at the edges the hero's does. */
.closing__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(70% 90% at 50% 50%, rgba(6, 17, 29, 0.52) 0%, rgba(6, 17, 29, 0.14) 70%, rgba(6, 17, 29, 0.06) 100%),
    linear-gradient(180deg, rgba(6, 17, 29, 0.30) 0%, rgba(6, 17, 29, 0.14) 40%, rgba(6, 17, 29, 0.62) 100%);
}
.closing__caustics {
  position: absolute;
  inset: -30%;
  z-index: -2;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='560' height='560'%3E%3Cfilter id='e' x='0' y='0' width='100%25' height='100%25'%3E%3CfeTurbulence type='turbulence' baseFrequency='0.018 0.05' numOctaves='2' seed='17' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.62 0 0 0 0 0.9 0 0 0 0 1 -7.5 0 0 0 0.86'/%3E%3C/filter%3E%3Crect width='560' height='560' filter='url(%23e)'/%3E%3C/svg%3E");
  background-size: 560px 560px;
  /* Same reasoning as .hero__caustics: over a photograph this layer only
     has to keep the surface alive, so 0.34 -> 0.14. */
  opacity: 0.14;
  mix-blend-mode: screen;
  will-change: transform;
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,0.9) 22%, rgba(0,0,0,0.35) 55%, rgba(0,0,0,0) 78%);
  mask-image: linear-gradient(180deg, rgba(0,0,0,0.9) 22%, rgba(0,0,0,0.35) 55%, rgba(0,0,0,0) 78%);
}
/* Fade the closing band into the footer floor.
   Measured at the seam before this existed: the photograph read rgb(3,25,42)
   on the last row of .closing and the footer starts flat on --deep-2
   rgb(6,17,29). That 13-point drop in blue landed on a single pixel row and
   read as a hard horizontal cut straight across the page.
   The gradient ends on --deep-2 exactly, so the photo has already become the
   footer colour by the time the sections actually meet and there is nothing
   left to see. z-index -1 keeps it above the scrim (also -1, but earlier in
   DOM order) and below the heading and buttons, so it can only ever darken
   BEHIND the white text, never over it. Height is relative because .closing
   loses ~100px of padding below 768px and a fixed 220px band would have
   swallowed the CTA row on a phone. */
.closing::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: clamp(120px, 34%, 240px);
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    180deg,
    rgba(6, 17, 29, 0) 0%,
    rgba(6, 17, 29, 0.55) 48%,
    rgba(6, 17, 29, 0.88) 78%,
    var(--deep-2) 100%
  );
}

.closing h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  letter-spacing: -0.015em;
  max-width: 18ch;
  margin-inline: auto;
  margin-top: var(--space-5);
}
.closing p {
  margin-top: var(--space-5);
  color: var(--on-deep-muted);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  max-width: 46ch;
  margin-inline: auto;
}
.closing__actions {
  margin-top: var(--space-7);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
}
.closing .eyebrow { justify-content: center; }

/* ═══════════════════════════════════════════════════════════════════
   GALLERY — filmstrip
   All fifteen of the client's photographs, small, running left to right,
   with arrows. Roughly eight are visible at 1440, seven at 1180, three on
   a phone (five will not fit on a 390px screen at any size a face survives).

   The strip is a direct child of <section>, NOT of .container, so it can run
   to the viewport edge while its first frame still lines up with the page's
   text column. The left inset is `calc((100% - container) / 2 + gutter)`
   using PERCENT, not vw: vw includes the scrollbar and would push the page
   into horizontal overflow on desktop.
   Bleeding right is the point. A strip that stops at the container edge
   looks like a row that happens to be cut off; one that runs off the screen
   reads as film continuing past the frame, which is the affordance.
   ═══════════════════════════════════════════════════════════════════ */
.gal-head { margin-bottom: var(--space-6); }
@media (min-width: 900px) { .gal-head { margin-bottom: var(--space-7); } }

/* The arrows live with the head, not over the photographs. Overlaid controls
   would cover a face in a 150px frame, and there is no spare room inside one. */
.gal-nav {
  display: flex;
  gap: var(--space-3);
  margin-top: var(--space-5);
}
@media (min-width: 900px) { .gal-nav { justify-content: flex-start; } }

.gal-btn {
  width: 46px;
  height: 46px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--hairline-deep);
  background: rgba(255, 255, 255, 0.06);
  color: var(--on-deep);
  display: grid;
  place-items: center;
  transition: background 0.25s var(--ease), border-color 0.25s var(--ease),
              transform 0.25s var(--ease-out), opacity 0.25s var(--ease);
}
.gal-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.9; }
.gal-btn:hover:not(:disabled) {
  background: var(--blue-deep);
  border-color: var(--blue-deep);
  transform: translateY(-2px);
}
.gal-btn:active:not(:disabled) { transform: translateY(0); }
/* Disabled is a real state here: at either end the button does nothing, and
   silently doing nothing is worse than looking unavailable. */
.gal-btn:disabled { opacity: 0.3; cursor: default; }

.gal-film {
  overflow-x: auto;
  overflow-y: hidden;
  padding-inline: max(var(--gutter), calc((100% - var(--container)) / 2 + var(--gutter)));
  /* scroll-padding MUST mirror padding-inline. Without it `scroll-snap-align:
     start` snaps the first frame to the raw scrollport edge, which silently
     scrolls the strip by exactly the left inset on load: the first photo was
     rendering half off the left of the screen and "previous" started enabled. */
  scroll-padding-inline: max(var(--gutter), calc((100% - var(--container)) / 2 + var(--gutter)));
  scroll-snap-type: x proximity;
  /* Native bar hidden in favour of .gal-progress below, which reports the same
     position but reads as part of the page instead of as an OS control. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  overscroll-behavior-x: contain;
}
.gal-film::-webkit-scrollbar { display: none; }
.gal-film:focus-visible { outline: 2px solid var(--blue); outline-offset: 4px; }

/* Grab-to-drag, mouse only. Touch already has native momentum scrolling and
   a pointer-coarse device must keep it: hijacking touch here would break the
   swipe people already expect. `.is-dragging` is set by JS for the duration
   of a drag and suppresses the hover treatment below, which would otherwise
   dim every frame the cursor is not on while the strip is being thrown. */
@media (hover: hover) and (pointer: fine) {
  .gal-film { cursor: grab; }
  .gal-film.is-dragging { cursor: grabbing; scroll-snap-type: none; }
  .gal-film.is-dragging img { pointer-events: none; }
}

.gal-track {
  display: flex;
  gap: var(--space-4);
  width: max-content;
}
@media (min-width: 900px) { .gal-track { gap: var(--space-5); } }

/* Frame size is the whole argument of this section, and it is a two-sided
   constraint. At the 152px the strip first ran, a face is about 40px across
   and unreadable, so fifteen photos of children read as a contact sheet
   rather than as evidence. At 384px the section stood 1241px tall against
   ~1030-1060 for every other section and broke the page's vertical rhythm.
   These are the sizes that clear both: a face survives, and the section
   measures in line with its neighbours.
   Mobile shows one frame and half of the next. A phone cannot fit several
   frames at a size where a face reads, and a visible partial frame is a
   stronger "there is more" signal than several tiny ones. */
.gal-shot {
  flex: 0 0 min(66vw, 240px);
  scroll-snap-align: start;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--deep-3);
  position: relative;
}
@media (min-width: 700px)  { .gal-shot { flex-basis: 240px; } }
@media (min-width: 1000px) { .gal-shot { flex-basis: 256px; } }
@media (min-width: 1400px) { .gal-shot { flex-basis: 272px; } }

.gal-shot img {
  display: block;
  width: 100%;
  /* height:auto is load-bearing: the width/height attributes are presentational
     hints, and without this both axes are definite and aspect-ratio is ignored. */
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  transition: transform 0.6s var(--ease-out), filter 0.4s var(--ease);
}
@media (hover: hover) {
  .gal-film:not(.is-dragging) .gal-shot:hover img { transform: scale(1.04); }
  /* Everything else recedes a little, so the hovered frame is the subject. */
  .gal-film:hover:not(.is-dragging) .gal-shot:not(:hover) img {
    filter: brightness(0.7) saturate(0.9);
  }
}

/* Scroll position, themed rather than left to the browser. Doubles as the
   only signal of how many frames are still to come. */
/* Rail and counter share a row: the rail says how far through the strip you
   are, the counter says how much is left in a number, because a 2px bar on a
   fifteen-frame strip moves too little per page to read on its own. */
.gal-meter {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin-top: var(--space-5);
}
.gal-count {
  flex: none;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.1em;
  color: var(--on-deep-muted);
  font-variant-numeric: tabular-nums;
}
.gal-count b { color: var(--on-deep); font-weight: 500; }

.gal-progress {
  flex: 1 1 auto;
  height: 2px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.12);
  overflow: hidden;
}
.gal-progress span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--blue);
  border-radius: inherit;
  transform-origin: left center;
  transition: width 0.18s var(--ease-out);
}

/* The one emotional line, after the evidence rather than before it. Serif and
   italic so it reads as the page speaking rather than as another caption.
   Selector is .s-deep .gal-close, not .gal-close: this is a <p>, and
   `.s-deep p { color: var(--on-deep-muted) }` outranks a single class. */
.s-deep .gal-close {
  margin-top: var(--space-6);
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(1.3rem, 1.05rem + 1.1vw, 1.9rem);
  line-height: 1.28;
  letter-spacing: -0.01em;
  color: var(--on-deep);
  max-width: 26ch;
}
@media (min-width: 900px) { .s-deep .gal-close { margin-top: var(--space-7); } }

/* ═══════════════════════════════════════════════════════════════════
   FOOTER
   ═══════════════════════════════════════════════════════════════════ */
.footer { background: var(--deep-2); color: var(--on-deep-muted); padding-block: var(--space-8); }
.footer__top {
  display: grid;
  gap: var(--space-6);
  padding-bottom: var(--space-7);
  border-bottom: 1px solid var(--hairline-deep);
}
@media (min-width: 760px) {
  .footer__top { grid-template-columns: 1fr auto; align-items: center; }
}
.footer__logo { width: 150px; height: auto; }
.footer__contact {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  font-size: var(--fs-sm);
}
/* Padding grows the tap target to ~44px on phones; the equal negative margin
   keeps the footer's layout exactly where it was. */
.footer__contact a { color: var(--on-deep); transition: color 0.2s var(--ease); padding-block: 10px; margin-block: -10px; }
.footer__contact a:hover { color: var(--blue); }

.footer__bottom {
  margin-top: var(--space-6);
  display: grid;
  gap: var(--space-5);
}
@media (min-width: 760px) {
  .footer__bottom { grid-template-columns: 1fr auto; align-items: center; }
}
.footer__social { display: flex; gap: var(--space-3); }
.footer__social a {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  border: 1px solid var(--hairline-deep);
  color: var(--on-deep-muted);
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.footer__social a:hover { border-color: var(--blue); color: var(--blue); }
.footer__social svg { width: 17px; height: 17px; }
.footer__meta {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.05em;
  color: var(--on-deep-faint);
  line-height: 1.8;
}
.footer__meta a { text-decoration: underline; }
.footer__meta a:hover { color: var(--on-deep-muted); }

/* Agency credit. Same mono register as .footer__meta so it reads as part of
   the legal line rather than as a badge; the client's own name stays the
   loudest thing in the footer. Left-aligned under the address on mobile,
   right-aligned opposite it once .footer__bottom goes two-column. */
.footer__credit {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: 0.05em;
  color: var(--on-deep-faint);
  line-height: 1.8;
}
.footer__credit a {
  color: var(--on-deep-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color 0.2s var(--ease);
}
.footer__credit a:hover { color: var(--blue); }
@media (min-width: 760px) {
  .footer__credit { text-align: right; }
}

/* ═══════════════════════════════════════════════════════════════════
   CONSENT BAR — compact floating card, not a full-width slab
   ═══════════════════════════════════════════════════════════════════ */
/* Kept deliberately compact. An earlier version was 160px tall, which on a
   900px viewport forced a 240px push on the hero and shoved the stat rail
   off the fold — the consent notice was costing a fifth of the hero. */
.consent-bar {
  position: fixed;
  left: var(--gutter);
  right: var(--gutter);
  bottom: var(--gutter);
  z-index: 60;
  background: rgba(10, 26, 43, 0.95);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid var(--hairline-deep);
  border-radius: var(--radius-md);
  color: var(--on-deep);
  padding: var(--space-4);
  box-shadow: var(--shadow-deep);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3) var(--space-4);
  transform: translateY(calc(100% + var(--gutter) + 8px));
  transition: transform 0.45s var(--ease-out);
}
.consent-bar.is-visible { transform: translateY(0); }
.consent-bar p {
  font-size: 0.8rem;
  line-height: 1.45;
  color: var(--on-deep-muted);
  flex: 1 1 260px;
  min-width: 0;
}
.consent-bar p a { color: var(--blue); text-decoration: underline; }
.consent-bar__actions { display: flex; gap: var(--space-2); flex: none; }
.consent-bar__actions .contact-link { padding: 9px 16px; min-height: 40px; }
.consent-bar__actions .btn-primary--sm { padding: 9px 18px; min-height: 40px; }

/* Desktop: a bottom-right toast. It clears the left-aligned hero content
   entirely, so the push it asks for stays small. */
@media (min-width: 760px) {
  .consent-bar { left: auto; max-width: 560px; }
}

/* Phones: the card spans the full width, so every pixel of it is taken
   straight out of the hero above. Trimmed to the minimum that still
   reads as a proper consent notice. */
@media (max-width: 480px) {
  .consent-bar { padding: var(--space-3) var(--space-4) var(--space-4); gap: var(--space-3); }
  .consent-bar p { font-size: 0.775rem; line-height: 1.4; flex-basis: 100%; }
  .consent-bar__actions .contact-link,
  .consent-bar__actions .btn-primary--sm { min-height: 38px; padding: 8px 16px; font-size: 0.875rem; }
}
/* ═══════════════════════════════════════════════════════════════════
   Components
   ═══════════════════════════════════════════════════════════════════ */

/* ── Eyebrow ────────────────────────────────────────────────────────
   Mono, letterspaced, numbered. "Lab" is in the client's name, so the
   measurement register is on-concept rather than decorative. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 400;
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--accent-deep);
}
.s-deep .eyebrow, .closing .eyebrow { color: var(--blue); }
/* The hero eyebrow is white, not brand blue. It sits over the brightest,
   most turquoise part of the photograph, where #1ca9e8 all but disappears.
   The dot inherits currentColor and goes white with it, which is correct:
   the accent belongs on a surface that can hold it. */
.hero .eyebrow {
  color: var(--on-deep);
  /* Measured, not decorative. Over the photograph 22.5% of the pixels behind
     this line fell under 4.5:1, which 11.5px text is not allowed to do. The
     shadow lifts the worst case over AA without darkening the scrim further
     and losing the picture. Re-measure if the hero image is ever swapped. */
  text-shadow: 0 1px 3px rgba(6, 17, 29, 0.92), 0 0 10px rgba(6, 17, 29, 0.55);
}

.eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* ── Brand bar ──────────────────────────────────────────────────────
   Logo + one CTA. Deliberately NOT a navigation menu — the brief bans
   one ("fără meniu de navigare") — but the page needs a brand anchor
   above the fold or it reads as an unfinished template. */
.brandbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 55;
  height: var(--bar-h);
  display: flex;
  align-items: center;
  transition: background 0.35s var(--ease), border-color 0.35s var(--ease), backdrop-filter 0.35s var(--ease);
  border-bottom: 1px solid transparent;
}
.brandbar__row {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.brandbar__logo { display: flex; align-items: center; }
/* Small on purpose. The supplied logo is a dense gradient mark; kept
   modest, its busyness reads as a badge rather than competing with the
   headline.
   Left in full colour: a white knockout was tried and it destroyed the
   mark — "Swim" (white) and "Lab" (turquoise) merged into one silhouette.
   Both of those are already light colours, so the logo reads fine on dark
   water; the earlier problem was only that 30px was too small to resolve.
   The shadow lifts it off the busiest part of the caustics. */
.brandbar__logo img {
  height: 38px;
  width: auto;
  filter: drop-shadow(0 2px 8px rgba(6, 17, 29, 0.6));
}
@media (min-width: 768px) { .brandbar__logo img { height: 46px; } }

/* Solid state once the hero has scrolled past. */
.brandbar.is-stuck {
  background: rgba(10, 26, 43, 0.86);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom-color: var(--hairline-deep);
}

/* The bar's CTA stays hidden while the hero's own CTA is on screen, so
   the two never compete for the same click. */
.brandbar__cta {
  opacity: 0;
  transform: translateY(-6px);
  pointer-events: none;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.brandbar.is-stuck .brandbar__cta {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

/* ── Buttons ────────────────────────────────────────────────────────
   .btn-primary is THE single button type. Every instance points at
   #formular — the brief requires one button that always goes to the
   same place ("un singur tip de buton, care duce în același loc"). */
.btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: 17px 30px;
  border-radius: var(--radius-pill);
  background: var(--blue-deep);
  color: var(--white);
  font-weight: 600;
  font-size: 1rem;
  letter-spacing: -0.005em;
  border: none;
  min-height: 56px;
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transition: transform 0.25s var(--ease-out), box-shadow 0.25s var(--ease-out), background 0.25s var(--ease);
  box-shadow: 0 6px 20px rgba(12, 107, 168, 0.30);
}
.btn-primary:hover {
  background: #0d78bd;
  transform: translateY(-2px);
  box-shadow: 0 12px 30px rgba(12, 107, 168, 0.40);
}
.btn-primary:active { transform: translateY(0); }
.btn-primary--block { width: 100%; }
.btn-primary--sm { padding: 11px 20px; min-height: 44px; font-size: 0.92rem; }

/* Arrow glyph, borrowed from the reference's CTA language. */
.btn-primary .arr {
  width: 14px;
  height: 14px;
  flex: none;
  transition: transform 0.3s var(--ease-out);
}
.btn-primary:hover .arr { transform: translate(2px, -2px); }

/* ── Secondary contact affordances ──────────────────────────────────
   Deliberately a different shape and weight from .btn-primary so they
   never read as a competing call to action. The WhatsApp button used to
   be filled brand green, which fought the primary CTA for attention;
   it is a ghost affordance now. --whatsapp is kept as a token because
   its darkened value is the WCAG-verified one. */
.contact-links { display: flex; flex-wrap: wrap; gap: var(--space-3); }

.contact-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 13px 20px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-sm);
  font-weight: 500;
  min-height: 48px;
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: var(--on-deep);
  background: rgba(255, 255, 255, 0.06);
  transition: background 0.25s var(--ease), border-color 0.25s var(--ease);
}
.contact-link:hover { background: rgba(255, 255, 255, 0.13); border-color: rgba(255, 255, 255, 0.4); }
.contact-link svg { width: 16px; height: 16px; flex: none; }
.contact-link--whatsapp svg { color: #3ad07d; }

/* Light-surface variant (used in the consent card's decline action). */
.contact-link--light {
  border-color: var(--border);
  color: var(--navy);
  background: transparent;
}
.contact-link--light:hover { background: var(--grey-100); border-color: var(--grey-400); }

/* ── Icons ──────────────────────────────────────────────────────── */
svg.icon { width: 20px; height: 20px; stroke: currentColor; stroke-width: 1.7; fill: none; }

/* ── Skip link (a11y) ───────────────────────────────────────────── */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--navy);
  color: var(--white);
  padding: 12px 20px;
  z-index: 100;
  border-radius: 0 0 8px 0;
}
.skip-link:focus { left: 0; }

/* ── Focus ring ─────────────────────────────────────────────────────
   One visible ring for every interactive element, on light and dark. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 3px;
  border-radius: 4px;
}
.s-deep a:focus-visible,
.hero a:focus-visible,
.closing a:focus-visible,
.footer a:focus-visible,
.consent-bar button:focus-visible { outline-color: var(--turquoise); }

/* ── Visually hidden ────────────────────────────────────────────── */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Deep links from the homepage into the topic pages ──────────────────────
   Added 2026-09-01 with the four SEO landing pages. These are the ONLY
   internal links pointing at /cursuri-inot-*/ and /bazin-inot-otopeni/, so
   removing them orphans those pages: Google would then reach them from the
   sitemap alone and pass them no internal authority. */
.card__more { margin-top: var(--space-3); }
.card__more a,
.note-block__more a {
  color: var(--blue-deep);
  font-weight: 600;
  font-size: .9rem;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  padding-bottom: 1px;
}
.card__more a:hover,
.note-block__more a:hover { opacity: .75; }
.note-block__more { margin-top: var(--space-3); }
/* ═══════════════════════════════════════════════════════════════════
   Motion
   Rules this file obeys, without exception:
   1. Only transform and opacity are animated. Never layout properties.
   2. Nothing moves before it is in view.
   3. prefers-reduced-motion turns all of it off, and the page must be
      complete and correct with every animation removed.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Scroll reveals ─────────────────────────────────────────────────
   .reveal is applied in the markup; main.js adds .is-in via
   IntersectionObserver. The `no-js`/observer-missing fallback is that
   .reveal never hides content in the first place — see the guard at
   the bottom of this file. */
.js .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
}
.js .reveal.is-in { opacity: 1; transform: none; }

/* Stagger for grid children — index set by main.js as --i. */
.js .reveal-stagger > * {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.65s var(--ease-out), transform 0.65s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 80ms);
}
.js .reveal-stagger.is-in > * { opacity: 1; transform: none; }

/* ── Water motion ───────────────────────────────────────────────────
   The caustic layer is a pre-rasterised bitmap; only its transform changes,
   so this stays on the compositor and never re-runs the SVG filter.
   drift-b, the counter-drifting second net, went with .hero__caustics--2:
   the photograph carries that variation now. */
@keyframes drift-a {
  0%   { transform: translate3d(0, 0, 0) scale(1.06); }
  50%  { transform: translate3d(2.5%, 1.8%, 0) scale(1.12); }
  100% { transform: translate3d(0, 0, 0) scale(1.06); }
}
/* The photograph itself drifts, very slowly. A still photo under a hero that
   used to have four moving layers reads as a page that failed to load.
   The base scale is 1.06 so the translate can never expose an edge, and the
   whole thing is one compositor transform on an already-composited image. */
@keyframes hero-drift {
  0%   { transform: scale(1.06) translate3d(0, 0, 0); }
  50%  { transform: scale(1.115) translate3d(-1.1%, -0.7%, 0); }
  100% { transform: scale(1.06) translate3d(0, 0, 0); }
}

.hero__caustics { animation: drift-a 26s ease-in-out infinite; }
.hero__photo img { animation: hero-drift 44s ease-in-out infinite; will-change: transform; }
.closing__caustics { animation: drift-a 30s ease-in-out infinite; }

/* ── Reduced motion ─────────────────────────────────────────────────
   reset.css already clamps durations globally. This block additionally
   guarantees revealed content is visible and the water simply stops —
   a still frame, not a broken one. */
@media (prefers-reduced-motion: reduce) {
  .js .reveal,
  .js .reveal.is-in,
  .js .reveal-stagger > *,
  .js .reveal-stagger.is-in > * {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .hero__caustics,
  .closing__caustics { animation: none; }
  /* Left at the base scale, so the framing is identical to the moving version. */
  .hero__photo img { animation: none; transform: scale(1.06); }
  .gal-item img { transition: none; }
  .gal-item:hover img { transform: none; }
}
