/* Overlays: "All Projects" + menu. Everything is prefixed ov-; z-index 60+ (the core UI sits at 10-20).
   Dim by design: large type <= .62 white, labels .42-.55, body .5-.6. Accent red only as a dot/rim/glint. */

.ov-root {
  --ov-bg: #18181a; /* the core's CSS grain (z 100, over the overlay too) lifts this to about #1d1d1f */
  --ov-lg: rgba(255,255,255,.6);
  --ov-lg-dim: rgba(255,255,255,.3);
  --ov-label: rgba(255,255,255,.46);
  --ov-body: rgba(255,255,255,.54);
  --ov-line: rgba(255,255,255,.1);
  --ov-red: #dc2626;
  --ov-ease: cubic-bezier(.2,.7,.1,1);
  --ov-ease-io: cubic-bezier(.45,0,.2,1);
  --ov-pad: clamp(22px, 11.8vw, 170px);
  --ov-barh: 64px;
  --ov-sl: env(safe-area-inset-left, 0px);
  --ov-sr: env(safe-area-inset-right, 0px);
  --ov-st: env(safe-area-inset-top, 0px);
  --ov-sb: env(safe-area-inset-bottom, 0px);
  position: fixed; inset: 0;
  z-index: 60;
  pointer-events: none;
  font-family: Geist, "Segoe UI Variable", "Segoe UI Variable Text", system-ui, sans-serif;
  color: var(--ov-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
  user-select: none; -webkit-user-select: none;
  -webkit-touch-callout: none;
}
:where(.ov-root) *, :where(.ov-root) *::before, :where(.ov-root) *::after { box-sizing: border-box; }
:where(.ov-root) button { font: inherit; color: inherit; background: none; border: 0; padding: 0; margin: 0; cursor: pointer; }
:where(.ov-root) a { color: inherit; text-decoration: none; -webkit-user-drag: none; }
:where(.ov-root) img { -webkit-user-drag: none; user-select: none; }
:where(.ov-root) :focus { outline: none; }
:where(.ov-root) :focus-visible { outline: 1px solid rgba(255,255,255,.5); outline-offset: 4px; }

.ov-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ---------------------------------------------------------------- panel shell */
.ov-panel {
  position: absolute; inset: 0;
  z-index: 61;
  background: var(--ov-bg);
  /* clip, not hidden: a hidden box is still a scroll container, and the grain/facets overhang it, so any
     scroll-into-view could shift the whole panel (bar included) */
  overflow: hidden;
  overflow: clip;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .42s var(--ov-ease-io), visibility 0s linear .42s;
  contain: strict;
}
.ov-panel.ov-in {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .6s var(--ov-ease-io), visibility 0s;
}
.ov-panel.ov-in.ov-front { z-index: 62; }
.ov-warm .ov-panel:not(.ov-in) { visibility: visible; opacity: .002; transition: none; }
.ov-warm .ov-panel:not(.ov-in) .ov-rise, .ov-warm .ov-panel:not(.ov-in) .ov-card { opacity: 1; transform: none; transition: none; }

/* faceted backdrop: two SVG layers of big flat planes at 1.5-4.5% white, moved by transform only */
.ov-facets {
  position: absolute; inset: -60px;
  width: calc(100% + 120px); height: calc(100% + 120px);
  pointer-events: none;
  will-change: transform;
}

.ov-grain {
  display: none; /* inside the core the page grain already covers the overlay: one grain, not two */
  position: absolute; inset: -128px;
  width: calc(100% + 256px); height: calc(100% + 256px);
  pointer-events: none;
  opacity: .045;
  will-change: transform;
  z-index: 5;
}

/* ---------------------------------------------------------------- top bar */
.ov-bar {
  position: absolute; left: 0; right: 0; top: 0;
  z-index: 4;
  height: calc(var(--ov-barh) + var(--ov-st));
  padding: var(--ov-st) calc(16px + var(--ov-sr)) 0 calc(16px + var(--ov-sl));
  display: flex; align-items: center; justify-content: space-between;
  /* the bar swallows its own taps: a near miss beside "Close" must not reach a card scrolled under the fade */
  pointer-events: auto;
}
.ov-bar > * { pointer-events: auto; }
/* content scrolls away under a soft charcoal fade instead of colliding with the bar (a static gradient: cheap) */
.ov-bar::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0;
  height: calc(100% + 34px);
  background: linear-gradient(to bottom, var(--ov-bg) 0%, var(--ov-bg) 50%, rgba(24,24,26,.8) 72%, rgba(24,24,26,0) 100%);
  pointer-events: none;
  z-index: -1;
}
.ov-mark {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: 50%;
  transition: transform .5s var(--ov-ease);
}
/* (round 14, L1) the site's R in its ring, drawn as the page's own top-left mark (style.css .mark-r) */
.ov-r {
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.2);
  font-size: 13px; font-weight: 500; line-height: 1;
  color: rgba(255,255,255,.55);
  transition: border-color .4s ease, color .4s ease;
}
@media (hover: hover) {
  .ov-mark:hover .ov-r { border-color: rgba(220,38,38,.75); color: rgba(255,255,255,.72); }
  .ov-mark:hover { transform: translateY(-1px); }
}
.ov-bar-r { display: flex; align-items: center; }
.ov-closetxt {
  min-height: 44px; padding: 0 14px;
  font-size: 13px; font-weight: 500; letter-spacing: .01em;
  color: rgba(255,255,255,.52);
  transition: color .3s;
}
.ov-sep { width: 1px; height: 20px; background: var(--ov-line); margin: 0 4px; }
.ov-iconbtn {
  width: 44px; height: 44px;
  display: grid; place-items: center;
}
.ov-iconbtn svg { width: 18px; height: 18px; overflow: visible; }
.ov-iconbtn line { stroke: rgba(255,255,255,.55); stroke-width: 1.3; stroke-linecap: round; transition: stroke .3s, transform .5s var(--ov-ease); transform-origin: 9px 9px; }
@media (hover: hover) {
  .ov-closetxt:hover { color: rgba(255,255,255,.68); }
  .ov-iconbtn:hover line { stroke: rgba(255,255,255,.68); }
  .ov-burger:hover line:nth-child(1) { transform: translateX(-2px); }
  .ov-burger:hover line:nth-child(3) { transform: translateX(2px); }
  .ov-x:hover line { transform: rotate(90deg); }
}

/* ---------------------------------------------------------------- scroll body */
.ov-scroll {
  position: absolute; inset: 0;
  z-index: 2;
  overflow-x: hidden; overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  scrollbar-width: none;
}
.ov-scroll::-webkit-scrollbar { display: none; }

/* rise-in: anything with --d (ms) slides up as the panel opens */
.ov-rise {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .3s ease, transform .3s ease;
}
.ov-in .ov-rise {
  opacity: 1;
  transform: none;
  transition: opacity .8s ease calc(var(--d, 0) * 1ms), transform 1s var(--ov-ease) calc(var(--d, 0) * 1ms);
}

/* ---------------------------------------------------------------- projects */
.ov-pinner {
  padding: calc(clamp(96px, 20vh, 196px) + var(--ov-st)) calc(var(--ov-pad) + var(--ov-sr)) calc(96px + var(--ov-sb)) calc(var(--ov-pad) + var(--ov-sl));
  max-width: calc(1100px + 2 * var(--ov-pad));
  margin: 0 auto;
  min-height: 100%;
}

