/* ==========================================================================
   main.css — layout and components. Speaks only in tokens (tokens.css).

   TRAP — there is no `opacity: 0` anywhere in this file, and there must never
   be one. Hidden-until-scrolled states are position only, and the class that
   arms them is attached by JS at runtime (see ui.js). A stalled observer, a
   blocked script or a JS error then costs an animation, never the text: the
   page without JS shows 100 % of its content. A blank page is indistinguishable
   from a broken build, and that is the failure this rule buys off (Л31).

   TRAP — spacing uses LOGICAL properties (inline-start/inline-end) rather than
   left/right. The Arabic version mirrors by setting dir="rtl" on <html> and
   nothing else; a stylesheet written in left/right needs a second, mirrored
   copy, and the second copy is where the two drift apart.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

/* TRAP — БЕЗ этого правила атрибут `hidden` не работает нигде, где
   элементу задан `display` классом: специфичность у `[hidden]` из UA-
   стиля минимальная, и любой селектор по классу её перебивает. Панель
   настроек баннера согласия приезжала из бандла с `hidden`, а
   `[class$="-cc__manage"] { display: grid }` её раскрывал — баннер
   занимал 298 px из 844 на телефоне и закрывал первый экран. Это
   чинили однажды точечно (`.bs-modal[hidden]`), то есть для одного
   элемента из двух. Правило общее, потому что и болезнь общая: любое
   состояние, которое ставит JS атрибутом, обязано выигрывать у CSS. */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  background: var(--ma-paper);
  color: var(--ma-ink);
  font-family: var(--ma-font-text);
  font-size: var(--ma-step-0);
  line-height: 1.6;
  text-rendering: optimizeLegibility;
}

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

a { color: var(--ma-accent); text-underline-offset: 0.18em; }
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--ma-accent);
  outline-offset: 3px;
}

h1, h2, h3 {
  font-family: var(--ma-font-display);
  font-weight: 600;
  letter-spacing: var(--ma-track-display);
  line-height: 1.08;
  margin: 0 0 0.4em;
  text-wrap: balance;
}
h1 { font-size: var(--ma-step-4); }
h2 { font-size: var(--ma-step-3); }
h3 { font-size: var(--ma-step-1); }
p { margin: 0 0 1em; max-width: var(--ma-measure); }

/* -------------------------------------------------------------- skeleton */

.ma-shell { display: flow-root; }
.ma-wrap {
  width: min(100% - 2 * var(--ma-gap), var(--ma-page));
  margin-inline: auto;
}
.ma-section { padding-block: var(--ma-section); }
.ma-section--sand { background: var(--ma-sand); }
.ma-section--card { background: var(--ma-card); }
.ma-skip {
  position: absolute;
  inset-inline-start: -9999px;
  background: var(--ma-card);
  color: var(--ma-ink);
  padding: 0.6rem 1rem;
}
.ma-skip:focus { inset-inline-start: 0; z-index: 60; }

/* A label above a heading. Не римские цифры и не порядковый номер: the eyebrow
   names the section, it does not count it (Л29). */
.ma-eyebrow {
  font-family: var(--ma-font-mono);
  font-size: var(--ma-step-small);
  letter-spacing: var(--ma-track-label);
  text-transform: uppercase;
  color: var(--ma-accent);
  margin: 0 0 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.ma-eyebrow::before {
  content: "";
  inline-size: 1.5rem;
  block-size: 1px;
  background: var(--ma-brass);
  flex: none;
}

/* ---------------------------------------------------------------- header */

.ma-head {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--ma-paper);
  border-block-end: 1px solid var(--ma-rule);
}
.ma-head__bar {
  display: flex;
  align-items: center;
  gap: var(--ma-gap);
  min-block-size: 4.25rem;
}
/* min-inline-size: 0 + overflow-wrap: the wordmark is the one flex item that
   may shrink. Found the day the language switch first rendered: a domain of
   sixteen unbreakable characters plus «Menu» plus «EN RU» is 392 px on a
   390-px phone, and the page scrolled sideways by 14 px on every screen.
   Second instance, the day the fourth language rendered: «EN DE FR RU» left
   the wordmark 40 px on a 320-px phone and it wrapped into a column of
   syllables — no overflow, so the scrollWidth test was green. The switch now
   lives in the menu list on a phone, and `anywhere` only bites after every
   ordinary break (the hyphen in the domain) has been tried. */
