/* MYST atelier site — kit-specific layout (composes DS tokens) */

.site { min-height: 100vh; display: flex; flex-direction: column; background: var(--bone); color: var(--ink); position: relative; }
.site-main { flex: 1; }

/* Full-screen scene mode (home) — one viewport per scene, firm snap.
   Mandatory snap locks every one-screen scene to its top; scenes taller than
   the viewport (the hero scrub, the journal grid) are oversized snap areas, so
   they scroll freely inside and only lock to the next scene at their edges.
   No CSS scroll-behavior:smooth here — it fights the snap and lets the scroll
   rest between scenes; nav clicks animate via JS scrollTo({behavior:'smooth'}). */
.site--scenes { height: 100svh; overflow-y: auto; scroll-snap-type: y mandatory; overscroll-behavior-y: contain; }
.site--scenes .home > section,
.site--scenes .site-footer { min-height: 100svh; scroll-snap-align: start; scroll-snap-stop: always; }
/* Nav-anchored sections (Команда / Публикации / Контакт) snap to their top too;
   snap-stop:always so a fast flick can never skip past one. */
.site--scenes .screen.team,
.site--scenes .screen.objects,
.site--scenes .screen.contact { scroll-snap-align: start; scroll-snap-stop: always; }
.site--scenes .site-footer { min-height: 60svh; height: auto; align-content: end; }
.site--scenes .home-intro,
.site--scenes .home-featured { align-content: center; padding-block: clamp(96px, 12vh, 140px); }
.site--scenes .home-quote { min-height: 100svh; }
.site--scenes .site-topbar--over { position: fixed; }
@media (prefers-reduced-motion: reduce) { .site--scenes { scroll-behavior: auto; } }

/* Top bar — transparent, no white fill. In flow on inner screens; floats over the hero on home. */
.site-topbar {
  height: 68px; padding: 0 var(--page-x);
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  position: relative; z-index: 20;
  background: transparent; border-bottom: 1px solid var(--hairline);
  transition: transform .55s cubic-bezier(.2, .7, .2, 1), opacity .4s ease, background .35s var(--ease-out, ease);
}
/* Hidden while the hero tagline is on screen; the first swipe slides it in. */
.site-topbar--hidden { transform: translateY(-100%); opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .site-topbar { transition: opacity .3s ease; } .site-topbar--hidden { transform: none; } }
/* Over the dark hero photo → floats, ivory marks, no rule */
.site-topbar--over {
  position: absolute; top: 0; left: 0; right: 0;
  border-bottom: 0; color: var(--bone);
}
.site-topbar--over .site-nav-link,
.site-topbar--over .site-edition { color: var(--bone); }
.site-topbar--over .site-wordmark .myst-logo {
  filter: brightness(0) invert(94%) sepia(10%) saturate(480%) hue-rotate(352deg) brightness(102%);
}
/* Tapestry scene under the bar → the walnut behind the marks is no longer a
   class-triggered fill but the frosted --veil panel in the motion section below,
   which ramps in with the scroll. The class is kept for the CTA's colour state. */
.site-topbar--intro { background: transparent; border-bottom: 0; }
.site-wordmark { display: block; }
.site-topbar nav { display: flex; gap: 34px; justify-content: center; }
.site-nav-link {
  font-family: var(--font-sans); font-size: 10px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--ink); position: relative; transition: opacity .2s ease;
}
.site-nav-link:hover { opacity: .5; }
.site-nav-link.is-active::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -8px; height: 1px; background: var(--brass);
}
.site-edition {
  justify-self: end; font-family: var(--font-sans); font-size: 10px;
  letter-spacing: .18em; text-transform: uppercase; opacity: .6;
}
.site-topbar__cta {
  justify-self: end; font-family: var(--font-sans); font-size: 10px;
  letter-spacing: .18em; text-transform: uppercase; text-decoration: none;
  color: var(--bone); background: var(--walnut); border: 1px solid var(--walnut);
  padding: 9px 20px; border-radius: 2px; white-space: nowrap;
  transition: background .25s ease, border-color .25s ease, color .25s ease;
}
.site-topbar__cta:hover { background: var(--brass); border-color: var(--brass); color: var(--forest); }
/* Over the hero / on walnut intro bar → outline style so it reads on dark */
.site-topbar--over .site-topbar__cta { background: transparent; border-color: rgba(241,235,221,.5); color: var(--bone); }
.site-topbar--over .site-topbar__cta:hover { background: var(--bone); border-color: var(--bone); color: var(--forest); }
.site-topbar--intro .site-topbar__cta { background: transparent; border-color: rgba(241,235,221,.55); color: var(--bone); }
.site-topbar--intro .site-topbar__cta:hover { background: var(--bone); border-color: var(--bone); color: var(--walnut); }

/* Mobile navigation — burger button + full-screen menu (activated ≤640px). */
.site-burger { display: none; justify-self: end; width: 34px; height: 34px; padding: 0; margin: 0;
  background: none; border: 0; cursor: pointer; flex-direction: column; align-items: center; justify-content: center; gap: 6px; }
.site-burger__line { display: block; width: 24px; height: 1.5px; background: var(--bone);
  transition: transform .3s var(--ease-out, ease), opacity .3s ease; }
.site-burger.is-open .site-burger__line:first-child { transform: translateY(3.75px) rotate(45deg); }
.site-burger.is-open .site-burger__line:last-child { transform: translateY(-3.75px) rotate(-45deg); }
.site-menu { display: none; position: fixed; inset: 0; z-index: 15; background: var(--forest);
  opacity: 0; pointer-events: none; transition: opacity .35s var(--ease-out, ease);
  align-items: center; justify-content: center; }
.site-menu.is-open { opacity: 1; pointer-events: auto; }
.site-menu nav { display: flex; flex-direction: column; align-items: center; gap: clamp(16px, 3.4vh, 30px); }
.site-menu__link { font-family: var(--font-display); font-weight: 400; font-size: clamp(30px, 8.5vw, 46px);
  line-height: 1; letter-spacing: -.01em; color: var(--bone); text-decoration: none; transition: color .2s ease; }
.site-menu__link.is-active { color: var(--brass); }
.site-menu__cta { margin-top: clamp(14px, 3vh, 26px); font-family: var(--font-sans); font-size: 12px;
  letter-spacing: .18em; text-transform: uppercase; color: var(--bone); text-decoration: none;
  border: 1px solid rgba(241,235,221,.5); padding: 15px 28px; border-radius: 2px; transition: background .25s ease, color .25s ease; }
.site-menu__cta:hover { background: var(--bone); color: var(--forest); }

/* Shared eyebrow / headings */
.eyebrow { font-family: var(--font-sans); font-size: 10px; letter-spacing: .2em; text-transform: uppercase; margin: 0; opacity: .7; }
.component-name { font-family: var(--font-sans); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; opacity: .6; margin: 0; }
.section-label { display: flex; gap: 20px; font-family: var(--font-sans); font-size: 10px; letter-spacing: .18em; text-transform: uppercase; line-height: 1.4; }
.section-label span { color: var(--brass); }
.section-label p { margin: 0; }