.ov-filter { position: relative; z-index: 5; margin-bottom: clamp(36px, 7vh, 70px); }
.ov-label {
  display: block;
  font-size: 11px; font-weight: 600; /* (20 Oct: 11 px everywhere, was 10 on a desktop) */
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--ov-label);
}
/* (round 14, L1) the kicker: the page's red dot, then the count */
.ov-kick::before { content: ""; display: inline-block; width: 5px; height: 5px; margin: 0 .9em .1em 0; border-radius: 50%; background: var(--ov-red); vertical-align: middle; }
.ov-ftitle {
  display: inline-flex; align-items: baseline; gap: .28em;
  min-height: 44px;
  margin-top: 6px;
  font-size: clamp(38px, 4.1vw, 60px);
  font-weight: 200;
  letter-spacing: -.02em;
  line-height: 1.05;
  color: var(--ov-lg);
  text-align: left;
  transition: color .35s;
}
.ov-ftext { display: inline-block; transition: opacity .2s ease, transform .35s var(--ov-ease); }
.ov-ftext.ov-swap { opacity: 0; transform: translateY(8px); }
.ov-chev { width: .3em; height: .3em; flex: none; transform: translateY(-.05em); transition: transform .5s var(--ov-ease); }
.ov-chev path { fill: none; stroke: currentColor; stroke-width: 1.6; }
.ov-ftitle[aria-expanded="true"] .ov-chev { transform: translateY(-.05em) rotate(180deg); }
@media (hover: hover) { .ov-ftitle:hover { color: rgba(255,255,255,.66); } }

.ov-flist {
  position: absolute; left: -14px; top: 100%;
  margin-top: 10px;
  min-width: 260px;
  padding: 10px 0;
  background: #19191b;
  border: 1px solid var(--ov-line);
  border-radius: 2px;
  box-shadow: 0 24px 60px rgba(0,0,0,.45);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-8px);
  transition: opacity .3s ease, transform .4s var(--ov-ease), visibility 0s linear .3s;
}
.ov-flist.ov-open {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .35s ease, transform .5s var(--ov-ease), visibility 0s;
}
.ov-fopt {
  position: relative;
  display: flex; align-items: center;
  width: 100%;
  min-height: 46px;
  padding: 0 22px 0 30px;
  font-size: 19px; font-weight: 300; letter-spacing: -.005em;
  color: rgba(255,255,255,.46);
  cursor: pointer;
  opacity: 0;
  transform: translateX(-6px);
  transition: color .25s, opacity .25s ease, transform .3s var(--ov-ease);
}
.ov-flist.ov-open .ov-fopt {
  opacity: 1; transform: none;
  transition: color .25s, opacity .45s ease calc(var(--j) * 40ms + 60ms), transform .55s var(--ov-ease) calc(var(--j) * 40ms + 60ms);
}
.ov-fopt::before {
  content: ""; position: absolute; left: 14px; top: 50%;
  width: 5px; height: 5px; margin-top: -2.5px; border-radius: 50%;
  background: var(--ov-red);
  opacity: 0; transform: scale(.4);
  transition: opacity .3s, transform .4s var(--ov-ease);
}
.ov-fopt[aria-selected="true"] { color: rgba(255,255,255,.62); }
.ov-fopt[aria-selected="true"]::before { opacity: .9; transform: none; }
.ov-fopt:focus-visible { outline: none; color: rgba(255,255,255,.66); background: rgba(255,255,255,.035); }
@media (hover: hover) { .ov-fopt:hover { color: rgba(255,255,255,.66); background: rgba(255,255,255,.03); } }
.ov-fopt:active { background: rgba(255,255,255,.05); }

/* grid */
.ov-grid {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: clamp(28px, 8vw, 116px);
  row-gap: clamp(48px, 8vh, 84px);
  align-items: start;
}
.ov-card {
  position: relative;
  display: block;
  text-align: center;
  opacity: 0;
  transform: translateY(30px);
  transition: opacity .25s ease, transform .3s ease;
  cursor: default;
}
a.ov-card { cursor: pointer; }
.ov-in .ov-card {
  opacity: 1; transform: none;
  transition: opacity .8s ease calc(var(--i) * 60ms + 260ms), transform 1s var(--ov-ease) calc(var(--i) * 60ms + 260ms);
}
.ov-card.ov-gone { display: none; }

.ov-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  overflow: clip;
  background: #111113;
  border-radius: 2px;
  isolation: isolate;
  transform: translateZ(0);
}
.ov-zoom { position: absolute; inset: 0; isolation: isolate; }
.ov-ch { position: absolute; inset: 0; }
.ov-ch img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: var(--pos, 50% 50%);
  display: block;
}
/* touch / no-hover: one plain image (cheap); the red/cyan pair only exists where hover does */
.ov-ch-c { display: none; }
.ov-shade {
  position: absolute; inset: 0;
  /* per-project dimming (--shade set from JS): a white sky must not read as bright */
  background: rgba(17,17,19,var(--shade, .5));
  transition: background .9s ease;
}
.ov-glint {
  position: absolute; inset: -20% auto -20% 0;
  width: 38%;
  background: linear-gradient(100deg, transparent 0%, rgba(255,255,255,.07) 45%, rgba(255,255,255,.12) 50%, rgba(255,255,255,.07) 55%, transparent 100%);
  translate: -140% 0;
  pointer-events: none;
}
.ov-desc {
  position: absolute; left: 0; right: 0; bottom: 0;
  z-index: 3; /* above the picture layers (the placeholder, the live preview and the split copies) */
  margin: 0;
  padding: 40px 22px 18px;
  font-size: 14px; line-height: 1.5; font-weight: 400;
  text-align: left;
  color: rgba(255,255,255,.6);
  background: linear-gradient(to top, rgba(14,14,16,.94), rgba(14,14,16,.78) 55%, transparent);
  opacity: 0;
  translate: 0 10px;
  transition: opacity .5s ease, translate .7s var(--ov-ease);
}
.ov-go {
  position: absolute; right: 14px; top: 12px; z-index: 3;
  font-size: 11px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; /* (20 Oct: was 10) */
  color: rgba(255,255,255,.5);
  opacity: 0;
  transition: opacity .5s ease;
}
.ov-go i { font-style: normal; display: inline-block; margin-left: 4px; color: var(--ov-red); }

.ov-ctitle {
  position: relative;
  display: inline-block;
  margin: 26px 0 0;
  font-size: 11px; font-weight: 600;
  letter-spacing: .2em; text-transform: uppercase;
  color: rgba(255,255,255,.55);
  transition: color .4s, text-shadow .4s;
}
.ov-ctitle::before {
  content: ""; position: absolute; left: -14px; top: 50%;
  width: 4px; height: 4px; margin-top: -2px; border-radius: 50%;
  background: var(--ov-red); opacity: 0; transform: scale(.3);
  transition: opacity .4s, transform .5s var(--ov-ease);
}
.ov-ccat {
  margin: 9px 0 0;
  font-size: 13px; font-weight: 300; letter-spacing: .01em;
  color: rgba(255,255,255,.42);
}
.ov-cdesc { display: none; }