.ma-head__mark { display: flex; align-items: center; gap: 0.6rem; text-decoration: none; color: var(--ma-ink); min-inline-size: 0; }
.ma-head__mark svg { inline-size: 26px; block-size: 26px; flex: none; }
.ma-head__word {
  font-family: var(--ma-font-display);
  font-weight: 600;
  letter-spacing: var(--ma-track-display);
  font-size: var(--ma-step-1);
  overflow-wrap: anywhere;
}
.ma-nav { flex: none; }
.ma-nav { margin-inline-start: auto; display: flex; align-items: center; gap: clamp(0.75rem, 2vw, 1.75rem); }
.ma-nav__list { display: flex; gap: clamp(0.75rem, 2vw, 1.75rem); list-style: none; margin: 0; padding: 0; }
.ma-nav__list a { color: var(--ma-ink); text-decoration: none; }
.ma-nav__list a:hover { color: var(--ma-accent); }
/* The language switch as the last row of the menu: one row on desktop with a
   divider, the bottom row of the panel on a phone. Absent without JS (the
   switch is built from the hreflang links), so the empty item hides itself. */
.ma-nav__lang { padding-inline-start: clamp(0.75rem, 2vw, 1.75rem); border-inline-start: 1px solid var(--ma-rule); }
.ma-nav__lang:has(.ma-lang:empty) { display: none; }
.ma-burger { display: none; }

@media (max-width: 860px) {
  .ma-head__word { font-size: var(--ma-step-0); }
  .ma-burger {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: none;
    border: 1px solid var(--ma-rule);
    border-radius: var(--ma-radius);
    color: var(--ma-ink);
    font: inherit;
    padding: 0.5rem 0.85rem;
  }
  /* TRAP: collapsing the list is gated to the SAME breakpoint that shows the
     burger. Declared globally it hid the desktop navigation outright — the
     script sets `closed` at every width, and above this breakpoint the burger
     that reopens it is `display: none`. The page then had no navigation at all
     with JS on and a correct one with JS off, which is the one direction of
     failure nobody thinks to test for. */
  .ma-nav[data-ma-nav="closed"] .ma-nav__list { display: none; }

  /* The panel behaves as a panel only once the script has taken charge of it:
     the markup ships `data-ma-nav="static"`, and with no JS the links stay a
     plain visible list instead of an overlay nobody can close. */
  .ma-nav[data-ma-nav="open"] .ma-nav__list {
    position: absolute;
    inset-inline: 0;
    top: 100%;
    flex-direction: column;
    background: var(--ma-card);
    border-block-end: 1px solid var(--ma-rule);
    padding: var(--ma-gap);
  }
  .ma-nav[data-ma-nav="open"] .ma-nav__lang {
    padding-inline-start: 0;
    border-inline-start: 0;
    padding-block-start: var(--ma-gap);
    border-block-start: 1px solid var(--ma-rule);
  }
}

/* ------------------------------------------------------------------ hero */

.ma-hero { position: relative; background: var(--ma-deep); color: var(--ma-on-deep); overflow: clip; }
.ma-hero__frame {
  position: absolute;
  inset: 0;
  background-image: var(--ma-hero-image), var(--ma-motif);
  background-size: cover, 30rem;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
}
/* THREE stacked veils, not one. A single bottom gradient leaves the top line
   of a headline unreadable over a lit façade — the veil is designed for the
   frame it sits on, and a fork re-tunes these stops against ITS hero (Л24). */
/* Tuned against THIS frame — the Saadiyat surf line, bright turquoise and
   pale sand. White text needs a ground of luminance ≤ 0.18 for 4.5:1, and
   the sand sits near 0.7, so the band the text occupies is veiled at ~0.8
   and the photo is let through where no text sits: the strips above and
   below the copy on a phone, the right half on a desktop. Measured on the
   rendered composite (docs/runbooks: hero contrast probe), not guessed. */