/* Screen fade-in */
.screen { animation: myst-screen .6s var(--ease-out) both; }
@keyframes myst-screen { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* ---------- Home ---------- */
/* Scroll-scrubbed hero reel: freeze-frame on load, playback tied to scroll, then next scene. */
.home-reel { position: relative; }
.site--scenes .home > section.home-reel { min-height: 340svh; padding: 0; }
.home-reel__stage { position: sticky; top: 0; height: 100svh; overflow: hidden; display: flex; align-items: flex-end; background: #14120c; }
.home-reel__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; background: #14120c; }
.home-reel__veil { position: absolute; inset: 0; z-index: 1; background: linear-gradient(180deg, rgba(20,18,12,.22) 0%, rgba(20,18,12,.18) 50%, rgba(20,18,12,.5) 100%); }

/* Centred tagline over the frozen first frame; flies down + out on the first swipe. */
.home-reel__title {
  position: absolute; inset: 0; z-index: 2;
  display: flex; align-items: center; justify-content: center; text-align: center;
  padding: 0 var(--page-x); color: var(--bone);
  transition: transform 1s cubic-bezier(.5, 0, .1, 1), opacity .8s ease;
  will-change: transform, opacity;
}
.home-reel__title.is-out { transform: translateY(90vh); opacity: 0; pointer-events: none; }
/* Tenor Sans, not the display Bodoni: a Didone's hairlines dissolve into the
   workshop photo behind the tagline. Even stroke weight carries it instead, so
   no shadow is used. Tenor is a single 400 upright — do not set a weight or
   italic here, both would be synthesised. */
/* One line, as large as it will go. In Tenor Sans the tagline measures 12.587em
   and the blinking caret adds 0.28em, so 12.87em total; --page-x leaves 92% of
   the viewport, and 92/12.87 = 7.15vw fills it exactly — measured flush to the
   pixel, which is too tight to trust. 6.9vw keeps ~2% in hand so a browser with
   slightly different metrics still cannot spill. nowrap is the guarantee — the
   phone override below puts the wrapping back. */
.home-reel__title h1 {
  font-family: var(--font-hero); font-weight: 400; font-style: normal;
  font-size: clamp(30px, 6.9vw, 200px); line-height: 1.06; letter-spacing: .005em;
  margin: 0; max-width: none; white-space: nowrap;
}
/* Tenor Sans has no italic, so the accent word is set apart by colour alone —
   asking for italic here would get a slanted fake. */
.home-reel__title h1 em { font-style: normal; color: var(--linen); }
.home-reel__caret { display: inline-block; margin-left: .04em; font-weight: 300; color: var(--linen); transform: translateY(-.02em); animation: caret-blink 1.05s step-end infinite; }
@keyframes caret-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .home-reel__caret { animation: none; } }
@media (prefers-reduced-motion: reduce) {
  .home-reel__title { transition: opacity .4s ease; }
  .home-reel__title.is-out { transform: none; }
}

.home-hero { position: relative; min-height: 78vh; display: flex; align-items: flex-end; overflow: hidden; }
.home-hero__image {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(20,18,12,.1), rgba(20,18,12,.55)), url('assets/myst-hero.jpg?v=fa08dc3b') center/cover no-repeat;
}
.home-hero__caption {
  position: absolute; inset: 104px var(--page-x) auto; display: flex; justify-content: space-between;
  color: var(--bone); font-family: var(--font-sans); font-size: 10px; letter-spacing: .16em; text-transform: uppercase; opacity: .7;
}
.home-hero__copy { position: relative; padding: 0 var(--page-x) 8vh; color: var(--bone); }
.home-hero__copy .eyebrow { opacity: .85; margin-bottom: 22px; }
.home-hero h1 { font-family: var(--font-display); font-weight: 400; font-size: clamp(52px, 7.2vw, 118px); line-height: .9; letter-spacing: -.04em; margin: 0; max-width: 14ch; }
.home-hero h1 em { font-style: italic; color: var(--linen); }

.home-intro { position: relative; overflow: hidden; display: grid; grid-template-columns: repeat(12, 1fr); column-gap: var(--grid-gap); padding: var(--section-y) var(--page-x); border-bottom: 1px solid var(--hairline); background: var(--forest) url('assets/myst-tapestry.jpg?v=7b34a0ae') center/cover; }
.home-intro__video { position: absolute !important; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0 !important; }
.home-intro__veil { position: absolute; inset: 0; z-index: 1; background: linear-gradient(180deg, rgba(20,30,24,.62), rgba(15,22,17,.78)); }
/* display:contents keeps the three blocks as the real grid items on desktop, so
   the wrapper costs nothing there; the phone rules turn it into a panel. */
.home-intro__panel { display: contents; }
/* Both forms need lifting above the video, which sits at z-index 0. */
.home-intro > *,
.home-intro__panel > * { position: relative; z-index: 2; }
.home-intro .eyebrow { grid-column: 1 / 3; color: var(--linen); opacity: .9; }
.home-intro__lede { grid-column: 4 / 11; font-family: var(--font-reading); font-size: clamp(24px, 3vw, 40px); line-height: 1.28; letter-spacing: -.012em; margin: 0; color: var(--bone); }
.home-intro__actions { grid-column: 4 / 11; display: flex; gap: 16px; flex-wrap: wrap; margin-top: 48px; }
.home-intro__actions .myst-btn--secondary { border-color: var(--bone); color: var(--bone); }
.home-intro__actions .myst-btn--secondary:hover { background: var(--walnut); border-color: var(--walnut); }

.home-featured { position: relative; overflow: hidden; display: grid; grid-template-columns: 2fr 10fr; column-gap: var(--grid-gap); padding: var(--section-y) var(--page-x); background: var(--forest); color: var(--bone); }
.home-featured__body { display: block; position: relative; z-index: 1; }
.home-featured .section-label { color: var(--linen); position: relative; z-index: 1; }
.home-featured .section-label span { color: var(--brass); }
/* oversized ghost index drifting behind the gallery for depth + motion */
.home-featured__ghost { position: absolute; right: -2vw; top: 50%; transform: translateY(-50%); z-index: 0; font-family: var(--font-display); font-size: clamp(220px, 34vw, 560px); line-height: .8; color: rgba(241,235,221,.05); pointer-events: none; user-select: none; letter-spacing: -.04em; }

/* ---------- Featured reel (carousel) ---------- */
.reel { display: grid; grid-template-columns: 7fr 5fr; gap: clamp(28px, 4vw, 64px); align-items: stretch; outline: none; }
.reel__stage { position: relative; height: clamp(360px, 46vw, 560px); overflow: hidden; background: var(--forest); cursor: grab; border-radius: 0 120px 0 0; touch-action: pan-y; }
.reel__stage:active { cursor: grabbing; }
.reel__stage::after { content: ""; position: absolute; inset: 0; z-index: 2; border: 1px solid rgba(241,235,221,0); border-radius: inherit; transition: border-color .35s ease; pointer-events: none; }
.reel__stage:hover::after { border-color: rgba(241,235,221,.42); }
.reel__slide { position: absolute; inset: 0; margin: 0; opacity: 0; transform: scale(1.04); transition: opacity .6s ease, transform .8s ease; pointer-events: none; }
.reel__slide.is-active { opacity: 1; transform: scale(1); }
.reel__slide img { width: 100%; height: 100%; object-fit: cover; display: block; user-select: none; filter: saturate(.82) sepia(.05); transition: filter .3s ease; }
.reel__stage:hover .reel__slide.is-active img { filter: saturate(.92) sepia(.02); }