@media (hover: hover) and (pointer: fine) {
  .ov-zoom { scale: 1; transition: scale 1.8s var(--ov-ease), filter .25s ease; }
  /* while the description shows, the picture under it (placeholder or live preview) steps down, so a bright line in a
     preview never runs through the text */
  .ov-card:hover .ov-zoom, .ov-card:focus-visible .ov-zoom { scale: 1.065; filter: brightness(.6); }
  /* red/cyan split: the two channel copies and their blend layers exist only on the active card (.ov-split, set
     from JS), so idle cards are a single plain image. Aligned, red-channel + cyan-channel screen back to the image. */
  .ov-ch { translate: 0 0; transition: translate .8s var(--ov-ease); }
  .ov-split .ov-ch-r, .ov-split .ov-ch-c { isolation: isolate; }
  .ov-split .ov-ch-r::after, .ov-split .ov-ch-c::after { content: ""; position: absolute; inset: 0; mix-blend-mode: multiply; }
  .ov-split .ov-ch-r::after { background: #f00; }
  .ov-split .ov-ch-c { display: block; mix-blend-mode: screen; }
  .ov-split .ov-ch-c::after { background: #0ff; }
  .ov-split-on .ov-ch-r { translate: -1.5px 0; }
  .ov-split-on .ov-ch-c { translate: 1.5px 0; }
  .ov-card:hover .ov-shade, .ov-card:focus-visible .ov-shade { background: rgba(17,17,19,calc(var(--shade, .5) - .2)); }
  .ov-card:hover .ov-glint, .ov-card:focus-visible .ov-glint { translate: 360% 0; transition: translate 1.3s var(--ov-ease-io) .05s; }
  .ov-card:hover .ov-desc, .ov-card:focus-visible .ov-desc { opacity: 1; translate: 0 0; transition-delay: .18s; }
  .ov-card:hover .ov-go, .ov-card:focus-visible .ov-go { opacity: 1; transition-delay: .25s; }
  .ov-card:hover .ov-ctitle, .ov-card:focus-visible .ov-ctitle { color: rgba(255,255,255,.62); text-shadow: -1px 0 rgba(220,38,38,.35), 1px 0 rgba(0,190,210,.3); }
  .ov-card:hover .ov-ctitle::before, .ov-card:focus-visible .ov-ctitle::before { opacity: .9; transform: none; }
  .ov-card:focus-visible { outline: none; }
  .ov-card:focus-visible .ov-thumb { outline: 1px solid rgba(255,255,255,.4); outline-offset: 6px; }
}
/* touch: a gentle press state instead of hover, and the description shown under the card */
@media (hover: none), (pointer: coarse) {
  .ov-desc, .ov-go { display: none; }
  .ov-cdesc {
    display: block;
    margin: 10px auto 0;
    max-width: 34em;
    font-size: 14px; line-height: 1.5; font-weight: 400;
    color: rgba(255,255,255,.5);
  }
  .ov-thumb { transition: transform .5s var(--ov-ease); }
  .ov-card:active .ov-thumb { transform: scale(.985); }
  .ov-card:active .ov-shade { background: rgba(17,17,19,calc(var(--shade, .5) - .2)); }
  .ov-card .ov-ext { color: var(--ov-red); margin-left: 6px; letter-spacing: 0; }
}
/* (round 44) a mouse's press too: the card gives a hair under it at once and comes back with a little give as it opens */
@media (hover: hover) and (pointer: fine) {
  .ov-thumb { transition: transform .45s cubic-bezier(.3, 1.45, .5, 1); }
  .ov-card:active .ov-thumb { transform: scale(.982); transition-duration: .08s; transition-timing-function: ease-out; }
}
.ov-ext { display: none; }
@media (hover: none), (pointer: coarse) { .ov-ext { display: inline; } }

/* paging arrows */
.ov-page {
  position: absolute; top: 50%;
  z-index: 4;
  width: 56px; height: 132px; margin-top: -66px;
  display: grid; place-items: center;
  opacity: 0; visibility: hidden;
  transition: opacity .5s ease, visibility 0s linear .5s, transform .6s var(--ov-ease);
}
.ov-page.ov-show { opacity: 1; visibility: visible; transition: opacity .6s ease, visibility 0s, transform .6s var(--ov-ease); }
.ov-up { left: calc(clamp(10px, 4.2vw, 58px) + var(--ov-sl)); }
.ov-down { right: calc(clamp(10px, 4.2vw, 58px) + var(--ov-sr)); }
.ov-page svg { width: 18px; height: 96px; overflow: visible; }
.ov-page path { fill: none; stroke: rgba(255,255,255,.5); stroke-width: 1.2; stroke-linecap: round; stroke-linejoin: round; transition: stroke .3s; }
@media (hover: hover) {
  .ov-page:hover path { stroke: rgba(255,255,255,.7); }
  .ov-up:hover { transform: translateY(-6px); }
  .ov-down:hover { transform: translateY(6px); }
}

/* (core round 2, fixer) the peek band, with a mouse on a wide screen: the list's bottom 48 px page down like the arrow, and the
   cards fade out over the bottom 56 px of the panel, so a card cut by the bottom edge is a sliver fading under the band, never a
   161 x 27 px target of its own (roman.wtf's at 1440 x 900). Hidden once the list is at its end (the inner padding below the
   last card, 96 px, is more than the fade) */
.ov-peek { display: none; }
@media (hover: hover) and (pointer: fine) and (min-width: 761px) {
  .ov-peek {
    display: block; position: absolute; left: 0; right: 0; bottom: 0; height: 48px; z-index: 3;
    padding: 0; border: 0; background: none; cursor: pointer;
    visibility: hidden; transition: visibility 0s linear .5s;
  }
  .ov-peek.ov-show { visibility: visible; transition: visibility 0s; }
  .ov-projects .ov-scroll {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 64px), transparent calc(100% - 8px));
    mask-image: linear-gradient(to bottom, #000 calc(100% - 64px), transparent calc(100% - 8px));
  }
}

/* ---------------------------------------------------------------- menu */
.ov-minner {
  min-height: 100%;
  padding: calc(104px + var(--ov-st)) calc(var(--ov-pad) + var(--ov-sr)) calc(36px + var(--ov-sb)) calc(var(--ov-pad) + var(--ov-sl));
  display: flex; flex-direction: column; justify-content: center;
  gap: clamp(36px, 7vh, 80px);
  max-width: calc(1100px + 2 * var(--ov-pad));
  margin: 0 auto;
}
.ov-mlist { list-style: none; margin: 12px 0 0; padding: 0; counter-reset: none; }
.ov-mitem {
  position: relative;
  display: flex; align-items: baseline; gap: 22px;
  min-height: 48px;
  padding: 2px 0;
  font-size: clamp(40px, min(6.2vw, 10.5vh), 88px);
  font-weight: 200;
  letter-spacing: -.025em;
  line-height: 1.12;
  color: rgba(255,255,255,.52);
  text-align: left;
  transition: color .45s, transform .7s var(--ov-ease), opacity .45s;
}
.ov-mnum {
  flex: none;
  width: 2.2em;
  font-size: 11px; font-weight: 600; letter-spacing: .2em; /* (20 Oct: was 10) */
  color: rgba(255,255,255,.34);
  transform: translateY(-.35em);
  font-variant-numeric: tabular-nums;
  transition: color .45s;
}
.ov-mtxt { position: relative; }
.ov-mtxt::before {
  content: ""; position: absolute; left: -.42em; top: 54%;
  width: 6px; height: 6px; margin-top: -3px; border-radius: 50%;
  background: var(--ov-red); opacity: 0; transform: scale(.3);
  transition: opacity .4s, transform .6s var(--ov-ease);
}
.ov-marr { font-size: .34em; margin-left: .3em; color: rgba(255,255,255,.34); font-weight: 300; transform: translateY(-.9em); display: inline-block; }
@media (hover: hover) {
  .ov-mlist:hover .ov-mitem { color: rgba(255,255,255,.26); }
  .ov-mlist .ov-mitem:hover { color: rgba(255,255,255,.62); transform: translateX(14px); }
  .ov-mitem:hover .ov-mnum { color: rgba(255,255,255,.55); }
  .ov-mitem:hover .ov-mtxt::before { opacity: .9; transform: none; }
}
.ov-mitem:focus-visible { outline: none; color: rgba(255,255,255,.62); }
.ov-mitem:focus-visible .ov-mtxt::before { opacity: .9; transform: none; }
/* (round 13, flow-05: the keyboard's focus was only the red dot) it looks as the mouse's hover does: the item steps in, its number
   lights, the other two dim */
.ov-mlist .ov-mitem:focus-visible { transform: translateX(14px); }
.ov-mitem:focus-visible .ov-mnum { color: rgba(255,255,255,.55); }
.ov-mlist:has(.ov-mitem:focus-visible) .ov-mitem:not(:focus-visible) { color: rgba(255,255,255,.26); }
.ov-mitem:active { color: rgba(255,255,255,.62); }
/* (round 44) a press is answered at once: the row gives under it (a quick press-in, its dot lit) and springs back as it is let go */
.ov-mlist .ov-mitem:active { transform: translateX(8px) scale(.985); transition: color .1s, transform .09s ease-out, opacity .45s; }
.ov-mitem:active .ov-mtxt::before { opacity: .9; transform: none; transition-duration: .1s; }
@media (hover: hover) { .ov-mlist .ov-mitem:hover:active { transform: translateX(12px) scale(.985); } }
.ov-mlist .ov-mitem:not(:active) { transition: color .45s, transform .7s cubic-bezier(.3, 1.25, .5, 1), opacity .45s; }
/* (round 20, the review's flow-04: on a 1100 px row the step and the dot were easy to miss) the keyboard's focus also draws the
   hairline the other controls get, under the words only */
.ov-mtxt::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: .02em; height: 1px;
  background: rgba(255,255,255,.6); transform: scaleX(0); transform-origin: left center;
  transition: transform .55s var(--ov-ease);
}
.ov-mitem:focus-visible .ov-mtxt::after { transform: none; }
@media (prefers-reduced-motion: reduce) { .ov-mtxt::after { transition: none; } }
/* (round 25, the review's flow-03: in the calm version the menu's rows still rose 22 px and the cards 30 px, staggered over 0.4-1.5 s) there
   they only fade in, with no stagger longer than the fade: nothing slides (overlays.js sets .ov-calm as the core reads the setting) */