.ma-hero__veil {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom,
      rgba(20, 33, 31, 0.40) 0%, rgba(20, 33, 31, 0.82) 16%,
      rgba(20, 33, 31, 0.82) 80%, rgba(20, 33, 31, 0.30) 100%);
}
@media (min-width: 861px) {
  .ma-hero__veil { background:
    linear-gradient(to bottom, rgba(20, 33, 31, 0.45), rgba(20, 33, 31, 0.10) 30%, rgba(20, 33, 31, 0.0) 60%, rgba(20, 33, 31, 0.55) 100%),
    linear-gradient(to right, rgba(20, 33, 31, 0.88) 0%, rgba(20, 33, 31, 0.84) 46%, rgba(20, 33, 31, 0.08) 72%, rgba(20, 33, 31, 0.0) 100%);
  }
  [dir="rtl"] .ma-hero__veil { background:
    linear-gradient(to bottom, rgba(20, 33, 31, 0.45), rgba(20, 33, 31, 0.10) 30%, rgba(20, 33, 31, 0.0) 60%, rgba(20, 33, 31, 0.55) 100%),
    linear-gradient(to left, rgba(20, 33, 31, 0.88) 0%, rgba(20, 33, 31, 0.84) 46%, rgba(20, 33, 31, 0.08) 72%, rgba(20, 33, 31, 0.0) 100%);
  }
}
.ma-hero__body { position: relative; padding-block: clamp(5rem, 4rem + 10vw, 11rem); }
.ma-hero h1 { color: var(--ma-on-deep); max-inline-size: 18ch; }
.ma-hero__lede { color: var(--ma-on-deep-soft); font-size: var(--ma-step-1); max-inline-size: 46ch; }
.ma-hero .ma-eyebrow { color: var(--ma-on-deep-lit); }
.ma-hero__facts {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 3vw, 3rem);
  margin-block-start: clamp(2rem, 4vw, 3.5rem);
  padding-block-start: 1.25rem;
  border-block-start: 1px solid var(--ma-brass);
  list-style: none;
  padding-inline: 0;
}
.ma-fact__key { display: block; font-size: var(--ma-step-small); color: var(--ma-on-deep-soft); letter-spacing: var(--ma-track-label); text-transform: uppercase; }
.ma-fact__val { display: block; font-family: var(--ma-font-display); font-size: var(--ma-step-1); color: var(--ma-on-deep); }

/* --------------------------------------------------------------- content */

