/* ============================================================
   AR — Andrii Rymarchuk portfolio
   Design system: brand olive palette, bento grid, dual theme
   ============================================================ */

/* ---------- Fonts ----------
   TT Travels Next + Avanti are the display/accent faces from the
   previous site. They may not carry Cyrillic, so Inter is listed
   directly after them — the browser then falls back per-glyph and
   Ukrainian text stays readable in the same visual rhythm.        */

@font-face {
  font-family: 'TT Display';
  src: url('../fonts/tt-black-it.woff2') format('woff2');
  font-weight: 900; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'TT Display';
  src: url('../fonts/tt-xbold-it.woff2') format('woff2');
  font-weight: 800; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'TT Display';
  src: url('../fonts/tt-bold-it.woff2') format('woff2');
  font-weight: 700; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'TT Display';
  src: url('../fonts/tt-demibold-it.woff2') format('woff2');
  font-weight: 600; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'TT Outline';
  src: url('../fonts/tt-black-outline-it.woff2') format('woff2');
  font-weight: 900; font-style: italic; font-display: swap;
}
@font-face {
  font-family: 'TT UI';
  src: url('../fonts/tt-bold.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Avanti';
  src: url('../fonts/avanti-regular.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Avanti';
  src: url('../fonts/avanti-bold.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}

/* ---------- Tokens ---------- */

:root {
  --font-display: 'TT Display', 'Inter', system-ui, sans-serif;
  --font-outline: 'TT Outline', 'TT Display', 'Inter', sans-serif;
  --font-ui: 'TT UI', 'Inter', system-ui, sans-serif;
  --font-hand: 'Avanti', 'Segoe Script', cursive;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;

  --r-card: 26px;
  --r-sm: 14px;
  --r-pill: 999px;

  --gap: 14px;
  --pad: clamp(18px, 2.4vw, 30px);
  --shell: 1240px;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur: 0.55s;

  --hdr-h: 68px;

  color-scheme: dark;
}

/* Every colour below traces back to one of the nine hex values from the
   brand palette. States that aren't in the palette directly (a lifted
   card, a soft divider) are mixed FROM those nine, never invented. */

:root,
:root[data-theme='dark'] {
  --bg: #1c1c16;
  --card: #1c1e19;
  --fg: #edefdd;
  --fg-dim: #a0a393;
  --accent: #c1c6b5;
  --accent-mute: #84877c;
  --line: #424438;

  --bg-deep: color-mix(in srgb, #1c1c16 88%, black 12%);
  --card-hi: color-mix(in srgb, #1c1e19 78%, #424438 22%);
  --line-soft: color-mix(in srgb, #424438 42%, #1c1e19 58%);

  --shadow: 0 18px 50px -22px rgba(0, 0, 0, 0.9);
  --img-veil: 0.86;
  color-scheme: dark;
}

:root[data-theme='light'] {
  --bg: #e5e8d8;
  --card: #edefdd;
  --fg: #1c1e19;
  --fg-dim: #686860;
  --accent: #424438;
  --accent-mute: #84877c;
  --line: #c1c6b5;

  --bg-deep: color-mix(in srgb, #e5e8d8 85%, black 15%);
  --card-hi: color-mix(in srgb, #edefdd 74%, #c1c6b5 26%);
  --line-soft: color-mix(in srgb, #c1c6b5 55%, #e5e8d8 45%);

  --shadow: 0 18px 46px -24px rgba(45, 48, 34, 0.4);
  --img-veil: 1;
  color-scheme: light;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    --bg: #e5e8d8;
    --card: #edefdd;
    --fg: #1c1e19;
    --fg-dim: #686860;
    --accent: #424438;
    --accent-mute: #84877c;
    --line: #c1c6b5;

    --bg-deep: color-mix(in srgb, #e5e8d8 85%, black 15%);
    --card-hi: color-mix(in srgb, #edefdd 74%, #c1c6b5 26%);
    --line-soft: color-mix(in srgb, #c1c6b5 55%, #e5e8d8 45%);

    --shadow: 0 18px 46px -24px rgba(45, 48, 34, 0.4);
    --img-veil: 1;
    color-scheme: light;
  }
}

/* ---------- Base ---------- */

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  /* clip, not hidden: `hidden` would make <body> its own scroll container,
     which freezes window.scrollY and kills the sticky header + parallax. */
  overflow-x: clip;
  transition: background-color 0.4s var(--ease), color 0.4s var(--ease);
}

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

/* An author `display` beats the UA sheet's [hidden] rule, so anything given
   display:grid/flex stays on screen while "hidden". Make the attribute win. */
[hidden] { display: none !important; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 6px;
}

.shell {
  width: min(100% - 2 * var(--pad), var(--shell));
  margin-inline: auto;
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- Header ---------- */

.hdr {
  position: sticky; top: 0; z-index: 60;
  height: var(--hdr-h);
  display: flex; align-items: center;
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s var(--ease), background-color 0.3s var(--ease);
}
.hdr.is-stuck { border-bottom-color: var(--line-soft); }

.hdr__in {
  display: flex; align-items: center; gap: 18px;
  width: min(100% - 2 * var(--pad), var(--shell));
  margin-inline: auto;
}

.brand { display: flex; align-items: center; flex: 0 0 auto; }
.brand img { height: 30px; width: auto; }
.brand .logo-light { display: none; }
:root[data-theme='light'] .brand .logo-dark { display: none; }
:root[data-theme='light'] .brand .logo-light { display: block; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .brand .logo-dark { display: none; }
  :root:not([data-theme]) .brand .logo-light { display: block; }
}

.nav { display: flex; align-items: center; gap: 4px; margin-inline: auto; }
.nav a {
  font-family: var(--font-ui);
  font-size: 12px; letter-spacing: 0.08em;
  padding: 8px 14px; border-radius: var(--r-pill);
  color: var(--fg-dim);
  display: inline-flex; align-items: center; gap: 7px;
  transition: color 0.25s var(--ease), background-color 0.25s var(--ease),
              transform 0.25s var(--ease);
  white-space: nowrap;
}
.nav__icon { width: 14px; height: 14px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* Hover/active read as a real chip — full fg/bg inversion, the same
   high-contrast pair the CONTACT pill uses — not a faint tint that
   nearly disappears into the page. */
.nav a:hover { color: var(--bg); background: var(--fg); transform: translateY(-1px); }
.nav a[aria-current='page'] { color: var(--bg); background: var(--fg); }
.nav a[aria-current='page']:hover { transform: none; }

.hdr__tools { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

.icon-btn {
  width: 36px; height: 36px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  color: var(--fg-dim);
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease),
              background-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.icon-btn:hover { color: var(--fg); border-color: var(--accent-mute); background: var(--card); transform: translateY(-1px); }
.icon-btn svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

/* .icon-btn is `display:grid` with no template-columns, which puts every
   extra child on its own implicit ROW — flag stacked above label instead
   of beside it. Flex overrides that with an explicit left-to-right row. */
.lang-btn {
  display: inline-flex !important; flex-direction: row; align-items: center;
  width: auto; padding: 0 13px; gap: 7px;
  font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.09em;
  /* .icon-btn's border-radius:50% turns a wide, fixed-height box into an
     ellipse/oval instead of a stadium — every other pill on the site
     (crumb, filter, btn-contact) uses --r-pill, so match that here too. */
  border-radius: var(--r-pill);
}
.lang-btn img { width: 17px; height: 12px; object-fit: cover; border-radius: 2px; }

/* The one button on the site that should be irresistible: solid fill,
   a permanent soft glow so it reads as "alive" even before the cursor
   arrives, and a light-sweep + lift on hover that makes it feel worth
   clicking rather than just another pill. */
a.btn-contact {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  font-family: var(--font-ui); font-weight: 600; font-size: 12px; letter-spacing: 0.08em;
  padding: 9px 22px; border-radius: var(--r-pill);
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--fg); color: var(--bg);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--fg) 40%, transparent),
              0 6px 20px -6px color-mix(in srgb, var(--accent) 65%, transparent);
  transition: transform 0.35s var(--ease), box-shadow 0.35s var(--ease);
  white-space: nowrap;
  animation: contact-glow 3.2s ease-in-out infinite;
}
/* Diagonal light sweep, parked off-frame until hover. */
a.btn-contact::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(115deg,
    transparent 20%,
    color-mix(in srgb, var(--bg) 55%, transparent) 50%,
    transparent 80%);
  transform: translateX(-130%);
  transition: transform 0.65s var(--ease);
}
a.btn-contact:hover {
  transform: translateY(-2px) scale(1.035);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--fg) 55%, transparent),
              0 10px 28px -8px color-mix(in srgb, var(--accent) 85%, transparent);
  animation-play-state: paused;
}
a.btn-contact:hover::before { transform: translateX(130%); }
a.btn-contact:active { transform: translateY(0) scale(0.98); }

@keyframes contact-glow {
  0%, 100% {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--fg) 40%, transparent),
                0 6px 20px -6px color-mix(in srgb, var(--accent) 55%, transparent);
  }
  50% {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--fg) 40%, transparent),
                0 8px 26px -6px color-mix(in srgb, var(--accent) 90%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  a.btn-contact { animation: none; }
  a.btn-contact::before { display: none; }
}

.nav-toggle { display: none; }

/* Two contact buttons live in the markup: the header one is for desktop,
   the in-nav one only appears inside the mobile drawer. */
.nav a.btn-contact { display: none; }

/* Icon pairs swap on state instead of being rebuilt in JS. */
.icon-btn .i-moon,
.icon-btn[data-state='light'] .i-sun { display: none; }
.icon-btn[data-state='light'] .i-moon { display: block; }

.nav-toggle .i-close { display: none; }
.nav-toggle[aria-expanded='true'] .i-menu { display: none; }
.nav-toggle[aria-expanded='true'] .i-close { display: block; }

/* Tablet gets the same collapsed hamburger nav as phone — inline nav items
   plus EN/theme/contact were crowding awkwardly on iPad-width viewports. */
@media (max-width: 1024px) {
  .nav {
    position: fixed; inset: var(--hdr-h) 0 auto 0;
    flex-direction: column; align-items: stretch; gap: 2px;
    background: var(--bg); border-bottom: 1px solid var(--line-soft);
    padding: 14px var(--pad) 22px;
    margin: 0;
    transform: translateY(-12px); opacity: 0; pointer-events: none;
    transition: transform 0.3s var(--ease), opacity 0.3s var(--ease);
  }
  .nav.is-open { transform: none; opacity: 1; pointer-events: auto; }
  .nav a { font-size: 15px; padding: 13px 16px; }
  .nav-toggle { display: grid; }
  .hdr__tools a.btn-contact.hide-sm { display: none; }
  /* flex, not block: block dropped the icon/label gap the pill sets up, so
     the arrow ran straight into the first letter. */
  .nav a.btn-contact {
    display: flex; justify-content: center; gap: 7px;
    text-align: center; margin-top: 10px;
  }
}

/* ---------- Bento grid ---------- */

.bento {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--gap);
  padding-block: clamp(18px, 3vw, 34px) clamp(40px, 6vw, 76px);
}

.card {
  position: relative;
  background: var(--card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-card);
  padding: var(--pad);
  overflow: hidden;
  isolation: isolate;
  transition: transform 0.45s var(--ease), border-color 0.45s var(--ease),
              background-color 0.45s var(--ease);
}
a.card:hover, .card.is-hoverable:hover {
  transform: translateY(-4px);
  border-color: var(--accent-mute);
  background: var(--card-hi);
}

.card__kicker {
  font-family: var(--font-ui);
  font-size: 10px; letter-spacing: 0.16em;
  color: var(--fg-dim); text-transform: uppercase;
  margin: 0 0 6px;
}
.card__title {
  font-family: var(--font-display);
  font-weight: 700; font-style: italic;
  /* Ukrainian labels run long; never break a word, shrink instead. */
  font-size: clamp(16px, 1.6vw, 21px);
  letter-spacing: -0.01em;
  margin: 0;
  overflow-wrap: normal; hyphens: manual;
}
.c-video .card__title,
.c-design .card__title,
.c-profile .card__title { font-size: clamp(15px, 1.4vw, 19px); }

/* circular arrow affordance, bottom-right of a card */
.card__go {
  position: absolute; right: var(--pad); bottom: var(--pad);
  width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--line);
  color: var(--fg-dim);
  transition: transform 0.4s var(--ease), color 0.3s var(--ease),
              border-color 0.3s var(--ease), background-color 0.3s var(--ease);
  z-index: 3;
}
.card__go svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
a.card:hover .card__go, .card.is-hoverable:hover .card__go {
  color: var(--bg); background: var(--fg); border-color: var(--fg);
  transform: rotate(-45deg);
}

.card__foot {
  position: relative; z-index: 2;
  margin-top: auto;
  padding-right: 54px; /* clears the circular arrow */
  min-width: 0;
}

/* ---------- Home cards ---------- */

/* Rows resolve to 5+4+3 / (hero)+3+4 / 3+4+5 — always twelve columns. */
/* Explicit placement, because .hero-portrait shares this exact cell —
   with auto-placement the portrait claimed it and shoved the card aside. */
.c-hero    { grid-column: 1 / span 6; grid-row: 1 / span 2; min-height: 420px; }
.c-about   { grid-column: span 3; display: flex; flex-direction: column; min-height: 210px; }
.c-video   { grid-column: span 3; display: flex; flex-direction: column; min-height: 210px; }
.c-design  { grid-column: span 3; display: flex; flex-direction: column; min-height: 210px; }
.c-offer   { grid-column: span 3; display: flex; flex-direction: column; min-height: 210px; }
.c-profile { grid-column: span 3; display: flex; flex-direction: column; min-height: 200px; }
.c-stats   { grid-column: span 5; padding: calc(var(--pad) * 0.5); }
.c-cta     { grid-column: span 4; display: flex; flex-direction: column; justify-content: flex-end; min-height: 200px; }

/* Full-width marquee band sitting above the grid. */
.ticker-strip {
  padding: 0; display: flex; align-items: center;
  height: 58px; margin-bottom: var(--gap);
}

/* Tablet/phone drop the 12-column track set entirely rather than keeping it
   and spanning across. Twelve `1fr` tracks at phone width are ~28px each, and
   `1fr` means `minmax(auto, 1fr)` — any card whose min-content is wider than
   its share blows the tracks out to ragged, unequal widths, which is what
   made the cards land two-up at mismatched sizes. Two real columns on tablet,
   one on phone, both with `minmax(0, 1fr)` so a track may shrink below its
   content instead of forcing the grid wider. */
@media (max-width: 1024px) {
  .bento { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .bento > .card { grid-column: auto / span 1; grid-row: auto; }
  /* `.bento >` prefix on purpose: `.bento > .card` above is two classes and
     would otherwise outrank a bare `.c-hero`, resetting grid-row to auto and
     dropping the portrait out of the card's cell into a row of its own. */
  .bento > .c-hero { grid-column: 1 / -1; grid-row: 1; }
  /* Stats stays half-width so it pairs with .c-profile — made full-width it
     pushed profile into a row on its own with dead space beside it. */
  .bento > .c-cta { grid-column: 1 / -1; }

  /* Video editing leads graphic design here. The source order puts design
     first because of where the two sit in the desktop grid, which stops
     mattering once the cards stack — and video is the main offering, so it
     should be the first thing reached after scrolling. Every auto-placed
     card needs an explicit order, otherwise the untouched ones (order 0)
     would all jump ahead of the two being reordered. */
  .bento > .c-about   { order: 1; }
  .bento > .c-video   { order: 2; }
  .bento > .c-design  { order: 3; }
  .bento > .c-offer   { order: 4; }
  .bento > .c-profile { order: 5; }
  .bento > .c-stats   { order: 6; }
  .bento > .c-cta     { order: 7; }
}
@media (max-width: 700px) {
  .bento { grid-template-columns: minmax(0, 1fr); }
  .bento > .card { grid-column: 1 / -1; }
  .bento > .c-hero { grid-row: 1; }
}

/* ---------- Hero card ----------
   The portrait is a transparent cut-out, not a framed photo — it sits flush
   in the card's own padding corner with no background of its own, and dips
   past the padding line so it reads as stepping out of the cell rather than
   being boxed inside it. */
.c-hero {
  display: flex; flex-direction: column;
  position: relative;
}

.hero__meta { min-width: 0; }
.hero__role {
  font-family: var(--font-ui); font-size: 10px; letter-spacing: 0.18em;
  color: var(--fg-dim); margin: 0 0 8px;
}
.hero__name {
  font-family: var(--font-display); font-weight: 900; font-style: italic;
  font-size: clamp(28px, 3.2vw, 44px); line-height: 1.02;
  letter-spacing: -0.025em; margin: 0 0 10px;
  overflow-wrap: normal; hyphens: manual;
}
.hero__tagline {
  font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.1em;
  color: var(--fg-dim); margin: 0; text-transform: uppercase;
}
.hero__quote {
  font-family: var(--font-hand);
  font-size: clamp(17px, 1.8vw, 22px); line-height: 1.45;
  color: var(--fg); margin: 22px 0 0;
  max-width: 40ch; position: relative; z-index: 2;
}

/* Looping AR mark. Two renders — the source art is white with alpha, so
   each is pre-composited onto its theme's card colour. */
.hero__logo {
  position: absolute; right: var(--pad); top: var(--pad);
  width: 84px; border-radius: 14px;
  overflow: hidden; pointer-events: none;
  border: 1px solid var(--line-soft);
}
.hero__logo video { width: 100%; height: auto; display: block; }
.hero__logo .logo-light { display: none; }
:root[data-theme='light'] .hero__logo .logo-dark { display: none; }
:root[data-theme='light'] .hero__logo .logo-light { display: block; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .hero__logo .logo-dark { display: none; }
  :root:not([data-theme]) .hero__logo .logo-light { display: block; }
}

/* ticker */
.ticker { overflow: hidden; width: 100%; mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }
.ticker__track { display: flex; width: max-content; will-change: transform; }
.ticker__track span {
  font-family: var(--font-ui); font-size: 12px; letter-spacing: 0.2em;
  color: var(--fg-dim); padding-right: 28px; white-space: nowrap;
}

/* media preview inside cards — kept clearly visible, not a faint hint. */
.card__media {
  position: absolute; inset: 0; z-index: 0;
  opacity: 0.72; filter: saturate(0.85);
  transition: opacity 0.5s var(--ease), transform 0.9s var(--ease);
}
.card__media img, .card__media video { width: 100%; height: 100%; object-fit: cover; }
a.card:hover .card__media { opacity: 0.85; transform: scale(1.04); }
/* The design teaser carries readable client copy — keep it a touch calmer. */
.c-design .card__media { opacity: 0.55; }
a.c-design:hover .card__media { opacity: 0.68; }
/* Scrim keeps only the kicker/title corner legible; the rest of the
   artwork stays clear rather than washed out under a heavy veil. */
.card__media::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(180deg,
    transparent 0%,
    transparent 40%,
    color-mix(in srgb, var(--card) 55%, transparent) 74%,
    color-mix(in srgb, var(--card) 92%, transparent) 100%);
}

/* services — each icon sits right beside its own label, not in a
   separate row disconnected from the text it belongs to. */
.offer__icon {
  width: 30px; height: 30px; border-radius: 9px; flex: 0 0 auto;
  display: grid; place-items: center;
  border: 1px solid var(--line-soft); color: var(--fg-dim);
  transition: color 0.3s var(--ease), border-color 0.3s var(--ease), transform 0.3s var(--ease);
}
.card:hover .offer__icon { color: var(--fg); border-color: var(--accent-mute); }
.offer__icon svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.offer__list { list-style: none; margin: 0 0 22px; padding: 0; columns: 2; column-gap: 26px; }
.offer__list li {
  display: flex; align-items: center; gap: 10px;
  font-size: 13.5px; color: var(--fg-dim); padding: 5px 0; break-inside: avoid;
}
@media (max-width: 620px) { .offer__list { columns: 1; } }

/* profiles */
.profile__row { display: flex; gap: 10px; margin-bottom: auto; }
.profile__dot {
  width: 54px; height: 54px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--card-hi); border: 1px solid var(--line-soft);
  color: var(--fg-dim);
  transition: transform 0.4s var(--ease), color 0.3s var(--ease);
}
.card:hover .profile__dot { color: var(--fg); }
.card:hover .profile__dot:first-child { transform: translateX(-3px) rotate(-6deg); }
.card:hover .profile__dot:last-child { transform: translateX(3px) rotate(6deg); }
.profile__dot svg {
  width: 22px; height: 22px;
  fill: none; stroke: currentColor; stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round;
}

/* stats */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; height: 100%; }
.stat {
  background: color-mix(in srgb, var(--card-hi) 60%, transparent);
  border: 1px solid var(--line-soft);
  border-radius: 18px;
  padding: clamp(16px, 2.2vw, 26px) 10px;
  text-align: center;
  display: flex; flex-direction: column; justify-content: center; gap: 8px;
  min-width: 0; /* four-digit years otherwise force the grid to overflow */
}
.stat__num {
  font-family: var(--font-display); font-weight: 900; font-style: italic;
  font-size: clamp(24px, 2.8vw, 38px); line-height: 1; letter-spacing: -0.03em;
}
.stat__lbl { font-family: var(--font-ui); font-size: 9.5px; letter-spacing: 0.14em; color: var(--fg-dim); line-height: 1.5; }

/* cta */
.cta__title {
  font-family: var(--font-display); font-weight: 900; font-style: italic;
  /* "Працюймо" is a single long word — size so it fits the card unbroken. */
  font-size: clamp(28px, 3.2vw, 46px); line-height: 1.02;
  letter-spacing: -0.03em; margin: 0; padding-right: 58px;
  overflow-wrap: normal; hyphens: manual;
}
.cta__title .hand {
  font-family: var(--font-hand); font-style: normal; font-weight: 700;
  color: var(--accent); letter-spacing: 0;
}
/* Top-right, clear of the title text (which is left-aligned and can run
   two lines tall) — it used to sit top-left and overlap the first line. */
.cta__spark {
  position: absolute; right: var(--pad); top: var(--pad);
  width: 30px; height: 30px; color: var(--accent-mute); opacity: 0.8;
}
.cta__spark svg { width: 100%; height: 100%; fill: currentColor; }

/* ---------- Page heading ---------- */

.page-head {
  padding-block: clamp(26px, 5vw, 60px) clamp(18px, 3vw, 34px);
  text-align: center;
}
.page-head__title {
  font-family: var(--font-display); font-weight: 900; font-style: italic;
  font-size: clamp(44px, 11vw, 132px); line-height: 0.86;
  letter-spacing: -0.04em; margin: 0;
  text-transform: lowercase;
}
.page-head__title .outline {
  font-family: var(--font-outline);
  color: transparent;
  -webkit-text-stroke: 1px var(--accent-mute);
  display: block;
}
.page-head__sub {
  font-family: var(--font-hand);
  font-size: clamp(16px, 2vw, 22px); color: var(--fg-dim);
  margin: 16px 0 0;
}

/* breadcrumb / quick-nav row — only the Back pill carries an arrow, so the
   row doesn't read as three identical, confusing directional cues. */
.crumbs { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; padding-block: 4px 22px; }
.crumb {
  font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.1em;
  padding: 9px 17px; border-radius: var(--r-pill);
  border: 1px solid var(--line); color: var(--fg-dim);
  display: inline-flex; align-items: center; gap: 7px;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease),
              background-color 0.25s var(--ease), transform 0.25s var(--ease);
}
/* Same fg/bg inversion as the nav chips — a crumb that's actually being
   hovered should look pressable, not just faintly tinted. */
.crumb:hover { color: var(--bg); border-color: var(--fg); background: var(--fg); transform: translateY(-1px); }
.crumb svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.crumb--back { border-style: dashed; }

/* ---------- Category grid (video hub) ---------- */

.cat-grid {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  padding-bottom: clamp(30px, 5vw, 60px);
}
.cat {
  position: relative; display: flex; flex-direction: column; justify-content: flex-end;
  aspect-ratio: 16 / 10; padding: var(--pad);
  border-radius: var(--r-card); overflow: hidden;
  border: 1px solid var(--line-soft); background: var(--card);
  transition: transform 0.45s var(--ease), border-color 0.45s var(--ease);
}
.cat:hover { transform: translateY(-5px); border-color: var(--accent-mute); }
.cat video, .cat img.cat__poster {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0;
  transition: transform 0.9s var(--ease), opacity 0.5s var(--ease);
  opacity: var(--img-veil);
}
.cat:hover video { transform: scale(1.05); }
.cat::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(10,12,8,0.05) 0%, rgba(10,12,8,0.55) 55%, rgba(10,12,8,0.9) 100%);
}
.cat__body { position: relative; z-index: 2; color: #edefdd; }
.cat__name {
  font-family: var(--font-display); font-weight: 800; font-style: italic;
  font-size: clamp(17px, 2vw, 23px); line-height: 1.1; margin: 0 0 4px;
  letter-spacing: -0.01em;
}
.cat__watch {
  font-family: var(--font-ui); font-size: 10px; letter-spacing: 0.16em;
  color: rgba(237,239,221,0.72); display: inline-flex; align-items: center; gap: 6px;
}
.cat__watch svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.cat__count {
  position: absolute; top: var(--pad); right: var(--pad); z-index: 2;
  font-family: var(--font-ui); font-size: 10px; letter-spacing: 0.1em;
  padding: 5px 11px; border-radius: var(--r-pill);
  background: rgba(20,22,16,0.55); color: #edefdd;
  backdrop-filter: blur(8px);
}

/* locked variant — full-bleed banner, capped so it stays a band */
.cat--locked { aspect-ratio: 3 / 1; max-height: 300px; margin-top: var(--gap); }
.cat--locked .cat__name { font-size: clamp(20px, 2.6vw, 30px); }
.locked-note {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  font-size: 13px; color: rgba(237,239,221,0.78); margin: 0 0 10px;
  max-width: 52ch;
}
.locked-note svg { width: 15px; height: 15px; flex: 0 0 auto; fill: none; stroke: currentColor; stroke-width: 1.8; }

/* ---------- Video grid ---------- */

.vid-grid {
  display: grid; gap: var(--gap);
  /* Big by default — this is a video editor's reel, not a thumbnail wall. */
  grid-template-columns: repeat(auto-fill, minmax(560px, 1fr));
  padding-bottom: clamp(30px, 5vw, 60px);
}
.vid-grid.is-vertical { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }

.vid {
  border-radius: var(--r-card); overflow: hidden;
  border: 1px solid var(--line-soft); background: var(--card);
  transition: transform 0.4s var(--ease), border-color 0.4s var(--ease);
}
.vid:hover { transform: translateY(-4px); border-color: var(--accent-mute); }

.vid__frame {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  background: var(--bg-deep); cursor: pointer; display: block; border: 0; padding: 0;
  overflow: hidden;
}
.is-vertical .vid__frame { aspect-ratio: 9 / 16; }
.vid__frame img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 0.7s var(--ease), opacity 0.4s var(--ease);
  opacity: 0.92;
}
.vid__frame:hover img { transform: scale(1.05); opacity: 1; }
.vid__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.vid__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  transition: opacity 0.3s var(--ease);
}
.vid__play span {
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center;
  background: rgba(20,22,16,0.55); backdrop-filter: blur(10px);
  border: 1px solid rgba(237,239,221,0.28); color: #edefdd;
  transition: transform 0.35s var(--ease), background-color 0.3s var(--ease);
}
.vid__frame:hover .vid__play span { transform: scale(1.08); background: rgba(20,22,16,0.75); }
.vid__play svg { width: 18px; height: 18px; fill: currentColor; margin-left: 3px; }
.vid__cap {
  font-family: var(--font-ui); font-size: 10.5px; letter-spacing: 0.09em;
  color: var(--fg-dim); padding: 13px var(--pad) 15px; line-height: 1.5;
}