.ov-panel.ov-calm .ov-rise, .ov-panel.ov-calm .ov-card, .ov-panel.ov-calm .ov-ftext, .ov-panel.ov-calm .ov-flist, .ov-panel.ov-calm .ov-fopt { transform: none !important; }
.ov-panel.ov-calm.ov-in .ov-rise, .ov-panel.ov-calm.ov-in .ov-card { transition: opacity .45s ease !important; }
.ov-panel.ov-calm .ov-flist.ov-open .ov-fopt { transition: color .25s, opacity .3s ease !important; }

.ov-mfoot {
  display: flex; flex-wrap: wrap; gap: 28px 72px;
  padding-top: 26px;
  border-top: 1px solid var(--ov-line);
}
.ov-mgroup { display: flex; flex-direction: column; gap: 4px; }
.ov-mlinks { display: flex; flex-wrap: wrap; margin: 4px 0 0 -12px; }
.ov-mlink {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 44px; min-width: 44px;
  padding: 0 12px;
  font-size: 17px; font-weight: 300;
  color: rgba(255,255,255,.55);
  transition: color .3s;
}
.ov-mlink small { font-size: 13px; color: rgba(255,255,255,.38); letter-spacing: .01em; }
.ov-mlt { position: relative; }
.ov-mlt::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px;
  background: rgba(255,255,255,.35);
  transform: scaleX(0); transform-origin: 100% 50%;
  transition: transform .5s var(--ov-ease);
}
@media (hover: hover) {
  .ov-mlink:hover { color: rgba(255,255,255,.66); }
  .ov-mlink:hover .ov-mlt::after { transform: scaleX(1); transform-origin: 0 50%; }
}
.ov-mlink:active { color: rgba(255,255,255,.66); }
/* (28 Oct) the language group: the current language reads brighter, underlined (the hover's line, kept on) */
.ov-mlang .ov-mlink { font: inherit; font-size: 17px; font-weight: 300; background: none; border: 0; cursor: pointer; }
.ov-mlang .ov-mlink.cur { color: rgba(255,255,255,.86); cursor: default; }
.ov-mlang .ov-mlink.cur .ov-mlt::after { transform: scaleX(1); background: rgba(255,255,255,.5); }
/* (round 15) the Sound row: as the language's; its twin in the menu's bar shows on a phone only (overlays.css, the phone bar) */
.ov-mbarsnd { display: none; }
.ov-mbarsnd .ov-mlink { font: inherit; font-size: 15px; font-weight: 300; background: none; border: 0; cursor: pointer; padding: 0 10px; }
.ov-mbarsnd .ov-mlink.cur { color: rgba(255,255,255,.86); cursor: default; }
.ov-mbarsnd .ov-mlink.cur .ov-mlt::after { transform: scaleX(1); background: rgba(255,255,255,.5); }
.ov-mfact {
  margin: 0;
  font-size: 11px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; /* (20 Oct: was 10) */
  color: rgba(255,255,255,.5); /* about 5:1 on the panel (.36 was 3.3:1, under AA for small text) */
  line-height: 1.8;
}
.ov-mfact i { font-style: normal; color: var(--ov-red); opacity: .85; }
.ov-mfact-short { display: none; }

/* ---------------------------------------------------------------- small screens */
@media (max-width: 760px) {
  .ov-root { --ov-pad: 22px; --ov-barh: 56px; }
  .ov-bar { padding-left: calc(6px + var(--ov-sl)); padding-right: calc(6px + var(--ov-sr)); }
  .ov-closetxt { padding: 0 10px; font-size: 12.5px; }
  .ov-pinner { padding-top: calc(104px + var(--ov-st)); padding-bottom: calc(64px + var(--ov-sb)); }
  .ov-ftitle { font-size: clamp(34px, 10vw, 44px); }
  .ov-flist { left: -10px; right: -10px; min-width: 0; }
  .ov-fopt { font-size: 18px; min-height: 48px; }
  .ov-grid { grid-template-columns: 1fr; row-gap: 52px; }
  .ov-ctitle { margin-top: 20px; }
  .ov-page { display: none; }
  .ov-minner { justify-content: flex-start; padding-top: calc(112px + var(--ov-st)); gap: 40px; }
  .ov-mitem { font-size: clamp(36px, 10.6vw, 46px); gap: 14px; min-height: 52px; }
  .ov-mnum { width: 2.4em; }
  .ov-mfoot { gap: 22px 40px; }
  .ov-mfact { margin-top: auto; }
}
/* phones: no small caps under 11 px: the menu's blurb, its section labels and numbers, the filter label */
@media (max-width: 640px), (max-height: 500px) {
  .ov-mfact { font-size: 11px; }
  .ov-label { font-size: 11px; }
  .ov-mnum { font-size: 11px; color: rgba(255,255,255,.45); }
}
@media (max-width: 380px) {
  .ov-mitem { font-size: 34px; }
  .ov-mlink { padding: 0 10px; }
  .ov-mlinks { margin-left: -10px; }
}
/* (fix round) a portrait phone's menu fits its screen with the notch and the home bar (at 390 x 844 Elsewhere sat under the
   home bar with 10 px of it showing; at 360 x 740 17 px of the email showed): less air above, no "Menu" label, the big rows
   tighter (still 44 px targets), Contact and Elsewhere in one row (the address is the email link's text there), and the
   bottom padded by the home bar + 24 px */