.reel__panel { display: flex; flex-direction: column; justify-content: center; }
/* All four copy blocks share one grid cell — the tallest sets the height, so the
   pager underneath never shifts. visibility, not display, because a hidden item
   still has to take part in sizing the cell. */
.reel__copy { display: grid; }
.reel__copy-item { grid-area: 1 / 1; visibility: hidden; opacity: 0; pointer-events: none;
  transition: opacity .45s var(--ease-out, ease); }
.reel__copy-item.is-active { visibility: visible; opacity: 1; pointer-events: auto; }
.reel__meta { display: flex; align-items: baseline; justify-content: space-between; font-family: var(--font-sans); font-size: 10px; letter-spacing: .18em; text-transform: uppercase; }
.reel__tag { color: var(--brass); }
.reel__count { opacity: .5; }
.reel__title { font-family: var(--font-display); font-weight: 400; font-size: clamp(30px, 3.4vw, 48px); line-height: 1; letter-spacing: -.02em; margin: 20px 0 0; }
.reel__metaline { font-family: var(--font-sans); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; opacity: .55; margin: 12px 0 0; }
.reel__desc { font-family: var(--font-reading); font-size: 20px; line-height: 1.45; margin: 22px 0 0; max-width: 40ch; }
/* The entrance lives on .is-active in the motion section below, not here: the
   copy blocks are all mounted at once now, and an animation already named on
   every one of them would never restart when the active slide changes. */
@keyframes reelIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }

.reel__nav { display: flex; align-items: center; gap: 24px; margin-top: clamp(32px, 4vw, 56px); }
.reel__arrow {
  width: 52px; height: 52px; border-radius: 50%; border: 1px solid rgba(241,235,221,.28);
  background: transparent; color: var(--bone); font-size: 18px; line-height: 1; cursor: pointer;
  display: grid; place-items: center; transition: background .25s ease, border-color .25s ease, color .25s ease, transform .25s ease;
}
.reel__arrow:hover { background: var(--brass); border-color: var(--brass); color: var(--forest); transform: scale(1.06); }
.reel__dots { display: flex; gap: 10px; margin-right: auto; }
.reel__dot { width: 8px; height: 8px; border-radius: 50%; border: 0; padding: 0; background: rgba(241,235,221,.22); cursor: pointer; transition: background .25s ease, transform .25s ease; }
.reel__dot.is-active { background: var(--brass); transform: scale(1.3); }

.home-quote { position: relative; padding: var(--section-y) var(--page-x); display: flex; justify-content: center; text-align: center; }
.home-quote .myst-quote { max-width: 20ch; }
.site--scenes .team-scene__photos { min-height: 100%; }

/* ---------- Scroll reveal ---------- */
.reveal { opacity: 0; transform: translateY(30px); transition: opacity .85s var(--ease-out, cubic-bezier(.2,.7,.2,1)), transform .85s var(--ease-out, cubic-bezier(.2,.7,.2,1)); will-change: opacity, transform; }
.reveal.is-in { opacity: 1; transform: none; }
.reveal--d1 { transition-delay: .1s; }
.reveal--d2 { transition-delay: .2s; }
.reveal--d3 { transition-delay: .3s; }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; } }

/* ---------- Scene index tags ---------- */
.scene-tag { display: flex; align-items: center; gap: 12px; font-family: var(--font-sans); font-size: 10px; letter-spacing: .2em; text-transform: uppercase; margin: 0 0 22px; opacity: .8; }
.scene-tag span { color: var(--brass); }
.scene-tag--light { color: var(--bone); }

/* ---------- Page head (Objects / Team) ---------- */
.page-head { display: grid; grid-template-columns: 2fr 10fr; column-gap: var(--grid-gap); padding: var(--section-y) var(--page-x) clamp(48px, 5vw, 80px); }
.page-head__body h2 { font-family: var(--font-display); font-weight: 400; font-size: clamp(48px, 5.4vw, 84px); line-height: .96; letter-spacing: -.03em; margin: 14px 0 0; }
.page-head__body .eyebrow { margin-bottom: 8px; }

/* ---------- Team scene (Команда) — swipeable: portrait left, quote + attribution right ---------- */
.team-scene { display: grid; grid-template-columns: 5fr 7fr; align-items: stretch; outline: none; }
.team-scene__photos { position: relative; min-height: 100svh; overflow: hidden; background: var(--forest); }
.team-scene__photo { position: absolute; inset: 0; background-position: 45% center; background-size: cover; background-repeat: no-repeat; opacity: 0; transform: scale(1.04); transition: opacity .6s ease, transform .8s ease; }
.team-scene__photo.is-active { opacity: 1; transform: scale(1); }
.team-scene__body { display: flex; flex-direction: column; justify-content: flex-start; padding: clamp(56px, 7vw, 120px) clamp(40px, 6vw, 110px); cursor: grab; height: 100svh; box-sizing: border-box; }
.team-scene__body:active { cursor: grabbing; }
.team-scene .myst-quote { max-width: 100%; }
.team-scene .myst-quote.is-long { max-width: 100%; }
.team-scene__card { animation: reelIn .5s ease both; flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; justify-content: center; padding-block: clamp(16px, 3vh, 40px); overflow: hidden; }
.team-scene__foot { flex: 0 0 auto; margin-top: auto; }
.team-scene .myst-quote blockquote { margin: 0; max-width: 22em; line-height: 1.14; letter-spacing: -.015em; text-wrap: balance; }
.team-scene__by { margin-top: 0; padding-top: clamp(20px, 2vw, 30px); border-top: 1px solid var(--hairline); }
.team-scene__name { font-family: var(--font-display); font-weight: 400; font-size: clamp(26px, 2.4vw, 38px); line-height: 1.02; letter-spacing: -.01em; margin: 0; }
.team-scene__by { margin-top: 0; padding-top: clamp(24px, 2.5vw, 36px); border-top: 1px solid var(--hairline); }
.team-scene__name { font-family: var(--font-display); font-weight: 400; font-size: clamp(26px, 2.4vw, 38px); letter-spacing: -.01em; margin: 0; }
.team-scene__role { font-family: var(--font-sans); font-size: 10px; letter-spacing: .16em; text-transform: uppercase; opacity: .6; margin: 12px 0 0; }
.team-scene__nav { margin-top: clamp(30px, 4vw, 52px); }
.team-scene__nav .reel__arrow { border-color: var(--hairline); color: var(--ink); }
.team-scene__nav .reel__arrow:hover { background: var(--walnut); border-color: var(--walnut); color: var(--bone); }
.team-scene__nav .reel__dot { background: rgba(0,0,0,.18); }
.team-scene__nav .reel__dot.is-active { background: var(--brass); }