/* ---------- Design gallery ---------- */

.filters { display: flex; flex-wrap: wrap; gap: 8px; padding-bottom: 22px; }
.filter {
  font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.1em;
  padding: 10px 18px; border-radius: var(--r-pill);
  border: 1px solid var(--line); color: var(--fg-dim);
  transition: all 0.28s var(--ease);
}
.filter:hover { color: var(--fg); border-color: var(--accent-mute); }
.filter[aria-pressed='true'] { background: var(--fg); color: var(--bg); border-color: var(--fg); }

.work-grid {
  display: grid; gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  padding-bottom: clamp(30px, 5vw, 60px);
}
.work {
  position: relative; display: block;
  border-radius: var(--r-card); overflow: hidden;
  border: 1px solid var(--line-soft); background: var(--card);
  cursor: zoom-in;
  transition: transform 0.4s var(--ease), border-color 0.4s var(--ease), opacity 0.35s var(--ease);
}
.work:hover { transform: translateY(-4px); border-color: var(--accent-mute); }
.work img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; transition: transform 0.8s var(--ease); }
.work:hover img { transform: scale(1.05); }
.work__cap {
  font-family: var(--font-ui); font-size: 10px; letter-spacing: 0.1em;
  color: var(--fg-dim); padding: 13px var(--pad) 15px; line-height: 1.5;
}
.work.is-hidden { display: none; }
/* Marks a project that has more than one banner variant behind it. */
.work__count {
  position: absolute; top: 12px; right: 12px; z-index: 2;
  display: inline-flex; align-items: center; gap: 5px;
  font-family: var(--font-ui); font-size: 10px; letter-spacing: 0.08em;
  padding: 5px 10px; border-radius: var(--r-pill);
  background: rgba(20,22,16,0.6); color: #edefdd; backdrop-filter: blur(8px);
}
.work__count svg { width: 11px; height: 11px; }