.ma-grid { display: grid; gap: clamp(1.5rem, 3vw, 3rem); }
@media (min-width: 861px) {
  .ma-grid--split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
  .ma-grid--thirds { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.ma-card {
  background: var(--ma-card);
  border: 1px solid var(--ma-rule);
  border-radius: var(--ma-radius);
  padding: clamp(1.25rem, 3vw, 2rem);
}
/* margin: 0 — the UA sheet gives <figure> 40px side margins, which centred a
   photo 80px narrower than its column on a phone and hid inside the motif
   figures for a month because a centred motif looks intentional. */
.ma-figure { position: relative; margin: 0; background: var(--ma-mist); border-radius: var(--ma-radius); overflow: clip; }
.ma-figure img { inline-size: 100%; object-fit: cover; }
.ma-figure__note { font-size: var(--ma-step-small); color: var(--ma-ink-soft); margin-block-start: 0.6rem; }
.ma-motif {
  background-image: var(--ma-motif);
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  min-block-size: 14rem;
}

.ma-table { inline-size: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.ma-table th, .ma-table td { text-align: start; padding: 0.85rem 1rem; border-block-end: 1px solid var(--ma-rule); }
.ma-table th { font-size: var(--ma-step-small); letter-spacing: var(--ma-track-label); text-transform: uppercase; color: var(--ma-ink-soft); font-weight: 600; }
.ma-table tbody tr:nth-child(even) { background: var(--ma-sand); }
.ma-scroll { overflow-x: auto; }

/* Numbers never wrap across their thousands separator. The separator itself is
   U+00A0 written as an escape in the markup — a narrow no-break space (U+202F)
   pasted out of a price list renders at almost zero width in many families and
   turns "1 100 000" into "1100 000". The two characters are indistinguishable
   in an editor; the defect is only ever caught on the rendered page. */
.ma-num { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ------------------------------------------------- prices (build-generated) */

.ma-money__list { display: grid; gap: 0.75rem; }
.ma-prices { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.ma-price {
  display: grid;
  gap: 0.15rem 1rem;
  padding: 1rem 1.15rem;
  background: var(--ma-card);
  border: 1px solid var(--ma-rule);
  border-radius: var(--ma-radius);
}
@media (min-width: 861px) { .ma-price { grid-template-columns: 1fr auto; align-items: baseline; } }
.ma-price__label { font-family: var(--ma-font-display); font-size: var(--ma-step-1); }
.ma-price__aed { font-family: var(--ma-font-mono); font-size: var(--ma-step-1); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* The converted figure is OUR calculation and is never presented as the
   developer's price: it carries the rate and the rate's date, and it is
   visually subordinate to the dirham figure, which is the legal unit. */
.ma-price__conv, .ma-price__src {
  grid-column: 1 / -1;
  font-size: var(--ma-step-small);
  color: var(--ma-ink-soft);
}
.ma-prices-slot:empty { display: none; }
/* Renders empty on a page that names no project — a hub index, the privacy
   page — and must then take no space at all. */
.ma-legal-slot:empty { display: none; }

/* ---------------------------------------------- off-plan legal (generated) */

/* Circular RERA/DLD 2-2022 §2–§3. Six lines, in the main flow of the page, at
   the page's own footnote size or larger — never in small print in the footer
   and never behind a disclosure widget. The size floor is enforced here, and
   the block's presence is enforced by the build. */
.ma-legal {
  margin-block: var(--ma-section) 0;
  padding: clamp(1.25rem, 3vw, 1.75rem);
  background: var(--ma-card);
  border: 1px solid var(--ma-rule);
  border-radius: var(--ma-radius);
  font-size: var(--ma-step-small);
  color: var(--ma-ink);
  display: grid;
  gap: 0.5rem;
}
.ma-legal__row { display: grid; gap: 0.15rem 1.5rem; }
@media (min-width: 861px) { .ma-legal__row { grid-template-columns: 15rem minmax(0, 1fr); } }
.ma-legal__key { color: var(--ma-ink-soft); letter-spacing: var(--ma-track-label); text-transform: uppercase; font-size: 0.8125rem; }
.ma-legal__val { color: var(--ma-ink); }

/* -------------------------------------------------------------- lead form */

.ma-form { display: grid; gap: 1rem; max-inline-size: 34rem; }
.ma-field { display: grid; gap: 0.35rem; }
.ma-field__label { font-size: var(--ma-step-small); color: var(--ma-ink-soft); }
.ma-field__req { color: var(--ma-warm); }
.ma-field input {
  font: inherit;
  color: var(--ma-ink);
  background: var(--ma-card);
  border: 1px solid var(--ma-rule);
  border-radius: var(--ma-radius);
  padding: 0.85rem 1rem;
  inline-size: 100%;
}
.ma-field input[aria-invalid="true"] { border-color: var(--ma-warm); }
.ma-field__hint { font-size: var(--ma-step-small); color: var(--ma-ink-soft); }
.ma-field__error { font-size: var(--ma-step-small); color: var(--ma-warm); min-block-size: 1.2em; }

/* Two boxes, never one. Being phoned about this enquiry and being marketed to
   later are two permissions; marketing never inherits from the callback tick.
   Neither box is pre-ticked — a pre-ticked box is evidence against us. */
.ma-consent { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.6rem; align-items: start; font-size: var(--ma-step-small); color: var(--ma-ink); }
.ma-consent input { margin-block-start: 0.25rem; inline-size: 1.1rem; block-size: 1.1rem; }

.ma-btn {
  font: inherit;
  font-weight: 600;
  letter-spacing: 0.01em;
  border: 1px solid var(--ma-deep);
  border-radius: var(--ma-radius);
  background: var(--ma-deep);
  color: var(--ma-on-deep);
  padding: 0.95rem 1.6rem;
  cursor: pointer;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}
.ma-btn--ghost { background: transparent; color: var(--ma-ink); border-color: var(--ma-ink); }

/* TRAP — на тёмной поверхности основная кнопка сливалась с фоном ПОЛНОСТЬЮ:
   `.ma-btn` красится в `--ma-deep`, и `.ma-hero` красится в него же.
   Замерено в браузере: контраст кнопки с тем, на чём она лежит, — 1.00. Текст
   при этом читался (14.69), поэтому проверка контраста молчала правомерно: она
   считает ТЕКСТ на ПОВЕРХНОСТИ, а не элемент управления на его контейнере.
   WCAG 1.4.11 требует для границ интерфейсных элементов не меньше 3:1, и это
   единственное место на странице, где промах стоит конверсии напрямую: главный
   CTA первого экрана выглядит как строка жирного текста. На тёмной поверхности
   кнопка инвертируется. */
.ma-foot .ma-btn,
.ma-hero .ma-btn,
.ma-cta-dark .ma-btn {
  background: var(--ma-on-deep);
  color: var(--ma-deep);
  border-color: var(--ma-on-deep);
}
.ma-btn[aria-busy="true"] { cursor: progress; }
.ma-form__status { font-size: var(--ma-step-small); color: var(--ma-ink); }

/* WhatsApp entry point (build-generated markup). The label is linted at build
   time and never promises a call: voice is blocked in the UAE by the TDRA. */
.ma-wa {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 600;
  color: var(--ma-accent);
  text-decoration: none;
  border: 1px solid var(--ma-accent);
  border-radius: var(--ma-radius);
  padding: 0.85rem 1.4rem;
}
.ma-wa-slot:empty { display: none; }

/* ----------------------------------------------------------------- modal */

/* Closed by `hidden`, not by opacity, and the trigger is a real anchor to the
   inline form underneath. With no JS the anchor jumps to a form that is fully
   rendered on the page; the modal is an enhancement, never the only way in. */
.ma-modal {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  padding: var(--ma-gap);
  background: rgba(20, 33, 31, 0.72);
}
.ma-modal__panel {
  background: var(--ma-card);
  border-radius: var(--ma-radius);
  padding: clamp(1.25rem, 4vw, 2.5rem);
  inline-size: min(100%, 34rem);
  max-block-size: 90vh;
  overflow-y: auto;
}
.ma-modal__close { background: none; border: 0; font: inherit; color: var(--ma-ink-soft); cursor: pointer; padding: 0.25rem; }
.ma-modal__head { display: flex; align-items: baseline; gap: 1rem; justify-content: space-between; }

/* ------------------------------------------------------------ location map */

/* The map is a real tile restyled in edit mode, never a drawn one, and it
   carries NO text: every label is HTML placed at coordinates computed by Web
   Mercator projection, not judged by eye. When the file is absent the block
   falls back to the motif and the written distances below it. */
.ma-map { position: relative; background: var(--ma-mist); border: 1px solid var(--ma-rule); border-radius: var(--ma-radius); overflow: clip; }
.ma-map__plate {
  aspect-ratio: 16 / 10;
  background-image: var(--ma-locmap-image), var(--ma-motif);
  background-size: cover, 18rem;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
}
.ma-map__pin {
  position: absolute;
  inset-inline-start: var(--ma-x);
  inset-block-start: var(--ma-y);
  transform: translate(-50%, -50%);
  background: var(--ma-card);
  border: 1px solid var(--ma-rule);
  border-radius: var(--ma-radius);
  padding: 0.3rem 0.55rem;
  font-size: 0.8125rem;
  color: var(--ma-ink);
  white-space: nowrap;
}
[dir="rtl"] .ma-map__pin { transform: translate(50%, -50%); }
.ma-map__legend { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.5rem; }
.ma-map__legend li { display: flex; justify-content: space-between; gap: 1rem; border-block-end: 1px solid var(--ma-rule); padding-block-end: 0.5rem; }

/* ---------------------------------------------------------------- footer */

.ma-foot { background: var(--ma-deep); color: var(--ma-on-deep-soft); padding-block: var(--ma-section) 3rem; }
.ma-foot a { color: var(--ma-on-deep-lit); }
.ma-foot h2 { color: var(--ma-on-deep); }
.ma-foot__cta { padding-block-end: 2.5rem; border-block-end: 1px solid var(--ma-brass); margin-block-end: 2.5rem; }
.ma-foot__cols { display: grid; gap: var(--ma-gap); }
@media (min-width: 861px) { .ma-foot__cols { grid-template-columns: 2fr 1fr 1fr; } }
.ma-foot__id { color: var(--ma-on-deep); font-size: var(--ma-step-small); }
.ma-foot__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; font-size: var(--ma-step-small); }

/* ------------------------------------------------------- language switcher */

.ma-lang { display: flex; align-items: center; gap: 0.5rem; font-size: var(--ma-step-small); }
.ma-lang__list { display: flex; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }
.ma-lang__list a { text-decoration: none; text-transform: uppercase; letter-spacing: var(--ma-track-label); }
.ma-lang__list [aria-current="true"] { color: var(--ma-ink-soft); }
.ma-lang:empty { display: none; }

/* ------------------------------------------------- consent banner (runtime)

   The banner's own markup is created by the site's telemetry bundle, and its
   class carries that site's namespace — which this stylesheet cannot know and
   must not hard-code (a shared namespace across domains is a one-grep proof
   that the domains are one operation). It is therefore styled by the stable
   SUFFIX of the class the bundle emits. Never re-create the banner in markup:
   two banners mean two consent states, and the stored one would stop matching
   what the visitor answered. */
[class$="-cc"] {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: 90;
  background: var(--ma-card);
  border-block-start: 1px solid var(--ma-rule);
  padding: var(--ma-gap);
}
[class$="-cc__body"] { width: min(100% , var(--ma-page)); margin-inline: auto; display: grid; gap: 0.85rem; }
[class$="-cc__text"] { margin: 0; font-size: var(--ma-step-small); max-inline-size: none; }
[class$="-cc__actions"] { display: flex; flex-wrap: wrap; gap: 0.6rem; }
[class$="-cc__actions"] button {
  font: inherit;
  padding: 0.6rem 1.1rem;
  border-radius: var(--ma-radius);
  border: 1px solid var(--ma-ink);
  background: transparent;
  color: var(--ma-ink);
  cursor: pointer;
}
/* Accept is not the only affordance and is not visually privileged past the
   point where the other two read as decoration: a banner whose real control is
   one button is not consent in the EEA or under the UAE PDPL. */
[class$="-cc__actions"] button:first-child { background: var(--ma-deep); color: var(--ma-on-deep); border-color: var(--ma-deep); }
[class$="-cc__manage"] { display: grid; gap: 0.5rem; font-size: var(--ma-step-small); }
[class$="-cc__manage"] label { display: flex; gap: 0.5rem; align-items: center; }

/* ---------------------------------------------------------------- motion */

/* Every rule below is armed by the `ma-motion` class, which JS attaches to
   <html> only after it has successfully built its observers. No JS, an old
   browser, a thrown error — no class, no transform, nothing hidden. */
.ma-motion [data-ma-reveal] {
  transform: translateY(18px);
  transition: transform 700ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.ma-motion [data-ma-reveal].ma-in { transform: none; }
.ma-motion [data-ma-reveal="lift"] { transform: translateY(28px) scale(0.995); }
.ma-motion [data-ma-reveal="lift"].ma-in { transform: none; }

/* Parallax and any pinning are desktop-only, and the gate lives in ui.js as
   well: a phone must never have its scroll taken away from it (Л30). */
@media (max-width: 860px) {
  .ma-motion [data-ma-parallax] { transform: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  .ma-motion [data-ma-reveal],
  .ma-motion [data-ma-reveal].ma-in { transform: none; transition: none; }
  * { scroll-behavior: auto !important; }
}

@media print {
  .ma-head, .ma-modal, [class$="-cc"] { display: none; }
}

/* ---------------------------------------------------------------- frames */

/* A frame is a <picture> from the stack's manifest (generator/core/frames.py),
   never a CSS background: it gets a preload, an alt in the page's language
   and a credit line in the footer. The hero keeps its motif underneath as the
   fallback while the image is still on its way, and the veil above it is
   tuned against THIS frame (Л24). `--ma-focus` is written on the <img> by the
   build from the manifest's focus point, so the crop keeps the subject when
   the box changes shape. */
.ma-hero__frame picture, .ma-hero__frame img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
}
.ma-hero__frame img { object-fit: cover; object-position: var(--ma-focus, 50% 50%); }
.ma-figure--frame { aspect-ratio: 4 / 3; }
.ma-figure--frame picture, .ma-figure--frame img { display: block; inline-size: 100%; block-size: 100%; }
.ma-figure--frame img { object-fit: cover; object-position: var(--ma-focus, 50% 50%); }
/* Л26: an illustration says so on the frame itself, not only in a footnote. */
.ma-frame__note {
  position: absolute;
  inset-block-end: 0.5rem;
  inset-inline-start: 0.5rem;
  padding: 0.2rem 0.5rem;
  font-size: var(--ma-step-small);
  color: var(--ma-on-deep);
  background: rgba(20, 33, 31, 0.72);
  border-radius: var(--ma-radius);
}
.ma-foot__credits { font-size: var(--ma-step-small); color: var(--ma-ink-soft); margin-block-start: 0.6rem; }
.ma-foot__credits a { color: inherit; }