/* ---------- Objects ---------- */
.objects__filters { display: flex; gap: 20px; flex-wrap: wrap; align-items: center; justify-content: space-between; padding: 0 var(--page-x) 40px; }
.objects__tags { display: flex; gap: 12px; flex-wrap: wrap; }
.objects__search { position: relative; display: flex; align-items: center; margin-left: auto; }
.objects__search-icon { position: absolute; left: 2px; font-size: 15px; color: var(--ink); opacity: .5; pointer-events: none; }
.objects__search input {
  appearance: none; -webkit-appearance: none; background: transparent;
  border: 0; border-bottom: 1px solid var(--line, var(--hairline));
  padding: 9px 22px 9px 24px; width: clamp(190px, 22vw, 300px);
  font-family: var(--font-sans); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink);
  transition: border-color .25s ease;
}
.objects__search input::placeholder { color: var(--ink); opacity: .4; }
.objects__search input:focus { outline: none; border-bottom-color: var(--brass); }
.objects__search input::-webkit-search-cancel-button { -webkit-appearance: none; height: 12px; width: 12px; background: currentColor; opacity: .4; cursor: pointer;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath stroke='black' stroke-width='1.4' d='M1 1l10 10M11 1L1 11'/%3E%3C/svg%3E") center/contain no-repeat; }
.objects__empty { padding: 0 var(--page-x) var(--section-y); font-family: var(--font-reading); font-style: italic; font-size: clamp(18px, 2vw, 24px); color: var(--olive); }
.objects__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2px; padding: 0 var(--page-x); }
.objects__card { cursor: pointer; outline: none; }
.objects__card:focus-visible { outline: 2px solid var(--brass); outline-offset: -2px; }

/* The archive is paged six at a time (two rows of three) rather than run out in
   full, so the scene holds one screen and the heading stays with its cards. */
.objects__deck { outline: none; padding-bottom: clamp(28px, 3vw, 48px); }
/* Replaces the minHeight prop the cards used to carry. As an inline style that
   value outranked every stylesheet rule, which is exactly what the one-screen
   layout below needs to override. */
.objects__grid .objects__card { min-height: 360px; }
.objects__deck:focus-visible .objects__nav .reel__arrow { border-color: var(--brass); }
.objects__nav { justify-content: center; padding: 0 var(--page-x); margin-top: clamp(26px, 3vw, 44px); }
.objects__nav .reel__arrow { border-color: var(--hairline); color: var(--ink); }
.objects__nav .reel__arrow:hover { background: var(--walnut); border-color: var(--walnut); color: var(--bone); }
.objects__nav .reel__dot { background: rgba(0,0,0,.18); }
.objects__nav .reel__dot.is-active { background: var(--brass); }
/* The dot row sits between the arrows here instead of pushing them apart. */
.objects__nav .reel__dots { margin-right: 0; }

/* The heading is one line now, so it is measured against the space it actually
   has instead of a fixed clamp: the line runs 12.3em in Bodoni, and dividing the
   column width by 17.5 (13.2 once the layout drops to a single column) leaves
   about a tenth of it in hand at every viewport down to 320px. nowrap is the
   guarantee — without it a hair of overflow would silently fold it back in two. */
.objects .page-head { padding-top: clamp(56px, 6vw, 88px); padding-bottom: clamp(20px, 2.2vw, 32px); }
.objects .page-head__body h2 {
  white-space: nowrap; margin-top: 10px;
  font-size: min(76px, calc((100vw - 2 * var(--page-x)) / 17.5));
}
.objects__filters { padding-bottom: clamp(20px, 2.2vw, 30px); }

/* One screen, three columns. The scene becomes a column: the head and filters
   take what they need, the deck takes the rest, and the two card rows split it.
   That is what puts the pager on the same screen as the cards — turning a page
   then swaps them under a stationary heading without scrolling anything.
   minmax()'s floor means a very short window grows the scene and scrolls it
   rather than crushing the cards; below 1001px the grid reflows to two and then
   one column, where six rows could never share a screen, so the fixed card
   height above stays and the scene scrolls as before. */
@media (min-width: 1001px) {
  .site--scenes .screen.objects { min-height: 100svh; display: flex; flex-direction: column; }
  .objects__deck { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; padding-bottom: clamp(20px, 2.2vw, 36px); }
  /* Both rows are declared, not implied, so a last page of one or two cards
     still reserves the second track. The grid keeps its height, and the pager
     stays on exactly the same line from page to page — turning one must not
     move anything on screen. */
  .objects__grid { flex: 1 1 auto; min-height: 0; grid-template-rows: repeat(2, minmax(165px, 1fr)); }
  .objects__grid .objects__card { min-height: 0; }
  .objects__nav { margin-top: clamp(14px, 1.8vw, 28px); }

  /* The design system frames a card for a 420px slot. Sharing a screen halves
     that, and its margins alone came to more than the row — the guide and the
     caption were being clipped off the bottom edge. Retuned to the shorter card
     (worst case, a crowned card under a two-line title, now fits ~170px), and
     scoped here so the phone and tablet layouts keep the original proportions. */
  .objects__grid .objects__card .myst-photo__guide { margin: 14px; padding: 8px 12px; }
  /* Both crops round a lot off the top of the card, and the guide has to clear
     the curve or its ends get eaten — the arched crown from the left, the corner
     from the right. These are the smallest offsets that still land under it. */
  .objects__grid .objects__card.myst-photo--crown .myst-photo__guide { margin-top: clamp(46px, 20%, 110px); }
  .objects__grid .objects__card.myst-photo--corner .myst-photo__guide { margin-top: 34px; }
  .objects__grid .objects__card figcaption { padding: 14px 16px; }
  .objects__grid .objects__card figcaption strong { font-size: clamp(17px, 1.4vw, 23px); }
}
/* min-height alone leaves the flex container as tall as its content, so nothing
   ever shrinks — a definite height is what makes the rows divide the leftover
   space. Only claimed where the result actually fits: the head, filters and
   pager cost ~360px, so under a 700px window two rows of cards cannot share the
   screen and the scene goes back to growing and scrolling instead of clipping. */
@media (min-width: 1001px) and (min-height: 700px) {
  .site--scenes .screen.objects { height: 100svh; }
}

/* ---------- Publication article (full-page lightbox) ---------- */
.pub-overlay { position: fixed; inset: 0; z-index: 200; background: var(--bone); color: var(--ink);
  animation: pub-fade .32s var(--ease-out, cubic-bezier(.2,.7,.2,1)) both; }
@keyframes pub-fade { from { opacity: 0; } to { opacity: 1; } }
.pub-overlay__scroll { height: 100%; overflow-y: auto; overscroll-behavior: contain; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { .pub-overlay { animation: none; } .pub-overlay__scroll { scroll-behavior: auto; } }

.pub-overlay__close { position: sticky; top: 0; z-index: 3; display: inline-flex; align-items: center; gap: 12px;
  margin: 0; padding: 20px var(--page-x); width: 100%; box-sizing: border-box;
  background: color-mix(in srgb, var(--bone) 88%, transparent); backdrop-filter: blur(12px);
  border: 0; border-bottom: 1px solid var(--hairline); cursor: pointer; text-align: left;
  font-family: var(--font-sans); font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink);
  transition: color .2s ease; }
.pub-overlay__close span { font-size: 15px; transition: transform .3s var(--ease-emph, ease); }
.pub-overlay__close:hover { color: var(--walnut); }
.pub-overlay__close:hover span { transform: translateX(-4px); }

.pub-article { max-width: 860px; margin: 0 auto; padding: clamp(40px, 6vw, 88px) var(--page-x) clamp(80px, 10vw, 140px); }
.pub-article__kicker { font-family: var(--font-sans); font-size: 10px; letter-spacing: .22em; text-transform: uppercase; color: var(--brass); margin: 0 0 22px; }
.pub-article__title { font-family: var(--font-display); font-weight: 400; font-size: clamp(38px, 5.4vw, 76px); line-height: .98; letter-spacing: -.03em; margin: 0; }
.pub-article__dek { font-family: var(--font-reading); font-style: italic; font-size: clamp(19px, 2.2vw, 26px); line-height: 1.4; color: var(--olive); margin: 22px 0 0; max-width: 30ch; }