/* ---------- Lightbox ----------
   Two modes on one overlay: an image carousel with prev/next through a
   project's variants, and a large video player — big on every device
   instead of the old approach of just growing the grid card in place. */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  display: none;
  /* Was place-items:center on the overlay itself, which centres the whole
     modal — fine for a plain image, but the moment a description makes it
     taller than the viewport, centring clips it symmetrically top AND
     bottom with no way to scroll up into the missing part. Scrolling now
     lives on the overlay; .lightbox__inner centres itself via auto margins
     below, which degrades to top-aligned (fully scrollable) the moment it
     no longer fits, instead of clipping. */
  overflow-y: auto;
  padding: clamp(16px, 4vw, 52px);
  background: color-mix(in srgb, var(--bg-deep) 92%, transparent);
  backdrop-filter: blur(18px);
}
.lightbox.is-open { display: block; }

.lightbox__inner {
  display: flex; flex-direction: column; align-items: center;
  max-width: 100%; width: 100%;
  margin-block: auto; /* centred when it fits, top-aligned + scrollable when it doesn't */
}

/* Frame wraps the stage AND its prev/next buttons together, sized to fit
   the image rather than the full overlay width — that's what lets the
   arrows hug the image's own edges instead of sitting pinned way out at
   the viewport edges, far from the picture they control. */