@media (max-width: 760px) and (orientation: portrait) {
  .ov-minner { padding-top: calc(78px + var(--ov-st)); padding-bottom: calc(24px + var(--ov-sb)); gap: 22px; }
  .ov-mnav > .ov-label { display: none; }
  .ov-mlist { margin-top: 0; }
  .ov-mitem { font-size: clamp(32px, 9.2vw, 38px); min-height: 44px; line-height: 1.04; padding: 0; }
  .ov-mchaps-li { margin: 0 0 4px; }
  .ov-mfoot { flex-wrap: nowrap; gap: 0 22px; padding-top: 14px; }
  .ov-mgroup:first-child .ov-mlt { display: none; }
  .ov-mgroup:first-child .ov-mlink small { font-size: 15px; color: rgba(255,255,255,.55); letter-spacing: 0; }
  /* (28 Oct) the language takes its own row under Contact and Elsewhere, still above the fact line: a grid, so Contact and
     Elsewhere always share the first row (under ~340 px wide they had wrapped onto two), and the label beside the three
     names where they fit (on a 360 px phone in English the names wrap under it) */
  .ov-mfoot { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: 22px; row-gap: 0; }
  .ov-mlang { grid-column: 1 / -1; margin-top: 4px; flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: 16px; row-gap: 0; }
  .ov-mlang .ov-mlinks { margin-top: 0; }
}
@media (max-width: 760px) and (max-height: 760px) and (orientation: portrait) {
  .ov-minner { padding-top: calc(68px + var(--ov-st)); }
}
/* (28 Oct) with the Language row in: a little less air under 790 px of height (360 x 740 ran 26 px over in one column) */
@media (max-width: 760px) and (max-height: 790px) and (orientation: portrait) {
  .ov-minner { gap: 14px; padding-bottom: calc(16px + var(--ov-sb)); }
}
/* short landscape (phones on their side) */
@media (max-height: 500px) and (orientation: landscape) {
  .ov-root { --ov-barh: 52px; --ov-mpr: max(var(--ov-pad), calc(22px + var(--ov-sr))); }
  .ov-flist { display: grid; grid-template-columns: 1fr 1fr; min-width: 400px; padding: 6px 0; }
  .ov-fopt { min-height: 44px; font-size: 17px; }
  .ov-pinner { padding-top: calc(78px + var(--ov-st)); padding-bottom: calc(48px + var(--ov-sb)); }
  .ov-ftitle { font-size: 34px; }
  .ov-filter { margin-bottom: 30px; }
  .ov-grid { row-gap: 40px; }
  .ov-page { height: 104px; margin-top: -52px; }
  .ov-page svg { height: 72px; }
  .ov-minner {
    display: grid;
    grid-template-columns: 1.3fr 1fr;
    align-items: center;
    column-gap: 40px; row-gap: 16px;
    padding-top: calc(64px + var(--ov-st)); padding-bottom: calc(20px + var(--ov-sb));
    /* (round 32: with a notch's 59 px on each side the padding was 99 + 59 px, the right column 172 px wide, and "Русский" wrapped
       onto a row of its own) the inset is the margin there: the padding is the larger of the two, never their sum */
    padding-left: max(var(--ov-pad), calc(22px + var(--ov-sl))); padding-right: var(--ov-mpr);
  }
  .ov-mnav { grid-row: 1 / span 2; }
  .ov-mitem { font-size: 34px; min-height: 46px; }
  .ov-mfoot { flex-direction: column; gap: 6px; border-top: 0; border-left: 1px solid var(--ov-line); padding: 0 0 0 26px; }
  .ov-mfact { grid-column: 2; }
  /* the cards scroll under the bar here (a card's text ran under "Close All Projects", 84 x 17 px): All Projects' scroll body
     is masked out under the bar and fades back in over the 20 px below it (the menu's own scroll is not: its first item
     starts right under the bar) */
  .ov-projects .ov-scroll {
    -webkit-mask-image: linear-gradient(to bottom, transparent calc(var(--ov-barh) + var(--ov-st)), #000 calc(var(--ov-barh) + var(--ov-st) + 20px));
    mask-image: linear-gradient(to bottom, transparent calc(var(--ov-barh) + var(--ov-st)), #000 calc(var(--ov-barh) + var(--ov-st) + 20px));
  }
}

/* polish: per-option project count */
.ov-fcount { margin-left: .7em; font-size: .62em; letter-spacing: .12em; vertical-align: super; opacity: .55; }

/* ---------------------------------------------------------------- v3: project cards dive into their scene */
a.ov-card.ov-dive, a.ov-card.ov-here { cursor: pointer; -webkit-user-drag: none; }
/* the live preview (rendered by the core from the scene itself) fades in over the placeholder image, which stays as the fallback */
.ov-ch img.ov-live { opacity: 0; transition: opacity .9s ease; }
.ov-live-on .ov-ch img.ov-live { opacity: 1; }
.ov-live-on .ov-ch img.ov-live { object-position: 50% 50%; }
/* the live picture answers a hover / keyboard focus (the picture itself, no live scene) */
.ov-ch img.ov-live { transition: opacity .9s ease, filter .4s ease, transform .4s ease; transform-origin: 50% 50%; }
.ov-live-on.ov-thumb .ov-ch img.ov-live { will-change: transform; }
.ov-card:focus-visible .ov-live-on .ov-ch img.ov-live { transform: scale(1.03); }
@media (hover: hover) and (pointer: fine) {
  .ov-card:hover .ov-live-on .ov-ch img.ov-live { transform: scale(1.03); }
}
/* touch: no description over the picture, so a focused card's live preview still lifts a little */
@media (hover: none), (pointer: coarse) {
  .ov-card:focus-visible .ov-live-on .ov-ch img.ov-live { filter: brightness(1.12); }
}
/* the core's card transitions cover the panel: open / close it without the fade */
.ov-instant .ov-panel, .ov-instant .ov-panel * { transition: none !important; }
/* during a dive the panel takes no input */
.ov-busy .ov-panel { pointer-events: none !important; }
/* (fix round) a card dive / its way back is a cinematic move: the panel's own chrome (its bar with Close and the menu, the page
   arrows) steps back to .18 with the core's (main.js body.moving), so the opening is the only thing that moves */
.ov-bar { transition: opacity .5s ease; }
.ov-busy .ov-bar { opacity: .18; transition: opacity .25s ease; }
.ov-busy .ov-page.ov-show { opacity: .18; }

/* ---------------------------------------------------------------- two rows on a short-for-width screen
   (1440x900: the first four cards and their titles fit under the filter, the fifth peeks in). Less air above the
   filter, a tighter row gap, and the thumbnails capped so two rows fit; they stay 16:9 (narrower, never cropped), and
   the cap stops biting at about 1080 px tall. Only where the grid has two columns. */
@media (min-width: 761px) and (min-height: 700px) {
  /* the column narrows with the thumbnails, so the filter title still lines up with the left card */
  .ov-pinner {
    padding-top: calc(clamp(64px, 12vh, 196px) + var(--ov-st));
    max-width: calc(2 * min(492px, max(360px, calc((37vh - 122px) * 16 / 9))) + clamp(28px, 8vw, 116px) + 2 * var(--ov-pad));
  }
  .ov-filter { margin-bottom: clamp(28px, 4vh, 70px); }
  .ov-grid { row-gap: clamp(36px, 5vh, 84px); }
  .ov-thumb { max-width: max(360px, calc((37vh - 122px) * 16 / 9)); margin-left: auto; margin-right: auto; }
}

/* ---------------------------------------------------------------- the story's seven chapters (under "The story") */
.ov-mchaps-li { margin: 0 0 8px; }
/* a grid, not a wrapping row (it broke 6 + 1 on a desktop, 2 + 1 + 1 ... on a phone): four columns and three on a desktop,
   one chapter per line on a portrait phone, two columns in short landscape (below) */
.ov-mchaps {
  display: grid; grid-template-columns: repeat(4, max-content); column-gap: 28px;
  padding-left: 46px; /* under the item text (the number column is 2.2em of 11 px + a 22 px gap) */
}
.ov-mchap {
  position: relative;
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 44px;
  padding: 0;
  font-size: 11px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255,255,255,.55);
  white-space: nowrap;
  text-align: left;
  transition: color .3s;
}
.ov-mcn { color: rgba(255,255,255,.5); font-variant-numeric: tabular-nums; letter-spacing: .14em; }
.ov-mchap::before {
  content: ""; position: absolute; left: -10px; top: 50%;
  width: 4px; height: 4px; margin-top: -2px; border-radius: 50%;
  background: var(--ov-red); opacity: 0; transform: scale(.3);
  transition: opacity .4s, transform .5s var(--ov-ease);
}
.ov-mchap.ov-mchap-at::before { opacity: .9; transform: none; }
@media (hover: hover) {
  .ov-mchap:hover { color: rgba(255,255,255,.75); }
  .ov-mchap:hover .ov-mcn { color: rgba(255,255,255,.62); }
}
.ov-mchap:active { color: rgba(255,255,255,.75); }
.ov-mchap:focus-visible { outline-offset: 2px; color: rgba(255,255,255,.75); }
/* a narrow window or a tablet: two columns (four run ~780 px wide from the menu's left edge) */
@media (max-width: 960px) {
  .ov-mchaps { grid-template-columns: repeat(2, max-content); }
}
@media (max-width: 760px) {
  .ov-mchaps { padding-left: 40px; grid-template-columns: 1fr; grid-auto-rows: 44px; column-gap: 0; }
}
@media (max-width: 380px) {
  .ov-mchaps { padding-left: 34px; }
}
/* short landscape: everything on one screen. The nav column is wider, its "Menu" label steps out, the three big items
   are a little smaller (still 44 px targets) and the seven chapters sit in two columns under "The story" */
@media (max-height: 500px) and (orientation: landscape) {
  .ov-minner { grid-template-columns: 1.6fr 1fr; padding-top: calc(56px + var(--ov-st)); }
  .ov-mnav > .ov-label { display: none; }
  .ov-mlist { margin-top: 0; }
  .ov-mitem { font-size: 28px; min-height: 44px; }
  .ov-marr { font-size: 11px; }
  .ov-mchaps { display: grid; grid-template-columns: repeat(2, max-content); column-gap: 14px; padding-left: 40px; }
  .ov-mchap { letter-spacing: .12em; }
  .ov-mchaps-li { margin-bottom: 0; }
}

/* (core-fix, the phone review: in the menu on a short landscape phone "Book a call" was 161 x 23 to the touch, the rest of it under
   the screen's bottom edge (WebKit iPhone 14 Pro landscape, 734 x 343: the menu ran 41 px past it), and chapter 7 152 x 43, its last
   pixel under the row below. Under ~420 px of height "All projects" and "Book a call" share a row, a little smaller (each still a
   44 px target), and the chapters' grid keeps a 2 px gap to it: the whole menu fits on the screen, every target 44 px or more) */
@media (max-height: 420px) and (orientation: landscape) {
  .ov-minner { padding-bottom: calc(12px + var(--ov-sb)); }
  .ov-mlist { display: grid; grid-template-columns: max-content max-content; column-gap: 30px; align-items: center; }
  .ov-mlist > li:first-child, .ov-mlist > .ov-mchaps-li { grid-column: 1 / -1; }
  .ov-mitem { font-size: 25px; min-height: 44px; }
  .ov-mchaps-li { margin-bottom: 2px; }
}

/* (20 Oct, surface) a short portrait phone: iPhone Safari with its bars (393 x 660), 360 x 740, or any portrait screen under
   ~790 px. One chapter a line ran the menu 45 px past the bottom at 393 x 660 (no insets) and 46 px at 360 x 740 with a
   notch and a home bar, so the blurb, then Contact / Elsewhere, slid under the bottom edge / the home bar. The seven
   chapters sit in two columns there (01-04, then 05-07; each still a 44 px target, a long name wraps to two lines), which
   gives back 132 px: the whole menu fits with no scroll */
@media (max-width: 760px) and (max-height: 790px) and (orientation: portrait) {
  .ov-mchaps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: repeat(4, minmax(44px, auto));
    grid-auto-rows: auto; grid-auto-flow: column;
    column-gap: 16px;
  }
  .ov-mchap { white-space: normal; letter-spacing: .12em; line-height: 1.35; padding: 4px 0; }
  .ov-mcn { flex: none; }
  .ov-mct { text-wrap: balance; }
}
/* under 700 px tall the gaps close up a little (375 x 629 with a notch and a home bar ran 25 px over) */
@media (max-width: 760px) and (max-height: 700px) and (orientation: portrait) {
  .ov-minner { gap: 12px; }
  .ov-mfoot { padding-top: 10px; }
  .ov-mfact { line-height: 1.55; }
}
/* and on the shortest (an iPhone SE, 320 x 568, or a small phone's Safari with its bars up) the blurb under the links steps
   out, so Contact and Elsewhere stay on the screen */