.pub-article__figure { margin: clamp(40px, 5vw, 64px) 0 clamp(32px, 4vw, 52px); }
.pub-article__figure img { display: block; width: 100%; height: auto; background: var(--forest);
  filter: saturate(.9) sepia(.03); border-radius: 0 90px 0 0; }
.pub-article__figure.is-portrait img { max-height: 78vh; width: auto; max-width: 100%; margin: 0 auto; border-radius: 48% 48% 0 0 / 12% 12% 0 0; }
.pub-article__figure figcaption { margin-top: 16px; font-family: var(--font-sans); font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--olive); opacity: .8; }

.pub-article__body { }
.pub-article__body .pub-h { font-family: var(--font-display); font-weight: 400; font-size: clamp(26px, 3vw, 38px); line-height: 1.06; letter-spacing: -.02em; margin: clamp(44px, 5vw, 64px) 0 18px; }
.pub-article__body .pub-p { font-family: var(--font-reading); font-size: clamp(16px, 1.25vw, 19px); line-height: 1.66; letter-spacing: -.003em; margin: 0 0 20px; color: var(--ink); }
.pub-article__body .pub-p + .pub-p { margin-top: 0; }
.pub-spec { display: flex; gap: 24px; align-items: baseline; margin: 0; padding: 14px 0; border-top: 1px solid var(--hairline); font-family: var(--font-sans); }
.pub-spec:last-of-type { border-bottom: 1px solid var(--hairline); }
.pub-spec__term { flex: 0 0 clamp(120px, 26%, 220px); font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--olive); line-height: 1.5; }
.pub-spec__value { flex: 1; font-family: var(--font-reading); font-size: clamp(15px, 1.15vw, 18px); line-height: 1.5; color: var(--ink); }

.pub-article__social { margin-top: clamp(56px, 7vw, 88px); padding-top: 28px; border-top: 1px solid var(--forest); display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 18px; }
.pub-article__social-label { font-family: var(--font-sans); font-size: 11px; letter-spacing: .16em; text-transform: uppercase; color: var(--olive); }
.pub-article__social-links { display: flex; gap: 28px; flex-wrap: wrap; }
.pub-article__social-links a { position: relative; font-family: var(--font-sans); font-size: 12px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink); text-decoration: none; padding-bottom: 4px; transition: color .2s ease; }
.pub-article__social-links a::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--brass); transform: scaleX(0); transform-origin: left; transition: transform .3s ease; }
.pub-article__social-links a:hover { color: var(--brass); }
.pub-article__social-links a:hover::after { transform: scaleX(1); }
.pub-article__foot { margin-top: 44px; }
.pub-article__foot .myst-btn { align-self: flex-start; }

/* ---------- Process ---------- */
.process-hero { padding: 0 var(--page-x); }
.process-stages { list-style: none; margin: 0; padding: var(--section-y) var(--page-x); display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px; border-top: 1px solid var(--hairline); }
.process-stage { padding: 32px 24px 40px; border-left: 1px solid var(--hairline); }
.process-stage:first-child { border-left: 0; }
.process-stage__n { font-family: var(--font-sans); font-size: 11px; letter-spacing: .16em; color: var(--brass); }
.process-stage h3 { font-family: var(--font-display); font-weight: 400; font-size: 30px; letter-spacing: -.02em; margin: 22px 0 14px; }
.process-stage p { font-family: var(--font-reading); font-size: 16px; line-height: 1.5; margin: 0; }

/* ---------- Contact ---------- */
.contact { display: grid; grid-template-columns: 7fr 5fr; min-height: calc(100vh - 88px); }
.contact__form { order: 1; }
.contact__image { order: 2; }
/* The photograph lives on a pseudo-element inset past the frame on every side,
   so the scene parallax can slide it without ever exposing an edge or bleeding
   over the form column next to it. */
.contact__image { position: relative; overflow: hidden; background: var(--forest); }
.contact__image::before {
  content: ""; position: absolute; inset: -4%;
  background: linear-gradient(180deg, rgba(13,23,17,.06), rgba(13,23,17,.5)), url('assets/myst-master-01.jpg?v=a6f449fe') center center/cover;
}
.contact__form { background: var(--forest); color: var(--bone); padding: clamp(56px, 7vw, 110px) clamp(40px, 5vw, 90px); display: flex; flex-direction: column; }
.contact__form .eyebrow { color: var(--linen); opacity: .85; }
.contact__form h2 { font-family: var(--font-display); font-weight: 400; font-size: clamp(48px, 5vw, 82px); line-height: .92; letter-spacing: -.035em; margin: 18px 0 56px; }
.contact__form h2 em { font-style: italic; color: var(--linen); }
.contact__form form { display: flex; flex-direction: column; gap: 40px; }
.contact__row { display: grid; grid-template-columns: 1fr; gap: 40px; }
.contact__wide .myst-field, .contact__wide { width: 100%; }
.contact__form .myst-field__label { color: var(--linen); }
.contact__form .myst-field__control { color: var(--bone); border-bottom-color: rgba(241,235,221,.5); }
.contact__form .myst-field__control::placeholder { color: var(--bone); opacity: .4; }
.contact__form .myst-field__control:focus { border-color: var(--brass); box-shadow: 0 2px 0 var(--brass); }
.contact__form select.myst-field__control option { color: var(--ink); }
.contact__form .myst-btn { align-self: flex-start; }
.contact__form .myst-btn[type="submit"] { background: var(--walnut); border-color: var(--walnut); color: var(--bone); }