.lightbox__frame {
  position: relative; display: inline-flex; align-items: center;
  max-width: 100%; margin-inline: auto;
}
.lightbox__stage { display: flex; justify-content: center; min-width: 0; }
.lightbox.is-image .lightbox__stage img {
  max-width: 100%; max-height: 74vh; object-fit: contain;
  border-radius: 18px; border: 1px solid var(--line);
}
/* Video mode fills most of the viewport at the right aspect ratio,
   instead of the small fixed-size card the embed used to be squeezed into. */
.lightbox.is-video .lightbox__stage {
  width: min(94vw, 128vh);
  aspect-ratio: 16 / 9;
  position: relative;
  border-radius: 18px; overflow: hidden; border: 1px solid var(--line);
  background: #000;
}
.lightbox.is-video .lightbox__stage iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}

.lightbox__cap {
  font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.12em;
  color: var(--fg-dim); text-align: center; margin-top: 16px;
}
.lightbox__desc {
  font-family: var(--font-body); font-size: 15px; line-height: 1.7;
  color: var(--fg); text-align: left;
  max-width: 62ch; width: 100%; margin: 18px auto 4px;
  padding: 18px 22px;
  background: var(--card);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-sm, 14px);
}
.lightbox__counter {
  font-family: var(--font-ui); font-size: 10.5px; letter-spacing: 0.14em;
  color: var(--fg-dim); text-align: center; margin-top: 6px;
}

/* button.foo (element+class) outranks .glass (class-only) regardless of
   source order — needed because .glass itself sets position:relative,
   which was silently winning over these buttons' position:absolute at
   equal specificity and collapsing them to their in-flow DOM position. */
button.lightbox__close {
  position: absolute; top: clamp(14px, 3vw, 30px); right: clamp(14px, 3vw, 30px);
  width: 44px; height: 44px; border-radius: 50%;
  display: grid; place-items: center; z-index: 3;
  border: 1px solid var(--line); color: var(--fg);
  background: var(--card);
}
.lightbox__close svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }

/* Positioned relative to .lightbox__frame (which shrinks to the image's own
   rendered size), just outside its edge — beside the photo, not stranded
   out at the viewport edge regardless of how narrow the image is. */
button.lightbox__nav {
  position: absolute; top: 50%; translate: 0 -50%; z-index: 3;
  flex: 0 0 auto;
  width: 48px; height: 48px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--line); color: var(--fg); background: var(--card);
}
.lightbox__nav svg { width: 18px; height: 18px; }
button.lightbox__nav.lightbox__nav--prev { left: -20px; right: auto; }
button.lightbox__nav.lightbox__nav--next { right: -20px; left: auto; }
@media (max-width: 780px) {
  /* Not enough room to sit outside the frame on narrow viewports — pull
     them just inside the image edge instead of overlapping the chrome. */
  button.lightbox__nav.lightbox__nav--prev { left: 8px; }
  button.lightbox__nav.lightbox__nav--next { right: 8px; }
}
@media (max-width: 620px) {
  .lightbox__nav { width: 40px; height: 40px; }
}

/* Thumbnail strip beneath the main image — click to jump straight to a
   variant instead of only stepping one at a time with prev/next. */
.lightbox__thumbs {
  display: flex; gap: 8px; flex-wrap: wrap; justify-content: center;
  max-width: min(94vw, 720px); margin-top: 16px;
}
.lightbox__thumb {
  width: 56px; height: 56px; border-radius: 10px; overflow: hidden;
  border: 1.5px solid var(--line); padding: 0; flex: 0 0 auto;
  opacity: 0.55; transition: opacity 0.25s var(--ease), border-color 0.25s var(--ease);
}
.lightbox__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lightbox__thumb:hover { opacity: 0.85; }
.lightbox__thumb.is-active { opacity: 1; border-color: var(--accent); }
@media (max-width: 620px) {
  .lightbox__thumb { width: 44px; height: 44px; }
}

/* ---------- About page ----------
   The portrait is the section's full background, not a boxed photo beside
   the copy — text sits over it on a scrim so it stays legible edge to edge. */