@media (max-width: 760px) and (max-height: 600px) and (orientation: portrait) {
  /* (round 15) not the whole line: its short version, in one row */
  .ov-mfact { letter-spacing: .06em; white-space: nowrap; }
  .ov-mfact-full { display: none; }
  .ov-mfact-short { display: inline; }
}
/* (20 Oct, surface) All Projects on a portrait phone: the card titles scrolled under "Close All Projects" (the projects
   review's phone frame). The scroll body is masked out under the bar and fades back in over the 20 px below it, as on a
   landscape phone (the menu's own scroll is not masked). A gradient on the bar alone hid them too, but would let them show
   through during a card dive, when the bar steps back to .18 */
@media (max-width: 760px) and (orientation: portrait) {
  .ov-projects .ov-scroll {
    -webkit-mask-image: linear-gradient(to bottom, transparent calc(var(--ov-barh) + var(--ov-st)), #000 calc(var(--ov-barh) + var(--ov-st) + 20px));
    mask-image: linear-gradient(to bottom, transparent calc(var(--ov-barh) + var(--ov-st)), #000 calc(var(--ov-barh) + var(--ov-st) + 20px));
  }
}

/* ---------------------------------------------------------------- the menu on a phone (23 Oct, the real-phone feedback:
   it "doesn't look organized")
   Only where the main pointer is a finger (pointer: coarse) on a phone-sized screen; a mouse keeps every rule above. It
   had two numbered lists side by side (01-03 for the big rows, 01-07 for the chapters, so two 01s and two 02s), and The
   story's own row did exactly what "01 Home" under it does. Here: that row steps out and its name heads the seven
   chapters as a small label, only the chapters keep their numbers, the chapters line up with the rows under them, and on a
   portrait phone the blurb follows the links instead of sitting alone at the bottom edge. All Projects is unchanged. */
@media (pointer: coarse) and (max-width: 760px), (pointer: coarse) and (max-height: 500px) {
  .ov-mlist > li:first-child { display: none; }
  .ov-mchaps-li::before {
    content: "The story";
    content: attr(data-story); /* (the same words in the visitor's language: overlays.js) */
    display: block; margin: 0 0 2px;
    font-size: 11px; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; line-height: 1.4;
    color: var(--ov-label);
  }
  .ov-mitem { gap: 0; }
  .ov-mitem .ov-mnum { display: none; }
  .ov-mitem .ov-marr { margin-left: 12px; font-size: max(11px, .34em); }
  .ov-mchaps { padding-left: 0; }
}
/* (run 2) two groups with air between them: the seven chapters, then All projects and Book a call (the 07 row ran straight
   into "All projects", ~12 px apart; 22 px more still fits an iPhone's Safari, 393 x 660, with ~18 px to spare) */
@media (pointer: coarse) and (max-width: 760px) and (orientation: portrait) {
  .ov-mchaps-li { margin: 0 0 22px; }
  .ov-mfact { margin-top: 0; }
}
/* one chapter a line down to 731 px of height: without The story's row the list fits there (the two columns came in under
   790 px, where a name like "This is my thing" broke over two lines); under 731 px the two columns stay. (28 Oct: it was 651,
   so an iPhone's Safari, 393 x 660, had one a line; with the Language row in that ran 76 px past its bottom edge. Under 341 px
   wide the two columns stay too: one a line there ran 35 px over) */
@media (pointer: coarse) and (min-width: 341px) and (max-width: 760px) and (max-height: 790px) and (min-height: 731px) and (orientation: portrait) {
  .ov-mchaps { grid-template-columns: 1fr; grid-template-rows: none; grid-auto-rows: 44px; grid-auto-flow: row; column-gap: 0; }
  .ov-mchap { white-space: nowrap; letter-spacing: .18em; line-height: normal; padding: 0; }
}
/* a turned phone: the blurb under Contact / Elsewhere starts where their column does (it sat 27 px to the left of it, under
   the column's rule: the rule's 1 px and the column's 26 px of padding), and keeps its old width (it reaches 27 px into the
   right padding), so it still breaks in three lines and never inside "17-year-old". (Round 32: at 734 x 343 with no insets that put
   "Sweden" 2 px from the screen's edge) it reaches at most to 12 px from the edge or the safe area, in every language */
@media (pointer: coarse) and (max-height: 500px) and (orientation: landscape) {
  .ov-mfact { padding-left: 27px; margin-right: calc(-1 * clamp(0px, var(--ov-mpr) - var(--ov-sr) - 12px, 27px)); }
}
/* (mobile round 3, core-ui: "the menu doesn't look organized") the menu's top bar on a phone: one control, the X, in the very
   spot the menu button had (so the button turns into it as the panel fades in over it). Three things there all closed the
   menu: the drop mark, the word "Close" and the X. On a phone the first two step out; with a mouse, or in All Projects'
   bar (Roman likes it as it is), nothing changes. The X's top is the menu button's (8 px under the safe area): the bar
   centres a 44 px button in its --ov-barh, so 60 px - barh of top margin moves it down by half of that */
@media (pointer: coarse) and (max-width: 760px), (pointer: coarse) and (max-height: 500px) {
  .ov-menu .ov-bar > .ov-mark, .ov-menu .ov-closetxt, .ov-menu .ov-sep { display: none; }
  .ov-menu .ov-bar { justify-content: flex-end; padding-right: calc(8px + var(--ov-sr)); }
  .ov-menu .ov-x { margin-top: calc(60px - var(--ov-barh)); }
  /* (round 15) the Sound row moves up into the bar's empty left side, on the X's line: the footer keeps its rows */
  .ov-menu .ov-msound { display: none; }
  .ov-menu .ov-mbarsnd { display: flex; align-items: center; margin: calc(60px - var(--ov-barh)) auto 0 max(0px, calc(var(--ov-pad) - 6px)); }
  .ov-menu .ov-mbarsnd .ov-label { margin-right: 4px; }
  .ov-menu .ov-mbarsnd .ov-mlinks { margin: 0; }
}
/* (28 Oct) the menu with its Language row on every phone, no scroll, every target still 44 px, the Language row above the fact
   line. Portrait: a little less air between the chapters and the rows under them under 790 px of height; under 341 px wide the
   chapters keep their two columns up to 840 px of height; on the shortest (320 x 568) less air above and below */
@media (pointer: coarse) and (max-width: 760px) and (max-height: 790px) and (orientation: portrait) {
  .ov-mchaps-li { margin-bottom: 14px; }
}
@media (max-width: 760px) and (max-height: 700px) and (orientation: portrait) {
  .ov-minner { padding-bottom: calc(10px + var(--ov-sb)); } /* (a 280 x 653 folding phone's cover screen ran 2 px over) */
}
@media (max-width: 340px) and (max-height: 840px) and (orientation: portrait) {
  .ov-mchaps { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(4, minmax(44px, auto)); grid-auto-rows: auto; grid-auto-flow: column; column-gap: 16px; }
  .ov-mchap { white-space: normal; letter-spacing: .12em; line-height: 1.35; padding: 4px 0; }
}
@media (max-width: 760px) and (max-height: 600px) and (orientation: portrait) {
  .ov-minner { padding-top: calc(60px + var(--ov-st)); padding-bottom: calc(4px + var(--ov-sb)); gap: 8px; }   /* (round 15: 12 -> 4 px, room for the short fact line) */
  .ov-mlang { margin-top: 0; }
}
@media (pointer: coarse) and (max-width: 760px) and (max-height: 600px) and (orientation: portrait) {
  .ov-mchaps-li { margin-bottom: 10px; }
}
/* a turned phone: Contact, Elsewhere and the three names share the narrow column beside the chapters (at 844 x 390 the names
   took two lines and the fact line ran 7 px under the bottom edge; 54 px at 734 x 343). There the names are 15 px with 8 px
   of padding (one line from ~730 px wide; still 44 px targets), the word "Language" is for screen readers only (the names
   say what they are), and the column's gaps are a little smaller */
@media (max-height: 500px) and (orientation: landscape) {
  .ov-minner { row-gap: 8px; }
  .ov-mfoot { gap: 2px; }
  .ov-mlang > .ov-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .ov-mlang .ov-mlink { padding: 0 6px; font-size: 15px; }
  .ov-mlang .ov-mlinks { margin: 0 0 0 -6px; }
  /* (round 22, the review's turned-phone menu: "Contact" at the top of its column sat under the bar's fade, at about 2.2:1 against
     5.4:1 for "Elsewhere", and in Swedish and Russian the third language name wrapped onto a row of its own) the menu's bar fades out
     within its own height here (nothing scrolls under it), and the names keep 6 px of padding, so all three share one row */
  .ov-menu .ov-bar::before { height: 100%; }
}
/* (round 32) and from 640 px wide they never wrap: at 667 x 375 in English "Русский" still took a row of its own */
@media (max-height: 500px) and (orientation: landscape) and (min-width: 640px) {
  .ov-mlang .ov-mlinks { flex-wrap: nowrap; }
}
/* (round 11) a small phone on its side (568 x 320, 600 x 320): the chapters' two columns and the two big rows were wider than
   the room left of Contact / Elsewhere, so that column ran past the screen's right edge and "hi@roman.wtf" and the fact line
   lost their last letters (English and Russian; the menu never scrolls sideways). There the gaps are a little smaller and the
   chapters' tracking is .06em; every target is still 44 px. On the 568 the Russian fact line keeps to its column */
@media (pointer: coarse) and (max-height: 500px) and (orientation: landscape) and (max-width: 610px) {
  .ov-minner { column-gap: 20px; }
  .ov-mlist { column-gap: 18px; }
  .ov-mchaps { column-gap: 12px; }
  .ov-mchap { letter-spacing: .06em; }
  /* (round 32: the fact line still ran 30 px past the right edge and 24 px under the bottom in English) the short line, as on a
     short portrait phone */
  .ov-mfact { white-space: nowrap; letter-spacing: .06em; }
  .ov-mfact-full { display: none; }
  .ov-mfact-short { display: inline; }
}
@media (pointer: coarse) and (max-height: 500px) and (orientation: landscape) and (max-width: 590px) {
  html[lang="ru"] .ov-mfact { margin-right: 0; }
}
/* (round 14) the fact line: on a 320 px phone held upright its tracking is a little tighter, so it keeps to its rows and the menu
   still fits its screen without scrolling */
@media (max-width: 340px) and (orientation: portrait) { .ov-root .ov-mfact { letter-spacing: .1em; } }
/* ---------------------------------------------------------------- (round 17) five cuts: All projects as cuts in the page
   Only with ?ap=cuts (overlays.js makeCuts puts .ov-cuts on the panel); without it nothing here applies. overlays.js places every
   row, name, cut and description (transforms, clip-paths); this sheet gives them their look. */
.ov-cuts .ov-facets { display: none; }
.ov-cuts .ov-page, .ov-cuts .ov-peek { display: none !important; }
.ov-cuts.ov-cuts-hover .ov-scroll { overflow: hidden; -webkit-mask-image: none; mask-image: none; }
.ov-cuts.ov-cuts-scroll .ov-scroll {
  scroll-snap-type: y mandatory;
  -webkit-mask-image: linear-gradient(to bottom, transparent calc(var(--ov-barh) + var(--ov-st)), #000 calc(var(--ov-barh) + var(--ov-st) + 20px));
  mask-image: linear-gradient(to bottom, transparent calc(var(--ov-barh) + var(--ov-st)), #000 calc(var(--ov-barh) + var(--ov-st) + 20px));
}
.ov-cuts .ov-csnap { position: absolute; left: 0; top: 0; width: 1px; height: 1px; scroll-snap-align: start; pointer-events: none; }
.ov-cuts .ov-cutspace { height: 0; pointer-events: none; }
.ov-cuts .ov-pinner { position: sticky; top: 0; height: 100%; min-height: 0; max-width: none; margin: 0; padding: 0; overflow: visible; }
.ov-cuts .ov-filter, .ov-cuts.ov-in .ov-filter { position: absolute; left: 0; top: 0; margin: 0; z-index: 8; transition: opacity .7s ease .25s; }
.ov-cuts .ov-ftitle { font-size: clamp(28px, 2.7vw, 40px); margin-top: 4px; min-height: 44px; }
.ov-cuts .ov-grid { position: absolute; inset: 0; display: block; }
/* (the cuts are wider than the screen and tilted: clipped here, so the list never has a hidden overflow that a focus, a find in the
   page or a script could scroll sideways) */
.ov-cuts .ov-grid { overflow: hidden; overflow: clip; }
.ov-cuts .ov-card, .ov-cuts.ov-in .ov-card {
  position: absolute; left: 0; top: 0; width: 100%; height: 0;
  opacity: 1; transition: none; text-align: left; cursor: pointer;
}
.ov-cuts .ov-card:focus-visible { outline: none; }
/* while the panel is open every row is its own layer: rows that only move (the phone's scroll, a cut opening above them) are
   moved on the compositor, not painted again */
.ov-cuts.ov-in .ov-cutrow, .ov-cuts.ov-in .ov-filter { will-change: transform; }
.ov-cuts .ov-chit { position: absolute; left: 0; right: 0; top: 0; height: 0; z-index: 3; }
/* the name: the biggest type on the panel, sitting on the cut's upper lip; it rises out of the cut as the panel opens */
.ov-cuts .ov-cname { position: absolute; left: 0; bottom: 0; z-index: 2; overflow: hidden; padding: 6px .12em 0 0; transform-origin: 0 100%; pointer-events: none; }
.ov-cuts .ov-ctitle {
  display: block; margin: 0;
  font-size: 80px; font-weight: 300; letter-spacing: -.035em; line-height: 1.12;
  text-transform: none; white-space: nowrap;
  color: rgba(255,255,255,.62); text-shadow: none; transition: none;
}
.ov-cuts .ov-ctitle::before { left: auto; right: calc(100% + .14em); top: 55%; width: .07em; height: .07em; min-width: 4px; min-height: 4px; margin-top: 0; }
.ov-cuts .ov-card:focus-visible .ov-ctitle::before { opacity: .9; transform: none; }
.ov-cuts .ov-card:focus-visible .ov-ctitle { text-decoration: underline; text-decoration-thickness: .02em; text-underline-offset: .12em; text-decoration-color: rgba(255,255,255,.4); }
.ov-cuts .ov-cbase { display: inline-block; width: 0; height: 0; vertical-align: baseline; }
.ov-cuts .ov-ccat {
  position: absolute; right: 0; bottom: 0; z-index: 2; margin: 0;
  font-size: 11px; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; line-height: 1;
  color: rgba(255,255,255,.62); white-space: nowrap; pointer-events: none;
}
.ov-cuts .ov-cseen { position: absolute; right: 100%; top: 0; margin-right: 1.6em; color: rgba(255,255,255,.5); letter-spacing: .18em; display: none; }
.ov-cuts.ov-cuts-hover .ov-cutrow.ov-visited .ov-cseen { display: inline; }
/* the description: under the lower lip, one or two lines, then "Step inside" */
.ov-cuts .ov-cdesc {
  display: block; position: absolute; left: 0; top: 0; z-index: 2;
  margin: 0; max-width: none; text-align: left;
  font-size: 14px; line-height: 1.5; font-weight: 300; letter-spacing: .005em;
  color: rgba(255,255,255,.74); pointer-events: none;
}
.ov-cuts .ov-cdk { display: none; }
.ov-cuts .ov-cgo { display: inline-block; margin-left: 1.3em; font-size: 11px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: rgba(255,255,255,.74); white-space: nowrap; }
.ov-cuts .ov-cdesc.ov-cgo-br .ov-cgo { display: block; margin: 6px 0 0; }
.ov-cuts .ov-cgo i { font-style: normal; display: inline-block; margin-left: .5em; color: var(--ov-red); }
.ov-cuts.ov-cuts-scroll .ov-cdesc { font-size: 13.5px; line-height: 1.48; }
.ov-cuts.ov-cuts-scroll .ov-cdk { display: block; margin-bottom: 7px; font-size: 11px; font-weight: 600; letter-spacing: .22em; text-transform: uppercase; color: rgba(255,255,255,.6); }
.ov-cuts.ov-cuts-scroll .ov-cgo { display: block; margin: 10px 0 0; }

/* the cut: light spilling out over the page, a bright rim on both lips, the opening with the project's picture, the lower lip's face */
.ov-cuts .ov-cut { position: absolute; left: 0; top: 0; width: 100%; height: 0; z-index: 1; pointer-events: none; transform: rotate(var(--ang)); transform-origin: 50% 0; }
.ov-cuts .ov-cut > div { position: absolute; left: 0; width: 100%; }
.ov-cuts .ov-cspill { pointer-events: none; }
/* the light's rays (a mouse; a phone keeps the plain glow, cheaper to paint at three device pixels per CSS pixel) */
.ov-cuts.ov-cuts-hover .ov-cspill {
  -webkit-mask-image: var(--ov-rays); mask-image: var(--ov-rays);
  -webkit-mask-size: 150% 100%; mask-size: 150% 100%;
  -webkit-mask-position: var(--rx) 0; mask-position: var(--rx) 0;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.ov-cuts .ov-crim { background: linear-gradient(90deg, rgba(var(--hot),.35), rgb(var(--hot)) 30%, rgb(var(--hot)) 70%, rgba(var(--hot),.35)); }
.ov-cuts .ov-cap { overflow: hidden; background: radial-gradient(ellipse 70% 120% at 50% 50%, rgba(var(--tint),.24), rgba(7,9,13,1) 72%); }
.ov-cuts .ov-cap .ov-thumb {
  position: absolute; aspect-ratio: auto; max-width: none; margin: 0; border-radius: 0;
  background: #101114; overflow: hidden; transform-origin: 50% 50%; outline: none !important;
}
.ov-cuts .ov-thumb .ov-shade, .ov-cuts .ov-thumb .ov-glint, .ov-cuts .ov-thumb .ov-desc, .ov-cuts .ov-thumb .ov-go, .ov-cuts .ov-ch-c { display: none !important; }
.ov-cuts .ov-zoom { scale: none !important; filter: none !important; transition: none !important; transform: scale(1.045); }
.ov-cuts.ov-cuts-hover .ov-cutrow.ov-open .ov-zoom { will-change: transform; } /* (it leans toward the pointer: moved on the compositor) */
.ov-cuts .ov-ch { translate: none !important; }
.ov-cuts .ov-ch-r::after { content: none !important; }
.ov-cuts .ov-ch img.ov-live { transform: none !important; filter: none !important; }
/* Griffin Games: the soft patch over the number in its ring (the ring's own dark inside, feathered) */
.ov-cuts .ov-chide { position: absolute; border-radius: 999px; background: rgb(64,66,72); box-shadow: 0 0 5px 3px rgb(64,66,72); pointer-events: none; }
/* Prompt Genius: its picture's warm halo goes grey-blue in the cut's cool light */
.ov-cuts .ov-cutrow[data-id="prompt"] .ov-ch img:not(.ov-live) { filter: saturate(.16) brightness(1.04); }
/* the world behind the cut is lit from the middle: its far ends fall off into the dark */
.ov-cuts .ov-cap::after { content: ""; position: absolute; inset: 0; background: radial-gradient(ellipse 56% 150% at 50% 50%, rgba(5,7,11,0) 52%, rgba(5,7,11,.5) 100%); pointer-events: none; }
.ov-cuts .ov-ctone { position: absolute; inset: 0; background: rgb(var(--tint)); mix-blend-mode: soft-light; opacity: .34; }
.ov-cuts.ov-cuts-scroll .ov-ctone { mix-blend-mode: normal; opacity: .07; }
.ov-cuts .ov-cshd { position: absolute; inset: 0; }
.ov-cuts .ov-ccore { position: absolute; left: 0; right: 0; background: linear-gradient(to bottom, rgba(var(--hot),0), rgba(var(--hot),.95) 50%, rgba(var(--hot),0)); }
.ov-cuts .ov-cface { background: linear-gradient(to bottom, rgba(var(--tint),.32), rgb(28,30,35) 40%, rgb(22,23,27)); }
.ov-cuts .ov-cscar {
  height: 1px; opacity: 0;
  background: linear-gradient(90deg, transparent 2%, rgba(255,255,255,.26) 9%, rgba(255,255,255,.26) 91%, transparent 98%);
  -webkit-mask-image: var(--ov-rays); mask-image: var(--ov-rays); -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
}
.ov-cuts .ov-cut > .ov-cspark { left: 0; width: 60px; top: -7px; height: 14px; opacity: 0; background: radial-gradient(closest-side, rgba(240,247,255,.95), rgba(200,225,255,.35) 45%, rgba(200,225,255,0)); }
.ov-cuts .ov-cdust { display: none; overflow: hidden; }
.ov-cuts.ov-cuts-hover .ov-cdust.on { display: block; }
.ov-cuts .ov-cdust i {
  position: absolute; left: -220px; top: -220px; right: 0; bottom: 0;
  background-image: var(--ov-dust, none); background-repeat: repeat;
  animation: ov-cdust 34s linear infinite;
}
@keyframes ov-cdust { to { transform: translate3d(220px, 220px, 0); } }
/* the calm version: no lean, no dust; the picture fades in and out instead of the lips moving */
.ov-cuts.ov-cuts-calm .ov-cap .ov-thumb { transition: opacity .3s ease; }
.ov-cuts.ov-cuts-calm .ov-cdust { display: none !important; }
/* closing: the cuts seal shut as the panel fades */
.ov-cuts.ov-cuts-seal .ov-cut { transform: rotate(var(--ang)) scaleY(.02); transition: transform .36s cubic-bezier(.4,0,.2,1); }