/* Personal-data consent — square hairline checkbox, brass tick, above the submit button */
.contact__consent { display: flex; align-items: flex-start; gap: 12px; margin: -8px 0 -14px; cursor: pointer; max-width: 46ch; }
.contact__consent input { appearance: none; -webkit-appearance: none; width: 18px; height: 18px; margin: 1px 0 0; flex: 0 0 auto; border: 1px solid rgba(241,235,221,.5); background: transparent; cursor: pointer; display: grid; place-content: center; transition: border-color .2s ease, background .2s ease; }
.contact__consent input:checked { border-color: var(--brass); background: var(--brass); }
.contact__consent input:checked::after { content: "✓"; color: var(--forest); font-size: 12px; line-height: 1; font-weight: 700; }
.contact__consent input:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }
.contact__consent span { font-family: var(--font-sans); font-size: 12px; line-height: 1.55; letter-spacing: .02em; color: var(--linen); opacity: .85; }
.contact__consent a { color: var(--linen); text-decoration: underline; text-underline-offset: 2px; transition: color .2s ease; }
.contact__consent a:hover { color: var(--brass); }
.contact__consent.is-invalid input { border-color: #c98a6b; }
.contact__direct { display: flex; flex-direction: column; gap: 14px; margin-top: 8px; padding-top: 32px; border-top: 1px solid rgba(241,235,221,.16); }
.contact__direct-label { font-size: 12px; letter-spacing: .16em; text-transform: uppercase; color: var(--linen); opacity: .7; }
.contact__direct-links { display: flex; gap: 32px; }
.contact__direct-links a { font-family: var(--font-display); font-size: 22px; color: var(--bone); text-decoration: none; position: relative; padding-bottom: 3px; }
.contact__direct-links a::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--brass); transform: scaleX(0); transform-origin: left; transition: transform .3s ease; }
.contact__direct-links a:hover { color: var(--brass); }
.contact__direct-links a:hover::after { transform: scaleX(1); }
.contact__error { margin: 18px 0 0; font-family: var(--font-sans); font-size: 12px; letter-spacing: .04em; color: #c98a6b; max-width: 44ch; }
.contact__done { display: flex; flex-direction: column; gap: 22px; max-width: 44ch; }
.contact__done .component-name { color: var(--brass); }
.contact__done p:nth-child(2) { font-family: var(--font-reading); font-size: 20px; line-height: 1.5; margin: 0; }
.contact__done .myst-btn { align-self: flex-start; margin-top: 8px; }

/* ---------- Footer ---------- */
.site-footer { position: relative; overflow: visible; height: 396px; padding: 0 var(--page-x) 32px; display: flex; align-items: flex-end; justify-content: flex-end; }
.site-footer__info { display: flex; flex-direction: column; align-items: flex-end; gap: 20px; padding-top: 21px; border-top: 1px solid var(--hairline); }
.site-footer__contacts { display: flex; flex-direction: column; gap: 12px; font-family: var(--font-sans); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; text-align: right; }
.site-footer__contacts a { color: var(--ink); text-decoration: none; transition: opacity .2s ease; }
.site-footer__contacts a:hover { opacity: .5; }
.site-footer__hours { margin: 10px 0 0; font-size: 10px; line-height: 1.9; letter-spacing: .1em; opacity: .6; }
.site-footer__addr { margin: 8px 0 0; font-size: 10px; line-height: 1.9; letter-spacing: .1em; opacity: .6; }
.site-footer__link { align-self: flex-end; }
/* Oversized wordmark resting flush on the footer's bottom edge */
.site-footer__mark { position: absolute; left: var(--page-x); bottom: 32px; display: block; line-height: 0; z-index: 0; max-width: 60%; overflow: hidden; }
.site-footer__mark img { display: block; width: clamp(360px, 42vw, 660px); height: auto; opacity: .12; }

/* ================= Motion ================================================
   Everything below is interaction and scroll choreography. Three deliberate
   omissions: the hero scrub (.home-reel), the tapestry scene (.home-intro) and
   the gobelin video are driven by their own code in Home.jsx and are not touched
   from here.
   ========================================================================= */

/* ---------- Scene transitions ----------
   App.jsx writes --sp (-1 above · 0 centred · 1 below) and --sf (opacity) onto
   every .scene-motion element on each scroll frame. The rules here spend that on
   layered parallax: backgrounds trail the scroll, type leads it, and the two
   cross-dissolve past each other. --pm scales the whole effect (halved on phones).
   Only transform and opacity are animated, so all of it stays on the compositor. */
.scene-motion { --sp: 0; --sf: 1; --pm: 1; }

/* Работы — the ghost numeral drifts hardest, the photograph trails, the copy leads. */
.home-featured__ghost {
  transform: translate3d(0, calc(-50% + var(--sp) * 72px * var(--pm)), 0);
  will-change: transform;
}
.home-featured .reel__stage { transform: translate3d(0, calc(var(--sp) * -20px * var(--pm)), 0); }
.home-featured .reel__panel { transform: translate3d(0, calc(var(--sp) * 24px * var(--pm)), 0); }

/* Команда — the portrait pans against the quote. The scale is the slack the pan
   moves inside, so the frame edge can never come into view. */
.team-scene__photo {
  transform: scale(1.09) translate3d(0, calc(var(--sp) * -2% * var(--pm)), 0);
  transition: opacity .6s ease, transform .9s var(--ease-emph), filter .6s ease;
}
.team-scene__photo.is-active { transform: scale(1.05) translate3d(0, calc(var(--sp) * -2% * var(--pm)), 0); }
.team-scene__body {
  transform: translate3d(0, calc(var(--sp) * 26px * var(--pm)), 0);
  opacity: var(--sf);
}

/* Публикации */
.screen.objects .page-head,
.screen.objects .objects__filters { transform: translate3d(0, calc(var(--sp) * 20px * var(--pm)), 0); opacity: var(--sf); }
.screen.objects .objects__deck { transform: translate3d(0, calc(var(--sp) * 36px * var(--pm)), 0); opacity: var(--sf); }
/* Each page of the archive deals its cards in rather than swapping them at once.
   The delay lives on the cards, not the grid, so it never fights --sf above. */
/* backwards, not both: `both` would pin transform:none once the deal finishes and
   quietly kill the cards' hover lift, which animations outrank. */
.objects__grid > * { animation: reelIn .55s var(--ease-out) backwards; }
.objects__grid > :nth-child(2) { animation-delay: .05s; }
.objects__grid > :nth-child(3) { animation-delay: .10s; }
.objects__grid > :nth-child(4) { animation-delay: .15s; }
.objects__grid > :nth-child(5) { animation-delay: .20s; }
.objects__grid > :nth-child(6) { animation-delay: .25s; }

/* Контакт */
.contact__image::before { transform: translate3d(0, calc(var(--sp) * -26px * var(--pm)), 0); }
.contact__form > * { transform: translate3d(0, calc(var(--sp) * 24px * var(--pm)), 0); opacity: var(--sf); }

/* Подвал */
.site-footer__mark { transform: translate3d(0, calc(var(--sp) * -30px * var(--pm)), 0); }
.site-footer__info { transform: translate3d(0, calc(var(--sp) * 22px * var(--pm)), 0); opacity: var(--sf); }

/* ---------- Top bar: frosted veil + scroll progress ----------
   Chrome.jsx ramps --veil 0 → 1 across the tail of the hero and tracks --progress
   over the whole page. z-index:-1 keeps both layers inside the bar's own stacking
   context, behind the marks but above whatever scene is passing underneath. */
.site-topbar { --veil: 0; --progress: 0; isolation: isolate; }
.site-topbar::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(180deg, rgba(86,56,42,.93), rgba(86,56,42,.74));
  -webkit-backdrop-filter: blur(16px) saturate(118%);
  backdrop-filter: blur(16px) saturate(118%);
  opacity: var(--veil);
  will-change: opacity;
}
.site-topbar::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; z-index: -1;
  background: linear-gradient(90deg, var(--brass), var(--linen) 65%, rgba(241,235,221,.3));
  transform: scaleX(var(--progress)); transform-origin: left center;
  opacity: calc(var(--veil) * .85); pointer-events: none;
}