.about-hero {
  position: relative;
  overflow: hidden;
  min-height: clamp(420px, 62vh, 640px);
  display: flex; align-items: flex-end;
  padding: clamp(28px, 5vw, 56px);
}
.about-hero__bg { position: absolute; inset: 0; z-index: 0; }
.about-hero__bg img {
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 22%;
  filter: saturate(0.92);
  /* Soft memory-like fade: full photo at the centre, dissolving to fully
     transparent toward the edges, instead of a hard rectangular crop. No
     border/background sits behind it (the section isn't .glass anymore),
     so past the fade it's just the page's own background — no visible
     card edge anywhere, the photo simply dissolves into the site. */
  mask-image: radial-gradient(ellipse 82% 84% at 50% 34%, #000 12%, transparent 98%);
  -webkit-mask-image: radial-gradient(ellipse 82% 84% at 50% 34%, #000 12%, transparent 98%);
}
/* Scrim: clear at the top so the photo reads, dense at the bottom where
   the text sits, so the copy never fights the image for legibility. */
.about-hero::after {
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(0deg,
    color-mix(in srgb, var(--bg-deep) 88%, transparent) 0%,
    color-mix(in srgb, var(--bg-deep) 55%, transparent) 32%,
    color-mix(in srgb, var(--bg-deep) 8%, transparent) 62%,
    transparent 85%);
}

.about-intro {
  position: relative; z-index: 2;
  display: flex; flex-direction: column; gap: 4px;
  color: #edefdd; max-width: 640px;
}
.about-intro .line { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.about-intro .small {
  font-family: var(--font-hand); font-size: clamp(19px, 2.4vw, 28px); color: rgba(237,239,221,0.75);
}
.about-intro .big {
  font-family: var(--font-display); font-weight: 900; font-style: italic;
  font-size: clamp(34px, 5.4vw, 64px); line-height: 1; letter-spacing: -0.03em;
}
.about-lead { font-size: clamp(15px, 1.6vw, 18px); color: rgba(237,239,221,0.82); margin: 20px 0 24px; max-width: 42ch; }
/* A crumb used as a standalone CTA must hug its label, not fill the column. */
.about-intro > .crumb {
  align-self: flex-start;
  border-color: rgba(237,239,221,0.35); color: #edefdd;
}
.about-intro > .crumb:hover { border-color: #edefdd; background: rgba(0,0,0,0.25); }

@media (max-width: 620px) {
  .about-hero { min-height: 78vh; align-items: flex-end; padding: 22px; }
  .about-intro .big { font-size: clamp(28px, 9vw, 44px); }
}

.prose { max-width: 74ch; padding-block: clamp(30px, 5vw, 56px); }
.prose p { font-size: clamp(14.5px, 1.5vw, 16.5px); line-height: 1.85; color: var(--fg-dim); margin: 0 0 26px; }
.prose p:last-child { margin-bottom: 0; }

.tools { display: flex; flex-wrap: wrap; gap: 8px; padding-bottom: clamp(24px, 4vw, 46px); }
.tool {
  font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.1em;
  padding: 9px 16px; border-radius: var(--r-pill);
  border: 1px solid var(--line-soft); color: var(--fg-dim); background: var(--card);
}

/* ---------- Password gate ---------- */

.gate { display: grid; place-items: center; min-height: 62vh; padding-block: 40px; }
.gate__box {
  width: min(100%, 460px); text-align: center;
  background: var(--card); border: 1px solid var(--line-soft);
  border-radius: var(--r-card); padding: clamp(26px, 4vw, 44px);
}
.gate__icon { width: 46px; height: 46px; margin: 0 auto 18px; color: var(--accent-mute); }
.gate__icon svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.5; }
.gate__title {
  font-family: var(--font-display); font-weight: 900; font-style: italic;
  font-size: clamp(21px, 2.6vw, 29px); margin: 0 0 10px; letter-spacing: -0.02em;
}
.gate__note { font-size: 14px; color: var(--fg-dim); margin: 0 0 24px; }
.gate__form { display: flex; flex-direction: column; gap: 10px; }
.gate__input {
  font-family: var(--font-body); font-size: 15px;
  padding: 14px 18px; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--bg); color: var(--fg);
  text-align: center; letter-spacing: 0.1em;
  transition: border-color 0.25s var(--ease);
}
.gate__input:focus { outline: none; border-color: var(--accent); }
.gate__submit {
  font-family: var(--font-ui); font-size: 12px; letter-spacing: 0.12em;
  padding: 14px 22px; border-radius: var(--r-pill);
  background: var(--fg); color: var(--bg);
  transition: transform 0.25s var(--ease), opacity 0.25s var(--ease);
}
/* .glass is declared further down this sheet than .gate__submit and both are
   single-class selectors, so its translucent card background used to win —
   leaving the button's dark label on a near-identical dark surface (about
   1.05:1 contrast, i.e. unreadable). Qualifying with .glass takes the
   specificity back and restores the solid pill this was always meant to be.
   Not scoped to a breakpoint: the button was invisible at every width. */
.gate__submit.glass {
  background: var(--fg);
  color: var(--bg);
  border: 1px solid var(--fg);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  font-weight: 600;
}
.gate__submit.glass::before,
.gate__submit.glass::after { content: none; }
.gate__submit:hover { transform: translateY(-1px); opacity: 0.9; }
.gate__err {
  font-size: 13px; color: #d2705f; min-height: 20px; margin: 4px 0 0;
  opacity: 0; transition: opacity 0.2s var(--ease);
}
.gate__err.is-on { opacity: 1; }
.gate__box.is-shaking { animation: shake 0.4s var(--ease); }
@keyframes shake {
  0%,100% { transform: translateX(0); }
  20% { transform: translateX(-9px); }
  40% { transform: translateX(8px); }
  60% { transform: translateX(-5px); }
  80% { transform: translateX(3px); }
}

/* ---------- Footer ---------- */

.foot { border-top: 1px solid var(--line-soft); padding-block: clamp(34px, 5vw, 58px) 30px; }
.foot__grid { display: grid; grid-template-columns: 1fr auto; gap: 30px; align-items: start; }
@media (max-width: 720px) { .foot__grid { grid-template-columns: 1fr; } }

.foot__cta-row { display: flex; align-items: center; gap: 22px; margin-bottom: 22px; }
.foot__mark {
  flex: 0 0 auto; width: 108px; border-radius: 20px;
  overflow: hidden; pointer-events: none;
  border: 1px solid var(--line-soft);
}
.foot__mark video { width: 100%; height: auto; display: block; }
.foot__mark .logo-light { display: none; }
:root[data-theme='light'] .foot__mark .logo-dark { display: none; }
:root[data-theme='light'] .foot__mark .logo-light { display: block; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .foot__mark .logo-dark { display: none; }
  :root:not([data-theme]) .foot__mark .logo-light { display: block; }
}
.foot__cta {
  font-family: var(--font-display); font-weight: 800; font-style: italic;
  font-size: clamp(22px, 3.4vw, 40px); line-height: 1.08;
  letter-spacing: -0.025em; margin: 0; max-width: 18ch;
}
.foot__cta .hand { font-family: var(--font-hand); font-style: normal; color: var(--accent); }
@media (max-width: 560px) {
  .foot__cta-row { gap: 14px; }
  .foot__mark { width: 72px; border-radius: 16px; }
}
.foot__meta { display: flex; flex-direction: column; gap: 16px; }
.foot__item .k { font-family: var(--font-ui); font-size: 10px; letter-spacing: 0.16em; color: var(--fg-dim); display: block; margin-bottom: 3px; }
.foot__item a { font-size: 14.5px; border-bottom: 1px solid var(--line); padding-bottom: 2px; transition: border-color 0.25s var(--ease); }
.foot__item a:hover { border-color: var(--accent); }
.foot__bottom {
  display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-top: clamp(30px, 4vw, 48px); padding-top: 22px; border-top: 1px solid var(--line-soft);
  font-size: 11.5px; color: var(--fg-dim);
}
.foot__logo img { height: 26px; opacity: 0.75; }
.foot__logo .logo-light { display: none; }
:root[data-theme='light'] .foot__logo .logo-dark { display: none; }
:root[data-theme='light'] .foot__logo .logo-light { display: block; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .foot__logo .logo-dark { display: none; }
  :root:not([data-theme]) .foot__logo .logo-light { display: block; }
}

/* ---------- Motion ---------- */

/* Scoped to .js so the page still reads fine with scripting disabled. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
  transition-delay: var(--d, 0ms);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .js [data-reveal] { opacity: 1; transform: none; }
  .ticker__track { transform: none !important; }
}

/* ============================================================
   Liquid Glass
   Apple-style: a translucent pane that samples and refracts what's
   behind it, with a bright specular rim on the top-left edge and a
   soft inner shadow bottom-right. Built from backdrop-filter plus two
   pseudo-elements — no images, no JS, works on every card and pill.
   ============================================================ */

.glass {
  position: relative;
  isolation: isolate;
  /* Tinted enough to read as a distinct surface against the page, not
     just a faint blur that disappears into the background behind it. */
  background: color-mix(in srgb, var(--card-hi) 72%, transparent);
  backdrop-filter: blur(22px) saturate(1.7);
  -webkit-backdrop-filter: blur(22px) saturate(1.7);
  border-color: color-mix(in srgb, var(--fg) 22%, transparent);
  box-shadow: 0 1px 0 0 color-mix(in srgb, var(--fg) 6%, transparent);
}

/* Specular rim — a 1px gradient border via mask, brightest top-left. */
.glass::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(
    145deg,
    color-mix(in srgb, var(--fg) 42%, transparent) 0%,
    color-mix(in srgb, var(--fg) 6%, transparent) 34%,
    transparent 62%,
    color-mix(in srgb, var(--fg) 14%, transparent) 100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
  z-index: 2;
  transition: opacity 0.4s var(--ease);
}

/* Inner depth: light pooling top-left, shadow settling bottom-right. Also
   carries the cursor-follow glow — a soft accent-coloured light that tracks
   the pointer (--mx/--my, set by motion.js), off-canvas by default so it
   never shows until the pointer is actually over the card. */
.glass::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  pointer-events: none;
  z-index: 1;
  background: radial-gradient(
    260px circle at var(--mx, -9999px) var(--my, -9999px),
    color-mix(in srgb, var(--accent) 38%, transparent),
    transparent 72%
  );
  box-shadow:
    inset 1px 1px 1px -0.5px color-mix(in srgb, var(--fg) 30%, transparent),
    inset -1px -1px 1px -0.5px color-mix(in srgb, var(--bg-deep) 60%, transparent),
    inset 0 0 22px 2px color-mix(in srgb, var(--fg) 5%, transparent);
  transition: box-shadow 0.4s var(--ease);
}

a.glass:hover, .glass.is-hoverable:hover, button.glass:hover {
  background: color-mix(in srgb, var(--card) 74%, transparent);
}
a.glass:hover::before, .glass.is-hoverable:hover::before, button.glass:hover::before { opacity: 1; }

/* Card content rides above both glass layers. Deliberately does NOT set
   `position` — doing so overrode `position:absolute` on .card__go and
   flung every arrow button into the text flow. */
.glass > * { z-index: 3; }
.glass > .card__media { z-index: 0; }
.glass > .card__foot { position: relative; z-index: 4; }
.glass > .card__go { z-index: 5; }

/* The pressed/active state on pills reads as the glass compressing. */
button.glass:active, a.glass:active { transform: translateY(1px) scale(0.994); }

@supports not (backdrop-filter: blur(2px)) {
  .glass { background: var(--card); }
}

/* ============================================================
   Hero portrait — steps out from behind the grid
   ============================================================ */

.bento { position: relative; }

/* Placed INTO the hero's own grid cell rather than absolutely against the
   whole grid — that way it can never drift into the rows below when the
   bento reflows. Anchored to the RIGHT, with text on the left of the card.
   The negative-margin breakout overhangs the card's edge without adding
   height to the row. */
/* Pushed as far into the bottom-right corner as possible — flush, with
   only a small deliberate bleed past the rounded edge (not the large
   breakout from before, just enough to read as stepping past the frame
   rather than being boxed a comfortable distance inside it). */
/* Height is locked to the shared grid cell (align-self:stretch + height:100%)
   instead of being derived from width × the photo's own aspect ratio — that
   old approach let the computed height exceed the card's actual rendered
   height whenever the row got shorter (different viewport, font metrics,
   content length), pushing the head above the card's top edge. Locking the
   box's own height and letting object-fit:cover scale+crop the photo to
   fill it means the top can never exceed the frame, on any breakpoint. */
.hero-portrait {
  grid-column: 1 / span 6;
  grid-row: 1 / span 2;
  align-self: stretch;
  justify-self: end;
  width: var(--portrait-w);
  height: 100%;
  overflow: hidden;
  /* Flush to the card's right edge — a right-side bleed read as "cheap"
     per feedback, so only the bottom keeps its small deliberate overhang. */
  margin: 0 0 -8px 0;
  z-index: 25;
  pointer-events: none;   /* never blocks the cards underneath */
  will-change: transform;
}
.hero-portrait img {
  width: 100%; height: 100%; display: block;
  /* contain, not cover: never crop the cut-out — on a narrow/tall cell
     (mobile, where the card is tall from stacked text) cover would zoom
     into a sliver of the photo. contain just scales it down to whatever
     fits and pins it to the bottom-right corner of the cell. */
  object-fit: contain;
  object-position: 100% 100%;
  /* Grounding shadow so the cut-out doesn't look pasted on. */
  filter:
    drop-shadow(0 24px 26px color-mix(in srgb, var(--bg-deep) 50%, transparent))
    drop-shadow(0 4px 10px color-mix(in srgb, var(--bg-deep) 35%, transparent));
}

/* The whole text block — role, name, tagline, quote — reserves a lane
   clear of the portrait's width, so nothing sits behind or under him
   regardless of how tall the figure renders at a given breakpoint. */
.bento { --portrait-w: clamp(210px, 23vw, 320px); }
.c-hero .hero__meta {
  max-width: calc(100% - var(--portrait-w) - var(--pad) * 0.7 - 18px);
}

@media (max-width: 1024px) {
  /* Full-width single row now (was 6 columns / 2 rows). Both c-hero and
     hero-portrait get the SAME explicit single-row placement, so they
     keep sharing one cell exactly as on desktop — switching the portrait
     to position:absolute here was tried and reverted: its nearest
     positioned ancestor is .bento (the whole grid), not this one card,
     so "bottom" measured from the page's total height, not the card's. */
  .bento { --portrait-w: clamp(150px, 26vw, 220px); }
  .bento > .hero-portrait { grid-column: 1 / -1; grid-row: 1; }
}
/* Phone: copy keeps its own lane on the left, the figure fills the right
   one top to bottom. The portrait is a tall cut-out scaled with
   object-fit:contain, so its rendered height follows its column width —
   a wider lane is what makes it stand full height rather than sitting as a
   small crop in the corner. The copy is tightened to match, so the card
   stays roughly as tall as the figure instead of towering over it. */
@media (max-width: 700px) {
  .bento { --portrait-w: 48%; }
  .c-hero { min-height: 0; }
  .c-hero .hero__meta { max-width: calc(100% - var(--portrait-w) - 10px); }
  .c-hero .hero__name { font-size: clamp(23px, 6.6vw, 30px); }
  .c-hero .hero__tagline { font-size: 9.5px; letter-spacing: 0.08em; }
  .c-hero .hero__quote { font-size: 12.5px; line-height: 1.5; margin: 14px 0 0; }
  .bento > .hero-portrait {
    align-self: stretch;
    width: var(--portrait-w);
    height: 100%;
  }
  /* The looping AR mark shared the top-right corner with the figure; with
     the figure now full height they collided, and the header already
     carries the same mark two centimetres above it. */
  .c-hero .hero__logo { display: none; }
}

/* ============================================================
   Proof shots (Final Cut timelines / client credits)
   ============================================================ */

.vid__meta { padding: 13px var(--pad) 15px; display: flex; flex-direction: column; gap: 11px; }
.vid__meta .vid__cap { padding: 0; }

/* A small square in the corner, not a full-width strip — .vid__meta is a
   column flex container, so without align-self it would stretch to match
   the card's width. */
.proof {
  align-self: flex-start;
  position: relative; display: block;
  width: clamp(88px, 30%, 116px);
  aspect-ratio: 1 / 1;
  border-radius: 10px; overflow: hidden;
  border: 1px solid var(--line-soft);
  background: var(--bg-deep);
  cursor: zoom-in;
  transition: border-color 0.3s var(--ease), transform 0.3s var(--ease);
}
.proof img {
  width: 100%; height: 100%; display: block;
  object-fit: cover; object-position: 50% 74%;
  opacity: 0.68;
  transition: opacity 0.35s var(--ease), transform 0.6s var(--ease);
}
.proof:hover { border-color: var(--accent-mute); }
.proof:hover img { opacity: 1; transform: scale(1.05); }

.proof__hint {
  position: absolute; inset: auto 5px 5px auto;
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--font-ui); font-size: 8px; letter-spacing: 0.1em;
  padding: 4px 7px; border-radius: var(--r-pill);
  background: rgba(20, 22, 16, 0.62); color: #edefdd;
  backdrop-filter: blur(8px);
}
.proof__hint svg { width: 10px; height: 10px; }
.proof__hint span { display: none; } /* icon only at this size — no room for the label */