/* Nav marks: a hairline sweeps in from the right and settles left. */
.site-nav-link::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: -8px; height: 1px;
  background: currentColor; opacity: .55;
  transform: scaleX(0); transform-origin: right center;
  transition: transform .45s var(--ease-emph);
}
.site-nav-link:hover { opacity: 1; }
.site-nav-link:hover::before { transform: scaleX(1); transform-origin: left center; }
.site-nav-link.is-active::before { display: none; }
.site-nav-link.is-active::after { animation: navmark .55s var(--ease-emph) both; transform-origin: left center; }
@keyframes navmark { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.site-wordmark { transition: opacity .3s ease, transform .5s var(--ease-emph); }
.site-wordmark:hover { opacity: .78; transform: translateY(-1px); }

/* CTA: the fill rises from the bottom edge instead of snapping on. */
.site-topbar__cta { position: relative; z-index: 0; overflow: hidden; --cta-wipe: var(--brass);
  transition: border-color .45s var(--ease-emph), color .45s var(--ease-emph); }
.site-topbar--over .site-topbar__cta,
.site-topbar--intro .site-topbar__cta { --cta-wipe: var(--bone); }
.site-topbar__cta::before {
  content: ""; position: absolute; inset: 0; z-index: -1; background: var(--cta-wipe);
  transform: translateY(102%); transition: transform .5s var(--ease-emph);
}
.site-topbar__cta:hover::before { transform: translateY(0); }
/* The wipe is the fill now — cancel the instant background swaps above it. */
.site-topbar__cta:hover,
.site-topbar--over .site-topbar__cta:hover,
.site-topbar--intro .site-topbar__cta:hover { background: transparent; }

/* ---------- Команда: цитаты ---------- */
/* The card gains a gutter to its left (pulled back by an equal negative margin,
   so the type does not move) for the brass rule to draw itself in. */
.team-scene__card {
  --qpad: clamp(14px, 1.6vw, 26px);
  position: relative; margin-left: calc(-1 * var(--qpad)); padding-left: var(--qpad);
  animation: quoteIn .7s var(--ease-emph) both;
}
.team-scene__card::before {
  content: ""; position: absolute; left: 0; top: 10%; bottom: 10%; width: 2px;
  background: var(--brass); opacity: .85;
  transform: scaleY(0); transform-origin: top center;
  transition: transform .65s var(--ease-emph);
}
@keyframes quoteIn { from { opacity: 0; transform: translate3d(0, 20px, 0); } to { opacity: 1; transform: none; } }
.team-scene__by { animation: reelIn .6s var(--ease-out) both .12s; transition: border-color .5s ease; }

/* The lift sits on the quote, not the card: the card's entrance animation pins
   its own transform for good (animation fill beats a normal declaration). */
.team-scene .myst-quote { transition: transform .6s var(--ease-emph); }
.team-scene .myst-quote blockquote em {
  text-decoration: underline; text-decoration-color: transparent;
  text-decoration-thickness: 1px; text-underline-offset: .14em;
  transition: color .45s ease, text-decoration-color .55s var(--ease-emph);
}

@media (hover: hover) and (pointer: fine) {
  .team-scene__card:hover::before { transform: scaleY(1); }
  .team-scene__card:hover .myst-quote { transform: translateY(-6px); }
  .team-scene__card:hover .myst-quote blockquote em {
    color: var(--brass); text-decoration-color: rgba(162,123,69,.5);
  }
  /* Hovering the words reaches back to the portrait — the photo sits earlier in
     the DOM, so :has() on the scene is what makes the link possible at all. */
  .team-scene:has(.team-scene__card:hover) .team-scene__photo.is-active {
    transform: scale(1.09) translate3d(0, calc(var(--sp) * -2% * var(--pm)), 0);
    filter: saturate(1.06) brightness(1.03);
  }
  .team-scene:has(.team-scene__card:hover) .team-scene__by { border-color: rgba(162,123,69,.5); }
}

/* ---------- Избранные работы: карусель ---------- */
/* Slides wait on the side they belong to (--off from Home.jsx) and are pushed
   through, so the direction of travel is legible. */
.reel__slide {
  transform: translate3d(calc(var(--off, 1) * 7%), 0, 0) scale(1.05);
  transition: opacity .65s var(--ease-out), transform .9s var(--ease-emph);
}
.reel__slide.is-active { transform: translate3d(0, 0, 0) scale(1); }
/* Slow drift while a work is on screen. The keyframes override the inline
   per-work zoom, which is why Home.jsx also passes it as --z. */
.reel__slide.is-active img { animation: reelDrift 11s var(--ease-out) both; }
@keyframes reelDrift {
  from { transform: scale(var(--z, 1)); }
  to { transform: scale(calc(var(--z, 1) * 1.055)); }
}
/* Naming the animation only on the active block is what restarts it: the class
   takes animation-name from none to reelIn, which is the trigger. */
.reel__copy-item.is-active .reel__meta,
.reel__copy-item.is-active .reel__title,
.reel__copy-item.is-active .reel__metaline,
.reel__copy-item.is-active .reel__desc { animation: reelIn .55s var(--ease-out) both; }
.reel__title { animation-delay: .06s; }
.reel__metaline { animation-delay: .12s; }
.reel__desc { animation-delay: .18s; }
.reel__arrow { transition: background .3s var(--ease-emph), border-color .3s ease, color .3s ease, transform .4s var(--ease-emph); }
.reel__arrow:active { transform: scale(.94); }

/* ---------- Публикации: карточки ---------- */
/* Specificity has to clear the design system's own .myst-photo rules, which are
   injected into <head> after this stylesheet and would otherwise win the tie. */
@media (hover: hover) and (pointer: fine) {
  .objects__grid .objects__card { transition: transform .55s var(--ease-emph); }
  .objects__grid .objects__card:hover { transform: translateY(-6px); }
  .objects__grid .objects__card:hover .myst-photo__img { transform: scale(1.055); }
  .objects__grid .objects__card figcaption strong { display: inline-block; transition: transform .5s var(--ease-emph); }
  .objects__grid .objects__card:hover figcaption strong { transform: translateX(5px); }
}

/* ---------- Публикация: разворот ---------- */
.pub-article { animation: pub-rise .62s var(--ease-emph) both .06s; }
@keyframes pub-rise { from { opacity: 0; transform: translate3d(0, 28px, 0); } to { opacity: 1; transform: none; } }

/* ---------- Контакт ---------- */
.contact__direct-links a { transition: color .3s ease, transform .45s var(--ease-emph); }
.contact__direct-links a:hover { transform: translateY(-2px); }

/* ---------- Reduced motion ----------
   App.jsx never starts the scene loop here, so --sp/--sf keep their defaults;
   these rules drop the decorative animations and pans that do not depend on it. */
@media (prefers-reduced-motion: reduce) {
  .scene-motion, .scene-motion * { --sp: 0 !important; --sf: 1 !important; }
  .home-featured__ghost { transform: translateY(-50%); }
  .reel__slide.is-active img, .objects__grid > *,
  .team-scene__card, .team-scene__by, .reel__copy-item.is-active *, .pub-article { animation: none; }
  .reel__slide { transition: opacity .3s ease; transform: none; }
  .site-topbar::before { transition: none; }
  .team-scene__card::before, .site-topbar__cta::before, .site-nav-link::before { transition: none; }
}

/* ---------- Responsive ---------- */
@media (max-width: 1000px) {
  .home-intro__lede, .home-intro__actions { grid-column: 3 / -1; }
  .home-featured, .page-head { grid-template-columns: 1fr; gap: 40px; }
  .home-featured__body { grid-template-columns: 1fr; }
  .reel { grid-template-columns: 1fr; gap: 28px; }
  .reel__stage { height: clamp(260px, 60vw, 420px); border-radius: 0 64px 0 0; }
  .objects__grid { grid-template-columns: repeat(2, 1fr); }
  /* page-head has collapsed to one column — the heading gets the full measure. */
  .objects .page-head__body h2 { font-size: min(76px, calc((100vw - 2 * var(--page-x)) / 13.2)); }
  .process-stages { grid-template-columns: 1fr 1fr; }
  .process-stage:nth-child(-n+2) { border-top: 0; }
  .contact { grid-template-columns: 1fr; }
  .contact__image { min-height: 40vh; }
}
@media (max-width: 640px) {
  .site-topbar nav { display: none; }
  .site-topbar__cta { display: none; }
  /* No toolbar on phones — no band, no rule: just the wordmark (left) and the
     burger (right) floating over the content. Every scene is dark under the
     marks (hero poster, forest, team photo, contact image) except Publications,
     which is cream — that one flips the marks to ink. A soft shadow carries
     them over the odd light patch in a photo. */
  .site-topbar,
  .site-topbar--intro {
    grid-template-columns: 1fr auto; height: 58px;
    background: transparent; border-bottom: 0; pointer-events: none;
  }
  .site-topbar > * { pointer-events: auto; }
  /* No bar band on phones by design — so no frosted veil and no progress rule
     either; the marks float straight over the scene. */
  .site-topbar::before, .site-topbar::after { display: none; }
  /* Half-amplitude parallax: a phone screen is short, so the same offsets read
     as a lurch rather than depth. */
  .scene-motion { --pm: .5; }
  .site-wordmark .myst-logo {
    width: 74px !important;
    filter: brightness(0) invert(94%) sepia(10%) saturate(480%) hue-rotate(352deg) brightness(102%)
            drop-shadow(0 1px 5px rgba(0,0,0,.45));
  }
  .site-burger__line { box-shadow: 0 1px 4px rgba(0,0,0,.4); }
  .site-topbar--light-scene .site-wordmark .myst-logo { filter: drop-shadow(0 1px 4px rgba(241,235,221,.9)); }
  .site-topbar--light-scene .site-burger__line { background: var(--ink); box-shadow: none; }
  .site-burger { display: inline-flex; }
  .site-menu { display: flex; }
  /* Keep the bar (logo + burger) always reachable on phones. */
  .site-topbar--hidden { transform: none; opacity: 1; pointer-events: auto; }
  /* Hero on phones: no scroll-scrub (iOS can't seek video) — one screen with the
     poster image + tagline. The video element is hidden; no 17MB download. */
  .site--scenes .home > section.home-reel { min-height: 100svh; }
  /* The loop plays over the poster, which stays as the stage background so the
     first paint is the still rather than a black gap. */
  /* Vertical poster here: the desktop one is a 16:9 frame and would be cropped
     to a sliver behind a portrait phone screen. */
  .home-reel__stage { background: #14120c url('assets/myst-hero-mobile.jpg?v=42f7f3c1') center/cover no-repeat; }
  /* Reduced motion: no loop is started, so keep the poster alone. */
  @media (prefers-reduced-motion: reduce) { .home-reel__video { display: none; } }
  /* A phone is far too narrow for one line — wrap again, and drop the desktop
     nowrap or the line would run off the screen. */
  .home-reel__title h1 { font-size: clamp(40px, 12vw, 68px); white-space: normal; max-width: 18ch; }
  /* Team on phones: stack photo on top, quote below (was a squeezed 2-col grid).
     Half the screen each — the quote's autofit (Home.jsx) shrinks the text to
     the card, so a fixed one-screen split never clips. */
  .site--scenes .team-scene { grid-template-columns: 1fr; height: 100svh; }
  .site--scenes .team-scene__photos { min-height: 50svh; }
  /* Bias the crop toward the top so faces (usually upper-frame) aren't cut.
     A larger percentage reveals more of the lower frame, i.e. nudges the image
     up. Chernysheva and Muzalev are framed differently and keep the old crop. */
  .site--scenes .team-scene__photo { background-position: center 36%; }
  .site--scenes .team-scene__photo.is-keep-crop { background-position: center 22%; }
  .site--scenes .team-scene__body {
    height: 50svh; justify-content: space-between;
    padding: 20px clamp(22px, 6vw, 36px) 26px;
  }
  .site--scenes .team-scene__card { justify-content: flex-start; padding-block: 0; }
  /* font-size here is advisory only — the autofit sets it inline. */
  .team-scene .myst-quote blockquote { line-height: 1.32; }
  /* The dot row grows with the team. At 13 people it needed 376px against 343
     available, and because a grid item's min-width is auto that shortfall
     stretched the whole scene to 423px — pushing the card off the screen edge.
     min-width:0 stops the row from dictating the track width ever again; the
     tighter gaps make it fit today, and wrapping absorbs future hires. */
  .site--scenes .team-scene__photos,
  .site--scenes .team-scene__body { min-width: 0; }
  .team-scene__nav { gap: 14px; }
  .team-scene__nav .reel__dots { flex-wrap: wrap; justify-content: center; gap: 6px; }
  .team-scene__by { padding-top: 16px; }
  .team-scene__name { font-size: clamp(20px, 5.4vw, 25px); }
  .team-scene__role { margin-top: 8px; }
  .team-scene__nav { margin-top: 18px; }
  .home-intro { grid-template-columns: 1fr; }
  /* Poster from the vertical loop that actually plays here, so the still shown
     before playback isn't a frame of the different landscape render. */
  .home-intro { background-image: url('assets/myst-tapestry-mobile.jpg?v=8135dc2e'); }
  /* The copy sits straight on the tapestry — readable over the old dark loop,
     not over the new one. A panel under just the text keeps the gobelin vivid
     around it instead of veiling the whole scene. */
  .home-intro__panel {
    display: block; position: relative; z-index: 2;
    padding: 26px 22px 30px;
    /* Lighter fill so the gobelin reads through, with more blur to make up for
       it: blurring softens the busy pattern behind the type, which buys back
       legibility that the transparency gives away. */
    background: rgba(14, 22, 17, .48);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
    border: 1px solid rgba(241, 235, 221, .12);
  }
  /* Buttons live outside the panel, straight on the tapestry. */
  .home-intro__actions { margin-top: 26px; }
  .home-intro__lede, .home-intro__actions, .home-intro .eyebrow { grid-column: 1; }
  .objects__grid { grid-template-columns: 1fr; }
  /* Filters: tags on top, search full-width below — no cramped/clipped input. */
  .objects__filters { flex-direction: column; align-items: stretch; gap: 22px; }
  .objects__search { margin-left: 0; }
  /* 16px is the threshold below which iOS Safari zooms the page in on focus.
     The letter-spacing comes down so the field still sits with the tags. */
  .objects__search input { width: 100%; font-size: 16px; letter-spacing: .08em; }
  /* The design system puts a U+2197 arrow on every button and arrow link. iOS
     renders that codepoint with the colour emoji font, so it lands as a sticker
     next to the type — drop it on phones. Without the arrow the button has one
     child, so its space-between would shove the label to the left edge. */
  .myst-arrow { display: none; }
  .myst-btn { justify-content: center; }
  .pub-article__social-links a span[aria-hidden] { display: none; }
  .process-stages { grid-template-columns: 1fr; }
  .process-stage { border-left: 0; border-top: 1px solid var(--hairline); }
  .contact__row { grid-template-columns: 1fr; }
  /* Footer on phones: left-aligned single column (was pushed to the right edge). */
  .site-footer { height: auto; min-height: 60svh; justify-content: flex-start; padding-bottom: 44px; }
  .site-footer__info { align-items: flex-start; }
  .site-footer__contacts { text-align: left; }
  .site-footer__link { align-self: flex-start; }
  .site-footer__mark { display: none; }
}