/* The few videos that came with a real write-up on the old site: timeline
   stays put on the left, the text runs beside it on the right. */
.vid__detail {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}
.vid__detail .proof { flex: none; } /* keep its own fixed square size, not the row's stretch */
.vid__desc {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--fg-dim);
}

@media (max-width: 560px) {
  /* Not enough room for two columns on a narrow card — stack instead. */
  .vid__detail { flex-direction: column; }
}

/* Instant hover preview — sits above the lightbox (z 200) since it can be
   triggered while a category grid is open behind it. Always pointer-events:
   none so the cursor can never "land on" the enlarged image itself; that's
   what makes leaving the small thumbnail reliably close it, even though
   the preview visually floats well outside the thumbnail's box. */
.proof-peek {
  position: fixed; inset: 0; z-index: 250;
  display: flex; align-items: center; justify-content: center;
  padding: 7vh 7vw;
  background: color-mix(in srgb, var(--bg-deep) 58%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  opacity: 0; pointer-events: none;
  transition: opacity 0.2s var(--ease);
}
.proof-peek.is-on { opacity: 1; }
.proof-peek img {
  max-width: min(1100px, 90vw); max-height: 86vh;
  width: auto; height: auto; display: block;
  border-radius: var(--r-card);
  border: 1px solid color-mix(in srgb, #edefdd 32%, transparent);
  box-shadow: 0 34px 80px -20px rgba(0, 0, 0, 0.6);
  object-fit: contain;
  transform: scale(0.96);
  transition: transform 0.25s var(--ease);
}
.proof-peek.is-on img { transform: scale(1); }

@media (hover: none) {
  /* No pointer to hover with on touch — a preview that can only be
     dismissed by "moving the mouse away" would otherwise get stuck open.
     Tap still reaches the full lightbox through the existing click handler. */
  .proof-peek { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .proof-peek, .proof-peek img { transition: none; }
}

/* ============================================================
   Shuffle deck
   ============================================================ */

/* Full width, single column: heading centred on top, the fan spans the
   whole card below it — was a cramped two-column layout before. */
.deck {
  margin-bottom: clamp(40px, 6vw, 76px);
  padding: clamp(28px, 4vw, 48px) clamp(20px, 3.4vw, 40px);
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(20px, 3vw, 34px); text-align: center;
}

.deck__head { max-width: 56ch; }
.deck__title {
  font-family: var(--font-display); font-weight: 900; font-style: italic;
  font-size: clamp(28px, 4vw, 48px); line-height: 1.02;
  letter-spacing: -0.03em; margin: 4px 0 12px;
}
.deck__note { font-size: 14px; color: var(--fg-dim); margin: 0 auto; max-width: 40ch; }

.deck__body { display: flex; flex-direction: column; gap: 26px; min-width: 0; width: 100%; }

.deck__stage {
  position: relative; width: 100%;
  height: clamp(280px, 34vw, 420px);
  touch-action: pan-y;
}

.deck__card {
  position: absolute; left: 50%; top: 50%;
  width: clamp(230px, 27vw, 360px);
  transition: transform 0.55s var(--ease), opacity 0.45s var(--ease);
  will-change: transform;
}
.deck__hit {
  display: block; width: 100%; position: relative;
  aspect-ratio: 16 / 10;
  border-radius: 18px; overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--fg) 16%, transparent);
  background: var(--bg-deep);
  box-shadow: 0 22px 40px -18px color-mix(in srgb, var(--bg-deep) 90%, transparent);
  cursor: pointer;
}
.deck__hit img { width: 100%; height: 100%; object-fit: cover; }
.deck__hit iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Silent looping preview on the front card — pointer-events off so the
   click that opens the full player still lands on the button underneath;
   a cross-origin iframe would otherwise swallow it. */
.deck__preview { pointer-events: none; }

.deck__play {
  position: absolute; inset: 0; display: grid; place-items: center;
  color: #edefdd; opacity: 0; transition: opacity 0.3s var(--ease);
}
.deck__play svg { width: 40px; height: 40px; filter: drop-shadow(0 2px 8px rgba(0,0,0,0.6)); }
.deck__card.is-front .deck__play { opacity: 0.92; }
.deck__card:not(.is-front) .deck__hit { cursor: pointer; }
.deck__card:not(.is-front) .deck__hit::after {
  content: ''; position: absolute; inset: 0;
  background: color-mix(in srgb, var(--bg-deep) 34%, transparent);
}

/* Caption sits BELOW its card, clear of the fan — not overlapping the
   neighbouring tilted cards the way a tight, small layout would. */
.deck__cap {
  display: block; margin-top: 12px; text-align: center;
  font-family: var(--font-ui); font-size: 10.5px; letter-spacing: 0.1em;
  color: var(--fg-dim); opacity: 0;
  transition: opacity 0.35s var(--ease);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.deck__card.is-front .deck__cap { opacity: 1; }

@media (max-width: 620px) {
  .deck__stage { height: 300px; }
  .deck__card { width: 220px; }
}

.deck__foot { display: flex; align-items: center; justify-content: center; gap: 12px; }
.deck__nav {
  width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--line); color: var(--fg-dim);
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.deck__nav:hover { color: var(--fg); border-color: var(--accent-mute); }
.deck__nav svg { width: 16px; height: 16px; }

.deck__all {
  display: inline-flex; align-items: center; gap: 9px;
  font-family: var(--font-ui); font-size: 11px; letter-spacing: 0.11em;
  padding: 12px 22px; border-radius: var(--r-pill);
  border: 1px solid var(--line); color: var(--fg);
  transition: border-color 0.25s var(--ease), transform 0.25s var(--ease);
}
.deck__all:hover { border-color: var(--accent); transform: translateY(-1px); }
.deck__all svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

.profile__handle {
  font-family: var(--font-ui); font-size: 10.5px; letter-spacing: 0.08em;
  color: var(--fg-dim); margin: 5px 0 0;
}
.profile__dot--ig svg { width: 26px; height: 26px; }

.lang-btn .flag { width: 20px; height: 14px; border-radius: 3px; display: block; }
.lang-btn [data-lang-flag] { display: flex; }

/* ============================================================
   Tablet + phone
   ============================================================ */

/* Tablet: two-up grids, roomier tap targets, no hover-only affordances. */
/* Tablet: three across. Fixed column counts rather than auto-fill/minmax —
   a min track width of 300–420px silently collapsed these to one-per-row on
   any portrait tablet, which is the "one giant card per screen" problem. */
@media (max-width: 1024px) {
  :root { --shell: 100%; --gap: 12px; }
  .vid-grid,
  .vid-grid.is-vertical { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .work-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cat-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Phone: two across. One-per-row meant a single work filled the whole
   screen and the page became an endless scroll. */
@media (max-width: 700px) {
  :root { --r-card: 20px; --pad: 16px; }

  .vid-grid,
  .vid-grid.is-vertical { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .work-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .cat { aspect-ratio: 4 / 5; }
  .cat--locked { grid-column: 1 / -1; aspect-ratio: 16 / 11; max-height: none; }

  /* 15vw overflowed the screen on the longest Ukrainian headings
     ("НЕПУБЛІЧНЕ ПОРТФОЛІО"), which the body's overflow-x:clip then cut
     off mid-word rather than scrolling to. */
  .page-head__title { font-size: clamp(30px, 11vw, 54px); }

  /* Touch targets: Apple's 44px minimum. */
  .icon-btn { width: 42px; height: 42px; }
  .nav a { padding: 15px 16px; }

  .proof img { max-height: 88px; }

  .foot__cta { max-width: 100%; }
  .stats { grid-template-columns: 1fr; }
  .stat { flex-direction: row; align-items: baseline; justify-content: flex-start; gap: 14px; text-align: left; padding: 14px 18px; }
  .stat__lbl { line-height: 1.3; }
}

/* Coarse pointers get no hover-reveal — those states never fire on touch,
   so anything hidden behind :hover would be permanently invisible. */
@media (hover: none) {
  .deck__play { opacity: 0.92; }
  .deck__cap { opacity: 1; }
  .proof img { opacity: 0.9; }
  .card__media { opacity: 0.42; }
}

/* Fullscreen video: strip the letterboxing the card would otherwise add. */
.vid__frame:fullscreen,
.vid__frame:-webkit-full-screen { aspect-ratio: auto; width: 100vw; height: 100vh; }
.vid__frame iframe:fullscreen { width: 100vw; height: 100vh; }

/* ---------- About photo: just the photo ----------
   The source PNG already carries its own soft cut-out (transparent edges),
   so the page must not add anything on top of it. The radial mask faded the
   edges a second time and the ::after scrim laid a dark gradient over the
   bottom — together they read as a black vignette that isn't in the file.
   Both are off here. The image also keeps its natural proportions, so the
   whole frame is visible rather than cover-cropped. */

.about-hero {
  position: relative;
  display: block;
  overflow: visible;
  min-height: 0;
  padding: 0;
}
.about-hero__bg { position: static; inset: auto; }
.about-hero__bg img {
  width: 100%;
  height: auto;            /* natural aspect — no crop, no stretch */
  object-fit: fill;
  object-position: center;
  filter: none;            /* exactly the exported grade */
  mask-image: none;
  -webkit-mask-image: none;
}
.about-hero::after { content: none; }   /* no scrim → no dark vignette */

/* Copy stays over the photo as before; a text shadow carries legibility
   instead of a scrim, so the picture itself is never tinted. */
.about-intro {
  position: absolute;
  z-index: 2;
  left: clamp(20px, 5vw, 56px);
  right: clamp(20px, 5vw, 56px);
  bottom: clamp(18px, 4vw, 48px);
  max-width: 640px;
  color: #edefdd;
  text-shadow: 0 1px 16px rgba(0, 0, 0, 0.6), 0 1px 3px rgba(0, 0, 0, 0.5);
}
.about-intro .small { color: rgba(237, 239, 221, 0.78); }
.about-lead { color: rgba(237, 239, 221, 0.88); }

/* Below this width the 16:9 photo is too short to hold the copy, so the
   text drops underneath it — the photo still shows in full. */
@media (max-width: 760px) {
  .about-intro {
    position: static;
    max-width: none;
    margin-top: clamp(22px, 5vw, 36px);
    color: var(--fg);
    text-shadow: none;
  }
  .about-intro .small { color: var(--fg-dim); }
  .about-lead { color: var(--fg-dim); }
  .about-intro > .crumb { border-color: var(--line); color: var(--fg-dim); }
}

/* ---------- Back-to-top ---------- */

.to-top {
  position: fixed;
  right: clamp(14px, 3vw, 28px);
  bottom: clamp(14px, 3vw, 28px);
  z-index: 120;
  width: 46px; height: 46px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--fg); color: var(--bg);
  box-shadow: var(--shadow);
  opacity: 0; transform: translateY(10px) scale(0.9);
  pointer-events: none;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease), background-color 0.3s var(--ease);
}
.to-top.is-on { opacity: 0.94; transform: none; pointer-events: auto; }
.to-top:hover { opacity: 1; transform: translateY(-2px); }
.to-top svg { width: 19px; height: 19px; }

@media (max-width: 620px) {
  .to-top { width: 42px; height: 42px; }
  .to-top svg { width: 17px; height: 17px; }
}
@media (prefers-reduced-motion: reduce) {
  .to-top { transition: opacity 0.2s linear; }
}

/* ============================================================
   Touch layout pass — tablet (≤1024px) and phone (≤700px) only.
   Everything below is inside a max-width query, so the desktop
   layout above is left exactly as it is.
   ============================================================ */

@media (max-width: 1024px) {
  /* --- Header ---------------------------------------------------------
     The language pill and the theme toggle move into the drawer (nav.js
     relocates them at this width). The bar itself keeps only the logo and
     the hamburger, which is what stopped the three controls from piling
     into each other: .lang-btn is an .icon-btn, so the phone rule pinning
     .icon-btn to a 42px square was clipping a pill whose flag + "EN" label
     needs ~62px, and the overflow landed on top of the theme button. */
  .hdr__in { gap: 10px; }
  /* .nav is position:fixed at this width, so it leaves the flex flow and the
     tools cluster collapsed against the logo on the left. Pushing it to the
     right edge puts the menu button under the thumb. */
  .hdr__tools { margin-left: auto; }

  .nav__drawer-tools {
    display: flex; align-items: center; justify-content: flex-end; gap: 10px;
    padding: 4px 16px 12px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--line-soft);
  }
  .nav__drawer-tools .lang-btn { width: auto; min-width: 62px; }

  /* Drawer scrolls on short/landscape screens rather than running off. */
  .nav { max-height: calc(100dvh - var(--hdr-h)); overflow-y: auto; }
  /* Every row ends at the right edge, on the same side as the button that
     opened the drawer — nothing to reach across the screen for. */
  .nav a { justify-content: flex-end; text-align: right; }

  /* --- Section nav pills ---------------------------------------------
     One pill per row ate a third of the first screen on every subpage. */
  .crumbs { gap: 6px; padding-block: 2px 16px; }
  .crumb { padding: 9px 14px; font-size: 10.5px; letter-spacing: 0.06em; }
  .crumb svg { width: 12px; height: 12px; }

  /* --- Design filters -------------------------------------------------
     Kept on one line as a swipeable strip; stacked full-width pills were
     pushing the actual work four screens down. */
  .filters {
    flex-wrap: nowrap; overflow-x: auto; gap: 6px;
    padding-bottom: 16px;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
  }
  .filters::-webkit-scrollbar { display: none; }
  .filter {
    flex: 0 0 auto; scroll-snap-align: start;
    padding: 9px 14px; font-size: 10.5px; letter-spacing: 0.06em;
  }

  /* --- Lightbox -------------------------------------------------------
     The overlay was display:block, so the `margin-block:auto` meant to
     centre the panel resolved to 0 and every image and video opened
     pinned under the status bar — out of thumb reach. Column flex makes
     the auto margins real, and they still collapse to 0 (scrollable from
     the top) when the content is taller than the screen. */
  .lightbox.is-open { display: flex; }
  .lightbox { flex-direction: column; }
  .lightbox__inner { margin-block: auto; }
  .lightbox.is-video .lightbox__stage { width: min(94vw, 128vh); }

  /* Arrows drop below the media instead of floating over it. Overlaid, they
     sat exactly on top of YouTube's own fullscreen and progress controls, so
     a tap meant to enter fullscreen skipped to the next video instead.

     The stage stays in normal flow so its 16/9 aspect-ratio resolves
     against its own final width. Both flex-wrap and grid were tried here
     and both sized the media's row from a width that excluded the buttons'
     tracks and gaps, leaving the video overflowing ~20px down over them.
     The frame instead reserves a strip at the bottom and the two buttons
     are pinned into it, which needs no track sizing at all. */
  .lightbox__frame {
    display: block;
    width: 100%;
    padding-bottom: 62px;
  }
  .lightbox__stage,
  .lightbox.is-video .lightbox__stage { width: 100%; }
  button.lightbox__nav {
    top: auto; bottom: 0;
    /* The base rule centres these with the standalone `translate`
       property, which `transform` does not override — both are cleared. */
    translate: none; transform: none;
    width: 46px; height: 46px;
  }
  button.lightbox__nav.lightbox__nav--prev { left: calc(50% - 53px); right: auto; }
  button.lightbox__nav.lightbox__nav--next { left: calc(50% + 7px); right: auto; }
  button.lightbox__nav:hover { transform: none; }


  /* --- Random-works deck (tablet keeps it, phones hide it below) -------
     Bigger cards and a caption that can actually be read at arm's length,
     always shown rather than waiting for a hover that never comes. */
  .deck__stage { height: 320px; }
  .deck__card { width: 250px; }
  .deck__cap { font-size: 12px; opacity: 1; }
  .deck__title { font-size: clamp(22px, 3.4vw, 30px); }
  .deck__note { font-size: 13px; }
}

@media (max-width: 700px) {
  /* --- Card captions --------------------------------------------------
     Two-up cards leave ~150px per caption; the desktop size wrapped every
     title into a stack of one-word lines. */
  .work__cap, .vid__cap {
    font-size: 9.5px; letter-spacing: 0.05em; line-height: 1.45;
  }
  .work__count { font-size: 9px; padding: 4px 7px; }
  .cat__name { font-size: clamp(13px, 3.6vw, 16px); }
  .cat__watch { font-size: 9px; }
  .cat__count { font-size: 9px; padding: 4px 8px; }
  .locked-note { font-size: 11.5px; }
  .vid__meta { padding: 10px 12px 12px; gap: 8px; }

  /* Description + timeline stack instead of sharing a 150px row. */
  .vid__detail { flex-direction: column; }
  .vid__desc { font-size: 12px; }
  .proof { width: 64px; }

  /* --- Random-works deck ----------------------------------------------
     Hidden on phones: the stacked-card carousel overlaps its own titles at
     this width and there is no room to fan the cards out. The full
     catalogue is one tap away on the design page, and desktop keeps it. */
  section.deck { display: none; }

  /* --- About ----------------------------------------------------------
     Intro lines were centring into a ragged block once the copy dropped
     below the photo; left-aligned reads as one column again. */
  .about-intro { text-align: left; }
  .about-intro .line { justify-content: flex-start; gap: 8px; }
  .about-intro .big { font-size: clamp(24px, 7.6vw, 36px); line-height: 1.05; }
  .about-intro .small { font-size: 12px; }
  .about-lead { font-size: 14px; margin: 16px 0 20px; }

  /* --- Lightbox chrome ------------------------------------------------ */
  .lightbox { padding: 12px; }
  .lightbox.is-image .lightbox__stage img { max-height: 62vh; }
  .lightbox__desc { font-size: 13.5px; padding: 14px 16px; }
  .lightbox__thumbs { gap: 6px; }

  /* --- Offer card ------------------------------------------------------ */
  .offer__list li { font-size: 12px; gap: 8px; }
  .offer__icon { width: 26px; height: 26px; border-radius: 8px; }
  .offer__icon svg { width: 13px; height: 13px; }
}

/* Only genuinely tiny screens (SE-class, ≤340px) fall back to one column —
   375px is a mainstream iPhone width and keeps the two-up grid. */
@media (max-width: 340px) {
  .vid-grid { grid-template-columns: minmax(0, 1fr); }
}
