﻿/* ==================================================================
   OLTRELIMITI â€” Interior Architecture & Design
   Every measurement here was taken off the existing Wix site so the
   rebuild lands on the same pixels: a 245px black rail inset 37px,
   Poppins 9px navigation, a photo strip 75% of the viewport tall,
   and a caption 12px beneath it in Poppins 15.75 / 9.
   ================================================================== */

:root {
  --ink:    #000000;
  --paper:  #FFFFFF;
  --quiet:  #8C8C8C;
  --signal: #ED1C24;          /* the studio red */
  --shade:  rgba(0,0,0,.62);
  --rule:   #FFFFFF;

  --rail:   245px;
  --inset:  37px;
  --strip:  75%;
  --cap-x:  9px;
  --cap-y:  11px;

  --hold:   2.5;              /* screenfuls each project holds */
}

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

html { background: var(--ink); }
body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: Poppins, Arial, Helvetica, sans-serif;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}
a { color: inherit; text-decoration: none; }

/* ---------------------------------------------------------------
   THE RAIL â€” fixed on every page
   --------------------------------------------------------------- */
.rail {
  position: fixed; left: 0; top: 0; z-index: 50;
  width: var(--rail); height: 100vh;
  background: var(--ink);
  border-right: 1px solid var(--rule);
}
.rail-logo {
  position: absolute; left: var(--inset); top: 30px;
  height: 39px; width: auto; display: block;
}
.rail-nav { position: absolute; left: var(--inset); top: 137px; }
.rail-nav a, .rail-nav button {
  display: block; background: none; border: 0; padding: 0 0 16px;
  font-family: inherit; font-size: 9px; line-height: 12.6px;
  font-weight: 400; color: var(--paper);
  cursor: pointer; text-align: left;
  transition: color .2s ease;
}
.rail-nav a:hover, .rail-nav button:hover { color: var(--signal); }
.rail-nav a:focus-visible, .rail-nav button:focus-visible {
  outline: 1px solid var(--paper); outline-offset: 3px;
}
.rail-nav [aria-current="page"] { color: var(--signal); }

/* the label returns to the index; the chevron opens the list â€” two
   hit areas so neither steals the other */
.rail-row { display: flex; align-items: center; gap: 8px; padding-bottom: 16px; }
.rail-row a, .rail-row button { padding: 0; }
.chev { width: 7px; height: 7px; display: block; transition: transform .3s ease; }
.chev path { stroke: currentColor; stroke-width: 1.4; fill: none; }
.rail.open .chev { transform: rotate(180deg); }

/* collapsed, this must contribute exactly zero height or it shoves
   About and Contact off the site's own spacing */
.rail-list {
  margin: 0; padding: 0; list-style: none;
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .34s ease;
}
.rail-list > div { overflow: hidden; min-height: 0; }
.rail.open .rail-list { grid-template-rows: 1fr; }
.rail-list li:first-child button { padding-top: 2px; }
.rail-list li:last-child  button { padding-bottom: 15px; }
.rail-list button {
  padding: 0 0 9px; font-size: 8.5px; line-height: 12px;
  color: rgba(255,255,255,.62);
}
.rail-list button:hover,
.rail-list button[aria-current="true"] { color: var(--signal); }

/* the glyphs sit on the caption's line, as they do on the site */
.rail-social {
  position: absolute; left: 148px; top: calc(var(--strip) + 13px);
  display: flex; gap: 17px;
}
.rail-social a {
  display: block; width: 23px; height: 23px;
  color: var(--paper); transition: color .2s ease;
}
.rail-social svg { width: 100%; height: 100%; display: block; }
.rail-social a:hover { color: var(--signal); }

.content { margin-left: var(--rail); }

/* ---------------------------------------------------------------
   THE INDEX
   --------------------------------------------------------------- */
.index {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 84px 40px 96px;
}
.shelf {
  display: flex; flex-wrap: wrap;
  justify-content: center; align-items: flex-start;
  gap: 26px 34px; width: 100%; max-width: 1000px;
}
.tile {
  position: relative; flex: 0 0 auto;
  width: 224px; height: 200px;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  background: none; border: 0; padding: 0;
  cursor: pointer; color: inherit;
  isolation: isolate;
}
.mark {
  position: relative; z-index: 1;
  height: 104px; width: 100%;
  display: flex; align-items: center; justify-content: center;
}
/* the thumbnail belongs to the mark, so its lower edge stays above
   the caption â€” the caption reads as a subtitle, not an overlay */
.thumb {
  position: absolute; z-index: 0; left: 50%; top: 50%;
  width: 218px; height: 138px;
  transform: translate(-50%,-50%) scale(1.05);
  background-size: cover; background-position: center;
  opacity: 0; pointer-events: none;
  transition: opacity .4s ease, transform .4s ease;
}
.thumb::after { content: ""; position: absolute; inset: 0; background: var(--shade); }
.tile:hover .thumb, .tile:focus-visible .thumb {
  opacity: 1; transform: translate(-50%,-50%) scale(1);
}
/* Each logo file carries a different amount of transparent margin, so
   the artwork is cropped to its measured content box before sizing â€”
   that is what lets seven unrelated marks sit level. */
.crop {
  position: relative; z-index: 1; display: block; overflow: hidden;
  transition: transform .4s ease;
}
.crop img { position: absolute; max-width: none; display: block;
  opacity: .88; transition: opacity .3s ease; }
.tile:hover .crop, .tile:focus-visible .crop { transform: scale(.92); }
.tile:hover .crop img, .tile:focus-visible .crop img { opacity: 1; }

.cap {
  position: relative; z-index: 1; margin-top: 26px;
  font-size: 8.5px; letter-spacing: .17em; text-transform: uppercase;
  color: var(--quiet); text-align: center; transition: color .3s ease;
}
.tile:hover .cap, .tile:focus-visible .cap { color: var(--signal); }
.tile:focus-visible { outline: 1px solid var(--paper); outline-offset: 6px; }

/* ---------------------------------------------------------------
   THE PROJECTS â€” sticky panes that stack
   --------------------------------------------------------------- */
/* Row height is pure CSS rather than measured in JS: a script that
   reads innerHeight before the viewport has one writes zeroes and the
   whole page collapses. calc() cannot be wrong. */
.reel {
  display: grid;
  grid-template-columns: minmax(0,1fr);
  grid-auto-rows: calc(100vh * var(--hold));
}
.pane {
  position: sticky; top: 0; align-self: start;
  height: 100vh; min-width: 0; overflow: hidden;
  background: var(--ink);
  display: flex; flex-direction: column;
  -webkit-user-select: none; user-select: none;
}
/* two outlined boxes stacked â€” the photo above, the caption below â€”
   so the shared edge reads as the rule under every project */
.holder {
  position: relative; flex: 0 0 var(--strip);
  min-height: 0; min-width: 0; overflow: hidden;
  border: 1px solid var(--rule);
}
.strip {
  width: 100%; height: 100%;
  display: flex; gap: 4px;
  background: var(--ink);
  overflow-x: scroll; overflow-y: hidden;
  scrollbar-width: none; cursor: grab;
}
.strip::-webkit-scrollbar { display: none; }
.strip.dragging { cursor: grabbing; }
.strip img {
  height: 100%; width: auto; flex: 0 0 auto;
  display: block; object-fit: cover;
  -webkit-user-drag: none; user-select: none;
}
.caption {
  flex: 1 1 auto; min-height: 0;
  padding: var(--cap-y) 0 0 var(--cap-x);
  border: 1px solid var(--rule);
}
.caption b { display: block; font-size: 15.75px; line-height: 25.2px; font-weight: 400; }
.caption i { display: block; font-style: normal; font-size: 9px; line-height: 14.5px; }

/* the reveal, matching the site's own 1200ms / 960ms fades */
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.js-reveal .pane .holder,
.js-reveal .pane .caption { opacity: 0; }
.js-reveal .pane.revealed .holder  { animation: fadeIn 1200ms linear 1ms forwards; }
.js-reveal .pane.revealed .caption { animation: fadeIn  960ms linear 1ms forwards; }

/* ---------------------------------------------------------------
   THE NEXT CUE â€” shaft and head are one object, so the whole arrow
   travels rather than the head sliding off a stationary line
   --------------------------------------------------------------- */
.cue {
  position: fixed; right: 34px; bottom: 26px; z-index: 60;
  display: grid; grid-template-columns: 1fr auto;
  align-items: end; gap: 0 12px;
  background: none; border: 0; padding: 0;
  cursor: pointer; text-align: right;
  /* a button does not inherit colour from the page â€” without this the
     UA stylesheet paints "Next" black on a black ground */
  font-family: inherit; color: var(--paper);
  opacity: 1; transition: opacity .35s ease;
}
.cue.spent { opacity: 0; pointer-events: none; }
.cue-label {
  grid-column: 1; font-size: 9px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--paper);
}
.cue-name  { grid-column: 1; margin-top: 5px; font-size: 9px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--signal); white-space: nowrap; }
.cue-rule {
  grid-column: 2; grid-row: 1 / span 2;
  width: 1px; height: 76px; align-self: end;
  background: var(--paper); position: relative;
  animation: nudge 2s ease-in-out infinite;
}
.cue-rule::after {
  content: ""; position: absolute; left: -3px; bottom: -1px;
  width: 7px; height: 7px;
  border-left: 1px solid var(--paper); border-bottom: 1px solid var(--paper);
  transform: rotate(-45deg);
}
@keyframes nudge { 0%,100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

/* ---------------------------------------------------------------
   ABOUT & CONTACT
   --------------------------------------------------------------- */
.page { min-height: 100vh; padding: 116px 56px 96px; max-width: 1000px; }
.page h1 {
  margin: 0 0 40px; font-size: 11px; font-weight: 400;
  letter-spacing: .3em; text-transform: uppercase;
}
.prose p {
  margin: 0 0 26px; max-width: 62ch;
  font-size: 13.5px; line-height: 2; font-weight: 300;
  color: rgba(255,255,255,.86);
}
.lede { margin: 0 0 44px; max-width: 54ch; font-size: 13.5px; line-height: 1.9;
  font-weight: 300; color: rgba(255,255,255,.86); }

.details { display: flex; flex-wrap: wrap; gap: 34px 64px; margin-top: 8px; }
.details div { display: flex; flex-direction: column; gap: 7px; }
.details dt, .details span.k {
  font-size: 8.5px; letter-spacing: .2em; text-transform: uppercase; color: var(--quiet);
}
.details a, .details span.v { font-size: 11px; line-height: 1.7; }
.details a { transition: color .2s ease; }
.details a:hover { color: var(--signal); }

form { display: grid; gap: 22px; max-width: 520px; margin-bottom: 56px; }
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }
label { display: flex; flex-direction: column; gap: 8px;
  font-size: 8.5px; letter-spacing: .2em; text-transform: uppercase; color: var(--quiet); }
input, textarea {
  background: none; border: 0; border-bottom: 1px solid rgba(255,255,255,.34);
  color: var(--paper); font-family: inherit; font-size: 12px;
  padding: 7px 0; transition: border-color .2s ease;
}
input:focus, textarea:focus { outline: 0; border-bottom-color: var(--signal); }
textarea { resize: vertical; min-height: 92px; }
button[type="submit"] {
  justify-self: start; margin-top: 6px;
  background: none; border: 1px solid var(--paper);
  color: var(--paper); font-family: inherit;
  font-size: 9px; letter-spacing: .2em; text-transform: uppercase;
  padding: 13px 34px; cursor: pointer;
  transition: background .25s ease, color .25s ease, border-color .25s ease;
}
button[type="submit"]:hover { background: var(--signal); border-color: var(--signal); }

/* ---------------------------------------------------------------
   NARROW SCREENS
   --------------------------------------------------------------- */
@media (max-width: 900px) {
  :root { --rail: 148px; --inset: 20px; }
      .mark { height: 74px; }
  .rail-social { left: 20px; gap: 12px; }
  .rail-social a { width: 18px; height: 18px; }
  .caption b { font-size: 13px; line-height: 20px; }
  .cue { right: 20px; bottom: 18px; }
  .page { padding: 92px 26px 72px; }
  .row { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .cue-rule { animation: none; }
  .thumb, .crop, .crop img, .cap, .rail-list { transition: none; }
  .js-reveal .pane .holder, .js-reveal .pane .caption { opacity: 1; animation: none; }
}

/* ---------------------------------------------------------------
   HOME â€” a full-bleed crossfading slideshow, as the site has now.
   Slides are stacked and cross-faded rather than scrolled, which is
   what the Wix gallery does behind its hidden overflow.
   --------------------------------------------------------------- */
.hero { position: relative; height: 100vh; overflow: hidden; background: var(--ink); }
.hero figure {
  position: absolute; inset: 0; margin: 0;
  opacity: 0; transition: opacity 1200ms ease-in-out;
}
.hero figure.on { opacity: 1; }
.hero img,
.hero video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* the slideshow never hides the page: if the script does not run,
   the first slide still shows */
.hero figure:first-child { opacity: 1; }
.js-hero .hero figure:first-child { opacity: 0; }
.js-hero .hero figure.on { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .hero figure { transition: none; }
}

/* ---------------------------------------------------------------
   HOME HEADER â€” the home page has no rail. The mark sits top-left
   and the nav top-right, both over the photograph. Measured off the
   live site: logo 70x40 at (24,19); nav at y=30, Poppins 9.58px,
   white, ~48px apart, 52px from the right edge.
   --------------------------------------------------------------- */
.topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  display: flex; align-items: flex-start; justify-content: space-between;
  padding: 19px 52px 0 24px;
  pointer-events: none;                 /* the photo stays clickable around it */
}
.topbar > * { pointer-events: auto; }
.topbar img { height: 40px; width: auto; display: block; }
.topbar nav { display: flex; gap: 48px; padding-top: 11px; }
.topbar nav a {
  font-size: 9.58px; line-height: 13px; font-weight: 400;
  color: var(--paper); transition: color .2s ease;
}
.topbar nav a:hover { color: var(--signal); }
.topbar nav a:focus-visible { outline: 1px solid var(--paper); outline-offset: 4px; }

/* the hero runs full-bleed on the home page, no rail to clear */
.home .content { margin-left: 0; }

@media (max-width: 900px) {
  .topbar { padding: 14px 20px 0 16px; }
  .topbar img { height: 30px; }
  .topbar nav { gap: 22px; padding-top: 8px; }
}

/* ---------------------------------------------------------------
   ABOUT & CONTACT â€” photograph left, words right, the studio mark
   bleeding off the bottom-right corner. Measured off the live site:
   heading Manrope Bold 42.72px; body Poppins 10.68/17.09, justified.
   --------------------------------------------------------------- */
.split {
  position: relative;
  min-height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 410px);
  gap: 0 62px;
  align-content: start;
  padding: 92px 40px 120px 140px;
}
.split-photo img {
  width: 100%; height: auto; display: block;
  max-height: 74vh; object-fit: cover;
}
.split-body h1 {
  margin: 0 0 26px;
  font-family: Manrope, Poppins, Arial, sans-serif;
  font-size: 42.72px; font-weight: 700; line-height: 1.12;
  letter-spacing: -.01em; text-transform: uppercase; color: var(--paper);
}
.split-body p {
  margin: 0 0 18px;
  font-size: 10.68px; line-height: 17.09px; font-weight: 400;
  text-align: justify; color: var(--paper);
}
.split-body .intro { margin-bottom: 34px; }

/* the mark, bleeding off the corner */
.arc {
  position: absolute; right: 14px; bottom: 0;
  width: 268px; height: auto; opacity: .95;
  pointer-events: none; user-select: none;
}

/* the form, boxed as the site has it */
.split form { display: grid; gap: 12px; margin: 0 0 30px; max-width: 400px; }
.split .row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.split form input, .split form textarea {
  width: 100%; background: none;
  border: 1px solid rgba(255,255,255,.55); border-radius: 0;
  color: var(--paper); font-family: inherit; font-size: 10px;
  padding: 8px 10px; transition: border-color .2s ease;
}
.split form input::placeholder, .split form textarea::placeholder { color: rgba(255,255,255,.72); }
.split form input:focus, .split form textarea:focus { outline: 0; border-color: var(--signal); }
.split form textarea { resize: vertical; min-height: 74px; }
.split form button[type="submit"] {
  justify-self: stretch; max-width: 187px;
  background: none; border: 1px solid var(--paper); color: var(--paper);
  font-family: inherit; font-size: 9px; letter-spacing: .06em;
  padding: 11px 0; cursor: pointer;
  transition: background .25s ease, border-color .25s ease, color .25s ease;
}
.split form button[type="submit"]:hover { background: var(--signal); border-color: var(--signal); }

.split .details { display: flex; flex-wrap: wrap; gap: 18px 40px; }
.split .details div { display: flex; flex-direction: column; gap: 5px; }
.split .details .k {
  font-size: 8px; letter-spacing: .18em; text-transform: uppercase; color: var(--quiet);
}
.split .details a, .split .details .v { font-size: 10px; }
.split .details a:hover { color: var(--signal); }

@media (max-width: 1100px) {
  .split { grid-template-columns: minmax(0,1fr); gap: 40px; padding: 84px 28px 110px; }
  .split-photo img { max-height: 46vh; }
  .split-body h1 { font-size: 32px; }
  .arc { width: 168px; right: 8px; }
}

/* ---------------------------------------------------------------
   ENTRANCES â€” the same three effects the Wix site uses, with the
   values read off it: Shape is a circle wipe 0% -> 75% over 1200ms
   with a 960ms fade; Expand scales from 0.8 over 1200ms; Spin turns
   a full -360deg from 0.6 over 1200ms with a 720ms fade. All linear.

   Pure CSS on purpose. These run on load with fill-mode backwards,
   so the element holds its opening state through the delay and ends
   at its natural one â€” nothing can leave the page stuck invisible
   the way a script-driven reveal can.
   --------------------------------------------------------------- */
@keyframes oltFade   { from { opacity: 0; } }
@keyframes oltShape  { from { clip-path: circle(0%); } to { clip-path: circle(75%); } }
@keyframes oltExpand { from { transform: scale(.8); } }
/* opens small and grows as it turns â€” Wix starts at .6, which barely
   reads as growth, so this opens much smaller */
@keyframes oltSpin   { from { transform: rotate(-360deg) scale(.18); } }

.anim-shape {
  animation: oltShape 1200ms linear 1ms backwards,
             oltFade   960ms linear 1ms backwards;
}
.anim-expand {
  animation: oltExpand 1200ms linear 1ms backwards,
             oltFade   1200ms linear 1ms backwards;
}
/* four times the speed of the others: 300ms rather than 1200ms */
.anim-spin {
  animation: oltSpin 300ms linear 1ms backwards,
             oltFade 180ms linear 1ms backwards;
}
/* the words settle a beat after the picture, as they do on the site */
.anim-expand.delay { animation-delay: 160ms, 160ms; }

@media (prefers-reduced-motion: reduce) {
  .anim-shape, .anim-expand, .anim-spin { animation: none; }
}

/* ---------------------------------------------------------------
   SELECTED WORK â€” page title, matching the About and Contact headers
   --------------------------------------------------------------- */
.index { flex-direction: column; }
.index-inner { width: 100%; max-width: 1000px; }
.index-title {
  margin: 0 0 44px;
  font-family: Manrope, Poppins, Arial, sans-serif;
  font-size: 42.72px; font-weight: 700; line-height: 1.12;
  letter-spacing: -.01em; text-transform: uppercase; color: var(--paper);
}
@media (max-width: 1100px) {
  .index-title { font-size: 32px; margin-bottom: 30px; }
}

/* The grid below centres, so the title centres with it â€” a
   left-aligned title over a centred grid reads as a misalignment
   rather than a choice. Switch to text-align:left if you prefer it
   ranged left like the About and Contact headers. */
.index-title { text-align: center; }

/* ---------------------------------------------------------------
   INDEX AT LARGER SIZES
   The tiles were fixed at 224px, so on a wide desktop the marks sat
   marooned in the middle of the section. Everything geometric in a
   tile is now a multiple of --k, so the logos and their hover
   thumbnails grow with the viewport while the captions stay put â€”
   the type is already at its intended size and scaling it would
   just make the page look zoomed.
   --------------------------------------------------------------- */
/* A row of four needs 4 tiles + 3 gaps = k x 998px. The rail is
   17vw, so the room available is W - 17vw - 80. Each step stays
   under that. */
:root { --k: 1; }
@media (min-width: 1440px) { :root { --k: 1.10; } }   /* needs 1098 of 1115 */
@media (min-width: 1700px) { :root { --k: 1.30; } }   /* needs 1297 of 1331 */
@media (min-width: 2000px) { :root { --k: 1.55; } }   /* needs 1547 of 1580 */
@media (min-width: 2400px) { :root { --k: 1.85; } }   /* needs 1846 of 1912 */
@media (min-width: 2800px) { :root { --k: 2.20; } }   /* needs 2196 of 2280 */

.shelf {
  max-width: calc(1000px * var(--k));
  gap: calc(26px * var(--k)) calc(34px * var(--k));
}
.index-inner { max-width: calc(1000px * var(--k)); }
.tile   { width: calc(224px * var(--k)); height: calc(200px * var(--k)); }
.mark   { height: calc(104px * var(--k)); }
.thumb  { width: calc(218px * var(--k)); height: calc(138px * var(--k)); }
.cap    { margin-top: calc(26px * var(--k)); }

/* the crop window carries its size inline in px, so it is scaled
   rather than resized â€” the hover shrink multiplies through it */
.crop { transform: scale(var(--k)); transform-origin: center; }
.tile:hover .crop, .tile:focus-visible .crop { transform: scale(calc(var(--k) * .92)); }

/* ==================================================================
   SCALE WITH THE VIEWPORT
   The rail was pinned at 245px and the type at fixed pixels, which is
   right at 1440 but leaves everything marooned on a large monitor.
   Wix's own rail is ~17% of viewport width (245/1440, 228/1349), so
   these track that ratio and clamp at both ends: identical to the old
   values around 1440, larger beyond it, never smaller on a laptop.
   ================================================================== */
:root {
  --rail:      clamp(148px, 17vw,   440px);
  --inset:     clamp(20px,  2.57vw, 66px);
  --nav-size:  clamp(9px,   0.72vw, 18px);
  --sub-size:  clamp(8.5px, 0.66vw, 16px);
  --logo-h:    clamp(28px,  2.71vw, 70px);
  --social:    clamp(18px,  1.6vw,  40px);
  --cap-name:  clamp(13px,  1.09vw, 28px);
  --cap-place: clamp(9px,   0.63vw, 16px);
  --title:     clamp(30px,  2.97vw, 76px);
}

/* the rule must reach the bottom whatever the window height â€”
   top/bottom beats height:100vh, which mobile browser chrome lies about */
.rail { top: 0; bottom: 0; height: auto; }

.rail-logo { height: var(--logo-h); top: calc(var(--inset) * .81); left: var(--inset); }
.rail-nav  { top: calc(var(--inset) * 3.7); left: var(--inset); }
.rail-nav a, .rail-nav button {
  font-size: var(--nav-size);
  line-height: 1.4;
  padding-bottom: calc(var(--nav-size) * 1.78);
}
.rail-row { padding-bottom: calc(var(--nav-size) * 1.78); gap: calc(var(--nav-size) * .9); }
.rail-row a, .rail-row button { padding-bottom: 0; }
.chev { width: calc(var(--nav-size) * .78); height: calc(var(--nav-size) * .78); }
.rail-list button {
  font-size: var(--sub-size);
  line-height: 1.4;
  padding-bottom: calc(var(--sub-size) * 1.06);
}
.rail-social {
  left: calc(var(--rail) - var(--social) * 2 - var(--inset) * .6);
  gap: calc(var(--social) * .74);
}
.rail-social a { width: var(--social); height: var(--social); }

/* project captions */
.caption b { font-size: var(--cap-name);  line-height: 1.6; }
.caption i { font-size: var(--cap-place); line-height: 1.6; }

/* page headings and the index title */
.split-body h1, .index-title { font-size: var(--title); }

/* the next cue */
.cue-label, .cue-name { font-size: var(--sub-size); }
.cue-rule { height: calc(var(--sub-size) * 8); }

/* ---------------------------------------------------------------
   THE ARC MARK â€” four times the size, opening from almost nothing
   --------------------------------------------------------------- */
.arc {
  width: min(1072px, 56vw);     /* 4x the previous 268px, capped so it
                                   never swamps a laptop screen */
  right: calc(var(--inset) * .4);
}
/* opens from almost nothing so the growth carries the whole entrance */
@keyframes oltSpin { from { transform: rotate(-360deg) scale(.05); } }

/* The rail is fixed, and a fixed layer next to compositing neighbours
   can leave a stale copy of itself painted during a resize â€” which is
   the sidebar appearing twice. Giving it its own layer stops the
   browser re-using a stale one. */
.rail {
  will-change: transform;
  transform: translateZ(0);
  backface-visibility: hidden;
}

/* the split pages need room for a much larger mark */
.split { padding-bottom: 180px; }
@media (max-width: 1100px) {
  .arc { width: min(560px, 70vw); }
  .split { padding-bottom: 130px; }
}

/* ==================================================================
   FULL-SCREEN, AND TYPE THAT KEEPS UP
   Studio Mellone fills the viewport with object-fit:cover at every
   size and lets tracking â€” not size â€” give small type its presence
   (10px with 3px of letter-spacing). Both ideas are borrowed here,
   except the type also grows on a large monitor, which theirs does
   not: theirs stays 10px at 2560 and reads as tiny.
   ================================================================== */
:root {
  --nav-size: clamp(11px, 0.80vw, 22px);
  --sub-size: clamp(10px, 0.70vw, 18px);
}

/* the home header was never scaled â€” it kept its 1440 pixel values */
.topbar { padding: calc(var(--inset) * .52) calc(var(--inset) * 1.4) 0 calc(var(--inset) * .65); }
.topbar img { height: var(--logo-h); }
.topbar nav { gap: calc(var(--nav-size) * 2.7); padding-top: calc(var(--logo-h) * .28); }
.topbar nav a { font-size: var(--nav-size); letter-spacing: .12em; }

/* tracking gives small nav type its weight, as on the reference */
.rail-nav a, .rail-nav button { letter-spacing: .12em; }
.rail-list button { letter-spacing: .08em; }

/* --- every page fills the screen ------------------------------- */
html, body { min-height: 100%; }
/* the hero must be measured against the viewport â€” height:100% here
   resolves against an auto-height parent and collapses to nothing.
   dvh follows mobile browser chrome as it hides; vh is the fallback. */
.hero { width: 100%; height: 100vh; height: 100dvh; }
.hero figure, .hero img, .hero video { width: 100%; height: 100%; }
.hero img, .hero video { object-fit: cover; }

/* About and Contact: one screen, photograph filling its column */
.split {
  min-height: 100vh;
  align-content: center;
  padding-top: calc(var(--inset) * 2.4);
  padding-bottom: calc(var(--inset) * 3.2);
}
.split-photo { display: flex; align-items: center; }
.split-photo img { max-height: 76vh; width: 100%; object-fit: cover; }

/* ---------------------------------------------------------------
   ABOUT & CONTACT AT SCALE
   The columns were fixed at 400/410px and the body at 10.68px, so on
   a large monitor the page was a small block adrift in black. These
   are the same values expressed as a share of the viewport, so 1440
   is unchanged and everything above it grows.
   --------------------------------------------------------------- */
:root {
  --body-size: clamp(10.68px, 0.74vw, 19px);
  --form-size: clamp(10px,   0.70vw, 18px);
}
.split {
  grid-template-columns: clamp(280px, 27.8vw, 720px) clamp(300px, 28.5vw, 760px);
  gap: 0 clamp(30px, 4.3vw, 110px);
  padding-left: clamp(24px, 9.7vw, 250px);
  padding-right: clamp(24px, 2.8vw, 72px);
}
.split-body p { font-size: var(--body-size); line-height: 1.6; }
.split-body .intro { margin-bottom: calc(var(--body-size) * 3); }

.split form { max-width: none; gap: clamp(10px, 0.85vw, 20px); }
.split .row { gap: clamp(10px, 0.85vw, 20px); }
.split form input, .split form textarea {
  font-size: var(--form-size);
  padding: calc(var(--form-size) * .75) calc(var(--form-size) * .95);
}
.split form textarea { min-height: clamp(74px, 9vh, 190px); }
.split form button[type="submit"] {
  font-size: var(--form-size);
  max-width: clamp(187px, 15vw, 380px);
  padding: calc(var(--form-size) * 1.1) 0;
}
.split .details { gap: clamp(14px, 1.4vw, 34px) clamp(28px, 3vw, 76px); }
.split .details .k { font-size: calc(var(--form-size) * .82); }
.split .details a, .split .details .v { font-size: var(--form-size); }

/* The photograph must fit what is left after the padding, or the page
   grows past one screen and the arc pushes a scrollbar with it. */
.split { overflow: hidden; }
.split-photo img { max-height: calc(100vh - var(--inset) * 5.9); }

/* ---------------------------------------------------------------
   HOME HERO â€” pinned to the viewport itself
   Sizing it through its parents means any wrapper that misreports
   its height leaves a band of black. Fixed to all four edges, the
   slideshow is the viewport, at every width and height, with no
   ancestor able to get it wrong.
   --------------------------------------------------------------- */
body.home { overflow: hidden; }
body.home .content { margin: 0; width: 100%; }
body.home .hero {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  width: auto; height: auto;
  z-index: 0;
}
body.home .hero figure { position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: auto; height: auto; }
body.home .hero img,
body.home .hero video { width: 100%; height: 100%; object-fit: cover; display: block; }
body.home .topbar { z-index: 10; }

/* ---------------------------------------------------------------
   ULTRAWIDE (3440 and beyond)
   The caps were set for a 2560 monitor, so on a 3440 display the
   rail fell back to 12.8% instead of its 17% and the logo grid
   stopped growing. These lift the ceilings and add a matching
   scale step: at 3440 the four-across row needs k x 998 = 2595 of
   the 2775 available.
   --------------------------------------------------------------- */
:root {
  --rail:      clamp(148px, 17vw,   600px);
  --inset:     clamp(20px,  2.57vw, 90px);
  --nav-size:  clamp(11px,  0.80vw, 26px);
  --sub-size:  clamp(10px,  0.70vw, 22px);
  --logo-h:    clamp(28px,  2.71vw, 96px);
  --social:    clamp(18px,  1.6vw,  52px);
  --cap-name:  clamp(13px,  1.09vw, 36px);
  --cap-place: clamp(9px,   0.63vw, 21px);
  --title:     clamp(30px,  2.97vw, 100px);
  --body-size: clamp(10.68px, 0.74vw, 25px);
  --form-size: clamp(10px,    0.70vw, 23px);
}
@media (min-width: 3200px) { :root { --k: 2.60; } }   /* needs 2595 of 2775 */

/* ---------------------------------------------------------------
   THE ARC MARK â€” pinned to the bottom-right corner, always clear
   of the text.
   It was sized at 56vw, which is why it ploughed through the body
   copy. It now scales with the viewport but stays bounded, and the
   page reserves exactly its height underneath, so the two can never
   meet at any width. arc.png is 893x509, so height = width / 1.755.
   --------------------------------------------------------------- */
:root { --arc: clamp(150px, 21vw, 440px); }

.arc {
  position: absolute;
  right: 0; bottom: 0;
  width: var(--arc);
  height: auto;
  opacity: .95;
  pointer-events: none; user-select: none;
}

/* the reserved band below the content: the mark's own height plus a
   gap, so the last line of text always clears its top edge */
.split {
  padding-bottom: calc(var(--arc) * 0.57 + 56px);
}

@media (max-width: 1100px) {
  :root { --arc: clamp(130px, 26vw, 300px); }
}

/* ==================================================================
   ABOUT & CONTACT â€” exactly one screen, never scrolling
   min-height:100vh plus a reserved band for the mark necessarily
   exceeds the viewport, which is what produced the scrollbar. These
   pages are now locked to the viewport height and everything inside
   is sized against BOTH axes, so a short wide window shrinks the type
   and the mark together rather than clipping either.
   ================================================================== */
body.split-page { height: 100vh; height: 100dvh; overflow: hidden; }

:root {
  /* the mark answers to whichever axis is tighter */
  --arc: clamp(110px, min(20vw, 25vh), 420px);
  --split-fs: clamp(8.5px, min(0.72vw, 1.2vh), 15px);
  --split-title: clamp(22px, min(2.9vw, 5.2vh), 72px);
}

.split {
  height: 100vh; height: 100dvh;
  min-height: 0;
  overflow: hidden;
  align-items: center;
  align-content: center;
  grid-template-columns: minmax(0, clamp(220px, 25vw, 560px))
                         minmax(0, clamp(280px, 30vw, 660px));
  gap: 0 clamp(28px, 3.4vw, 76px);
  padding-top:    clamp(28px, 4.5vh, 80px);
  /* the band the mark occupies; fitArc() sets --arc to fill it */
  padding-bottom: calc(var(--arc) * 0.57 + 20px);
  padding-left:   clamp(28px, 6vw, 140px);
  padding-right:  clamp(20px, 2.6vw, 60px);
}

/* the photograph fills its column but never forces the page taller */
.split-photo { height: 100%; min-height: 0; display: flex; align-items: center; }
.split-photo img { width: 100%; height: 100%; max-height: 100%; object-fit: cover; }

.split-body { min-height: 0; }
.split-body h1 {
  font-size: var(--split-title);
  margin-bottom: clamp(10px, 2.2vh, 26px);
}
.split-body p {
  font-size: var(--split-fs);
  line-height: 1.62;
  margin-bottom: clamp(8px, 1.6vh, 18px);
}
.split-body p:last-child { margin-bottom: 0; }

/* the form shrinks with the page rather than pushing it open */
.split form { gap: clamp(7px, 1.2vh, 12px); margin-bottom: clamp(12px, 2.4vh, 30px); }
.split .row { gap: clamp(7px, 1.2vh, 12px); }
.split form input, .split form textarea {
  font-size: var(--split-fs);
  padding: clamp(7px, 1.3vh, 13px) clamp(9px, 1vw, 14px);
}
.split form textarea { height: clamp(56px, 11vh, 120px); }
.split form button { font-size: var(--split-fs); padding: clamp(8px, 1.5vh, 15px); }
.split .details { font-size: var(--split-fs); gap: clamp(6px, 1.4vh, 16px) clamp(16px, 2vw, 40px); }
.split .details dt { font-size: calc(var(--split-fs) * .84); }

/* ---------------------------------------------------------------
   The columns stretch to the content box rather than centring, so
   the text's bottom edge is simply padding-top + its own height.
   That makes the space left over for the mark exactly computable,
   which is what fitArc() in site.js then fills.
   --------------------------------------------------------------- */
.split { align-items: stretch; align-content: stretch; }
.split-body { align-self: start; }

/* ---------------------------------------------------------------
   PAN HINT â€” chevrons telling you the strip moves sideways.
   Pulsing rather than sliding, so it reads as an invitation without
   competing with the photographs. It retires for good the moment
   that strip is moved: a hint that keeps nagging after you have
   understood it is just noise.
   --------------------------------------------------------------- */
.pan-hint {
  position: absolute; z-index: 4;
  right: clamp(14px, 1.6vw, 34px);
  top: 50%; transform: translateY(-50%);
  display: flex; gap: 2px;
  pointer-events: none;
  opacity: 1;
  transition: opacity .5s ease;
}
.pan-hint.done { opacity: 0; }
.pan-hint span {
  display: block;
  width: clamp(10px, 1vw, 18px); height: clamp(10px, 1vw, 18px);
  border-right: 2px solid #fff; border-top: 2px solid #fff;
  transform: rotate(45deg);
  animation: oltPan 1.5s ease-in-out infinite;
  filter: drop-shadow(0 0 4px rgba(0,0,0,.55));
}
.pan-hint span:nth-child(2) { animation-delay: .18s; }
@keyframes oltPan {
  0%, 100% { opacity: .25; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .pan-hint span { animation: none; opacity: .8; }
}

/* ---------------------------------------------------------------
   TAGLINE â€” the red line beneath each project's name
   --------------------------------------------------------------- */
.caption em {
  display: block; font-style: normal;
  margin-top: calc(var(--cap-place) * .5);
  font-size: var(--sub-size); line-height: 1.5;
  letter-spacing: .06em;
  color: var(--signal);
  max-width: 78ch;
}

/* ---------------------------------------------------------------
   TAGLINE â€” pinned bottom-left, on the same line as the NEXT cue.
   Both sit 26px off the bottom, so their baselines agree across the
   width of the pane. Absolute inside .pane, which is sticky and so
   already a containing block; when a pane is the stuck one its
   bottom is the viewport bottom, which is what the fixed cue is
   measured from too.
   --------------------------------------------------------------- */
.caption em {
  position: absolute;
  left: var(--cap-x);
  bottom: 26px;
  margin: 0;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .12em;          /* caps need more air than sentence case */
  max-width: min(70ch, 56%);      /* stays clear of the cue on the right */
}

/* project name in caps; the location line below it stays as set */
.caption b { text-transform: uppercase; letter-spacing: .02em; }

/* ---------------------------------------------------------------
   THE LAST PROJECT â€” the cue turns back on itself
   On Gables there is nothing below, so instead of fading out the cue
   inverts: the caret flips to the top of the rule, the nudge travels
   upward, and the red line reads ALL WORK. Clicking it returns to
   the index rather than doing nothing.
   --------------------------------------------------------------- */
.cue.up { opacity: 1; pointer-events: auto; }
.cue.up .cue-rule { animation: nudgeUp 2s ease-in-out infinite; }
.cue.up .cue-rule::after {
  top: -1px; bottom: auto;
  border-left: 1px solid var(--paper);
  border-bottom: 1px solid var(--paper);
  transform: rotate(135deg);      /* the same corner, turned to point up */
}
@keyframes nudgeUp {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}
@media (prefers-reduced-motion: reduce) { .cue.up .cue-rule { animation: none; } }

/* ==================================================================
   SELECTED WORK ENTRANCES
   Each project now arrives with the same Expand the About and Contact
   copy uses â€” scale .8 with a matching fade, 1200ms linear â€” rather
   than the plain fade it had. Applied to the photo box (whose border
   is the rule under every project), the title, the location and the
   red caption, with the words a beat behind the picture, exactly as
   .anim-expand.delay does on the other pages.
   ================================================================== */
.js-reveal .pane .holder,
.js-reveal .pane .caption b,
.js-reveal .pane .caption i,
.js-reveal .pane .caption em { opacity: 0; }

.js-reveal .pane.revealed .holder {
  animation: oltExpand 1200ms linear 1ms forwards,
             oltFade   1200ms linear 1ms forwards;
}
.js-reveal .pane.revealed .caption b,
.js-reveal .pane.revealed .caption i,
.js-reveal .pane.revealed .caption em {
  animation: oltExpand 1200ms linear 160ms forwards,
             oltFade   1200ms linear 160ms forwards;
}

/* the title, location and caption grow about their own left edge, so
   the block stays ranged left instead of drifting inward */
.js-reveal .pane .caption b,
.js-reveal .pane .caption i,
.js-reveal .pane .caption em { transform-origin: left center; }

@media (prefers-reduced-motion: reduce) {
  .js-reveal .pane .holder,
  .js-reveal .pane .caption b,
  .js-reveal .pane .caption i,
  .js-reveal .pane .caption em { opacity: 1; animation: none; }
}

/* ---------------------------------------------------------------
   One rule, not a box. The photo area was outlined on all four
   sides; only the bottom edge â€” the line between the images and the
   text â€” is kept, in exactly the same place.
   --------------------------------------------------------------- */
.holder {
  border: 0;
  border-bottom: 1px solid var(--rule);
}

/* The caption still carried a full box. Its left edge landed on the
   rail's own border, which is the doubled vertical line, and its top
   edge doubled the rule below the photographs. The single horizontal
   rule now comes only from .holder's bottom border. */
.caption { border: 0; }

/* ---------------------------------------------------------------
   The social glyphs move to the foot of the rail, ranged right and
   sitting on the same line as the red text in the opposite corner.
   Both are 26px off the bottom: the rail is fixed top-to-bottom and
   the cue is fixed to the viewport, so that resolves to one line.
   --------------------------------------------------------------- */
.rail-social {
  left: auto;
  right: var(--inset);
  top: auto;
  bottom: 26px;
  align-items: flex-end;
}

/* ---------------------------------------------------------------
   PAGE TITLES â€” PP Agrandir Grand Heavy
   Not a Google font, so it is served from the site itself. The OTF
   is the only weight needed; Manrope stays behind it as the fallback
   for the moment before it loads.
   --------------------------------------------------------------- */
@font-face {
  font-family: "PP Agrandir";
  src: url("/assets/fonts/PPAgrandir-GrandHeavy.otf") format("opentype");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

.index-title,
.split-body h1 {
  font-family: "PP Agrandir", Manrope, Poppins, Arial, sans-serif;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -.005em;   /* Grand Heavy is already tightly fitted */
}

/* ---------------------------------------------------------------
   PAGE TITLES â€” same face as the red project captions
   Poppins at 400 with the captions' tracking, kept at the large
   title size and set in white. This supersedes the PP Agrandir rule
   above; the @font-face is left in place but nothing references it,
   so the browser no longer downloads it.
   --------------------------------------------------------------- */
.index-title,
.split-body h1 {
  font-family: Poppins, Arial, Helvetica, sans-serif;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: .12em;        /* matches .caption em and .cue-name */
  color: var(--paper);
}

/* ==================================================================
   HOME INTRO â€” the mark centred on black, then away
   Home page only. Timings use the 750ms ease-out fade that Ferrari's
   page applies to its hero imagery, which is the dominant fade on
   that site; the hold between the two fades is my own choice.
   Tune all three with the variables below.
   ================================================================== */
:root {
  --intro-in:   750ms;    /* mark fades up      */
  --intro-hold: 700ms;    /* mark sits still    */
  --intro-out:  750ms;    /* black lifts away   */
}

.olt-intro {
  position: fixed; inset: 0; z-index: 9999;
  display: flex; align-items: center; justify-content: center;
  background: var(--ink);
  animation: introLift var(--intro-out) ease-out
             calc(var(--intro-in) + var(--intro-hold)) forwards;
}
.olt-intro img {
  width: clamp(85px, 10vw, 200px);   /* half the previous size */
  height: auto; display: block;
  opacity: 0;
  animation: introMark var(--intro-in) ease-out both;
}

@keyframes introMark { from { opacity: 0; } to { opacity: 1; } }
/* ends unclickable and out of the way, not merely transparent */
@keyframes introLift {
  from { opacity: 1; }
  to   { opacity: 0; visibility: hidden; pointer-events: none; }
}

/* an intro is the one thing that must never outstay its animation â€”
   if motion is off, it is simply not shown */
@media (prefers-reduced-motion: reduce) { .olt-intro { display: none; } }

/* The mark is centred on the red "o", not on the image box. Measured
   off the file: the o's centre sits at 35.56% across the canvas and
   exactly 50% down, so the artwork shifts right by the 14.444%
   difference and needs no vertical correction. Expressed as a
   percentage of the image, so it holds at every size. */
.olt-intro img { transform: translateX(14.444%); }

/* arc.png carries 418px of empty canvas on its right â€” 46.81% of the
   file â€” so pinning the image box to the corner still left the mark
   short of it. Shifting by that exact fraction puts the artwork's own
   edge on the corner; the empty part runs off-screen and .split's
   overflow:hidden clips it. There is no bottom padding to correct. */
.arc { transform: translateX(46.81%); }

/* ---------------------------------------------------------------
   THE ARC'S ENTRANCE â€” opens tiny and turns in like a wheel
   Two corrections were needed. The corner fix put a translateX on
   .arc, and a transform animation replaces the whole property â€” so
   the translate has to be carried inside the keyframes or the mark
   slides across as it spins. And the artwork sits in the left 53% of
   the file, so the box's centre is not the wheel's: the origin is set
   to the ring's own centre (26.54% across, 50% down, measured off the
   alpha channel) or it orbits instead of spinning in place.
   --------------------------------------------------------------- */
.arc { transform-origin: 26.54% 50%; }

@keyframes oltSpin {
  from { transform: translateX(46.81%) rotate(-360deg) scale(.05); }
  to   { transform: translateX(46.81%) rotate(0deg)    scale(1); }
}

/* ==================================================================
   PRESS
   Rows of: the outlet's mark on the left, the story on the right.
   Hovering anywhere on a row â€” mark or text â€” brings up a darkened
   photograph behind it, the same device the Selected Work index uses
   on its logos, at the same 62% shade.
   ================================================================== */
.press {
  min-height: 100vh;
  padding: clamp(70px, 9vh, 130px) 0 clamp(60px, 8vh, 110px);
}
.press-title {
  margin: 0 0 clamp(34px, 5vh, 68px);
  text-align: center;
}

.press-list { display: block; }

.press-row {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 40%) minmax(0, 60%);
  align-items: center;
  min-height: clamp(180px, 24vh, 300px);
  border-top: 1px solid var(--rule);
  isolation: isolate;
  cursor: pointer;
  color: inherit;
  text-decoration: none;
}
.press-row:last-child { border-bottom: 1px solid var(--rule); }

/* the darkened photograph, revealed on hover of the whole row */
.press-shade {
  position: absolute; inset: 0; z-index: 0;
  background-size: cover; background-position: center;
  opacity: 0; pointer-events: none;
  transition: opacity .4s ease, transform .4s ease;
  transform: scale(1.04);
}
.press-shade::after { content: ""; position: absolute; inset: 0; background: var(--shade); }
.press-row:hover .press-shade,
.press-row:focus-visible .press-shade { opacity: 1; transform: scale(1); }

/* the outlet's mark, cropped to its artwork so the three sit level */
.press-mark {
  position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  padding: clamp(20px, 3vh, 40px);
}
.press-mark .crop { transition: transform .4s ease; }
.press-row:hover .press-mark .crop,
.press-row:focus-visible .press-mark .crop { transform: scale(.94); }

.press-body {
  position: relative; z-index: 1;
  padding: clamp(20px, 3vh, 40px) clamp(24px, 3vw, 64px) clamp(20px, 3vh, 40px) 0;
}

/* date â€” rule â€” outlet, on one line, in the studio red */
.press-meta {
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: 0 clamp(10px, 1.4vw, 24px);
  margin-bottom: clamp(12px, 1.8vh, 22px);
  font-size: var(--sub-size); letter-spacing: .12em;
  text-transform: uppercase; color: var(--signal);
}
.press-meta i { font-style: normal; }
.press-meta span { height: 1px; background: var(--rule); display: block; }

.press-headline {
  margin: 0 0 clamp(12px, 1.8vh, 22px);
  font-size: var(--cap-place); line-height: 1.55;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--paper); font-weight: 400;
  max-width: 52ch;
}
.press-more {
  font-size: var(--cap-place); letter-spacing: .12em;
  text-transform: uppercase; color: var(--paper);
  transition: color .25s ease;
}
.press-row:hover .press-more { color: var(--signal); }
.press-row:focus-visible { outline: 1px solid var(--paper); outline-offset: -3px; }

@media (max-width: 800px) {
  .press-row { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .press-body { padding: 0 20px clamp(20px, 3vh, 40px); }
}

/* the shade rests at scale(1.04) so it settles inward on hover; the
   row has to clip that or the extra 4% widens the page */
.press-row { overflow: hidden; }

/* ---------------------------------------------------------------
   The index was centring its whole block vertically, which dropped
   the title into the middle of the screen â€” hence it reading oddly.
   It now starts from the top on the same padding Press uses, so both
   page titles land on one line, with the logo grid taking the space
   that remains beneath.
   --------------------------------------------------------------- */
.index {
  justify-content: flex-start;
  align-items: stretch;
  padding-top: clamp(70px, 9vh, 130px);      /* identical to .press */
  padding-bottom: clamp(60px, 8vh, 110px);
}
.index-inner {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0 auto;
}
.index-title { margin-bottom: clamp(34px, 5vh, 68px); }   /* identical to .press-title */
/* the marks take the room left under the title */
.index-inner .shelf { flex: 1 1 auto; align-content: center; }

/* ==================================================================
   FOOTER â€” the band from the live Contact page
   Measured off oltrelimiti.com/blank-1-1: rgb(23,30,32), 191px tall,
   spanning the content area beside the rail, with three items in
   Poppins 15px/500 white on one line. Sizes scale here rather than
   sitting at fixed pixels, as everywhere else on the rebuild.
   ================================================================== */
.footer-band {
  background: #171E20;
  min-height: clamp(120px, 15vh, 200px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: center;
  padding: clamp(24px, 3vh, 40px) 12%;
  gap: clamp(12px, 2vw, 40px);
  margin-top: clamp(40px, 6vh, 90px);
}
.footer-band a,
.footer-band span {
  display: block;
  text-align: center;
  font-size: clamp(11px, 0.81vw, 18px);
  font-weight: 500;
  line-height: 1.5;
  color: var(--paper);
  transition: color .25s ease;
}
.footer-band a:hover { color: var(--signal); }
.footer-band a:focus-visible { outline: 1px solid var(--paper); outline-offset: 4px; }

@media (max-width: 800px) {
  .footer-band {
    grid-template-columns: minmax(0, 1fr);
    padding: clamp(24px, 4vh, 40px) 20px;
    gap: 14px;
  }
}

/* ---------------------------------------------------------------
   FOOTER OVERLAP
   The press content scrolls normally until its foot reaches the
   bottom of the window, then stops dead; the footer then travels up
   over it on its own.
   position:sticky with bottom:0 is what pins a tall element by its
   lower edge â€” it scrolls until that edge meets the viewport floor
   and holds there, which is exactly the "scroll stops completely"
   moment. The footer follows in normal flow on a higher layer, so it
   rides over the frozen content instead of pushing it.
   --------------------------------------------------------------- */
.press {
  position: sticky;
  bottom: 0;
  z-index: 1;
}
.footer-band {
  position: relative;
  z-index: 2;              /* over the pinned content, not under it */
  margin-top: 0;           /* no gap â€” it arrives straight over the foot */
}

/* bottom-sticky does not engage once an element is taller than the
   viewport â€” verified in isolation, so it is a browser constraint,
   not this markup. Sticking by a negative `top` is the equivalent
   that does work: the element scrolls until its foot meets the
   viewport floor, then holds. site.js sets the value, since it
   depends on the content's own height. */
.press { position: sticky; bottom: auto; z-index: 1; }

/* ---------------------------------------------------------------
   PRESS â€” refinements
   --------------------------------------------------------------- */

/* the photograph belongs behind the mark only, not the copy.
   The grid is 40/60, so the shade stops at the column boundary. */
.press-shade { inset: 0 60% 0 0; }
@media (max-width: 800px) { .press-shade { inset: 0; } }

/* the rule between the date and the source takes the studio red */
.press-meta span { background: var(--signal); }

/* room at the foot of the list so the footer rides over empty black
   rather than over the last story */
.press { padding-bottom: clamp(210px, 24vh, 320px); }

/* the footer is separated from the content it covers by a rule */
.footer-band { border-top: 1px solid var(--rule); }

/* ---------------------------------------------------------------
   ABOUT & CONTACT â€” entrances at the mark's speed
   The copy and photograph were opening over 1200ms while the mark
   turned in over 300ms, so the page finished assembling long after
   the mark had landed. Scoped to these two pages: Press and the
   Selected Work index keep their slower reveal.
   --------------------------------------------------------------- */
body.split-page .anim-shape  { animation-duration: 300ms, 180ms; }
body.split-page .anim-expand { animation-duration: 300ms, 180ms; }
body.split-page .anim-expand.delay { animation-delay: 60ms, 60ms; }

/* The marks were centring in the space under the title, which left
   them 70px below the middle of the screen with a pool of dead space
   beneath. Deepening the lower padding shrinks that box from the
   bottom and lifts the grid onto the viewport's centre line, while
   the title stays put on the Press title's line. */
.index { padding-bottom: clamp(150px, 19vh, 300px); }

/* The shade rests at scale(1.04) and settles to 1 on hover. Scaled
   about its centre that pushed its right edge 12px into the copy
   column mid-transition; anchoring the growth to the right edge keeps
   that boundary fixed and sends the extra width leftward, where the
   row's overflow clips it. */
.press-shade { transform-origin: right center; }

/* ---------------------------------------------------------------
   ABOUT & CONTACT â€” one speed for everything: 600ms
   The heading's wipe, the photograph and copy expanding, and the
   mark's turn all run for the same 600ms, fades included, so nothing
   outpaces or lags anything else. The copy's stagger is scaled to
   match. Press and the Selected Work index are untouched.
   --------------------------------------------------------------- */
body.split-page .anim-shape,
body.split-page .anim-expand,
body.split-page .anim-spin {
  animation-duration: 600ms, 600ms;
}
body.split-page .anim-expand.delay { animation-delay: 120ms, 120ms; }

/* ---------------------------------------------------------------
   ABOUT & CONTACT â€” the heading matches Selected Work and Press
   The heading sat inside the wrapper that scales from 0.8, so it grew
   as well as wiped. It now lives outside that wrapper, leaving only
   the circle wipe, and it is excluded from these pages' 600ms
   override so it runs at the 1200/960 the other two page titles use.
   --------------------------------------------------------------- */

/* The heading runs at the page's own speed, like everything else on
   About and Contact. It keeps the structural fix from before â€” it
   sits outside the wrapper that scales â€” so it wipes without growing.
   This supersedes the 1200/960 exception above. */
body.split-page .split-body h1.anim-shape {
  animation-duration: 600ms, 600ms;
  animation-delay: 1ms, 1ms;
}

/* ---------------------------------------------------------------
   SELECTED WORK & PRESS â€” body content at 1000ms
   Titles keep their 1200/960; only the content beneath them speeds
   up. About and Contact are unaffected â€” their own 600ms rule is
   more specific.
   --------------------------------------------------------------- */
.anim-expand { animation-duration: 1000ms, 1000ms; }

.js-reveal .pane.revealed .holder {
  animation: oltExpand 1000ms linear 1ms forwards,
             oltFade   1000ms linear 1ms forwards;
}
.js-reveal .pane.revealed .caption b,
.js-reveal .pane.revealed .caption i,
.js-reveal .pane.revealed .caption em {
  animation: oltExpand 1000ms linear 160ms forwards,
             oltFade   1000ms linear 160ms forwards;
}

/* the photograph stops short of the copy, leaving a clear gap */
.press-shade { inset: 0 63% 0 0; }
@media (max-width: 800px) { .press-shade { inset: 0; } }

/* ---------------------------------------------------------------
   One value for the footer's height and the blank band above it, so
   the footer's top rule lands exactly on the last story's bottom
   rule when it is fully open. Deriving both from --footer-h removes
   the timing problem entirely: nothing is measured, so nothing can
   be measured too early.
   --------------------------------------------------------------- */
:root { --footer-h: clamp(120px, 15vh, 200px); }
.footer-band { min-height: var(--footer-h); }
.press { padding-bottom: var(--footer-h); }

/* stacked on a narrow screen the footer grows past its minimum, so
   the band grows with it */
@media (max-width: 800px) {
  :root { --footer-h: 210px; }
}

/* ==================================================================
   INDEX LAYOUT â€” final
   Wide: flex-wrap, centred. Four marks, then three centred beneath
   them â€” staggered against the row above, as it was originally. A
   grid cannot do that; its columns would line the second row up with
   the first.
   The breakpoint sits at 1360px, which is where four marks stop
   fitting the column (4x224 + 3x34 = 998px against roughly 1050px of
   room). Below it, straight to two across â€” no three-across stage.
   ================================================================== */
.shelf {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  align-content: center;
  gap: calc(26px * var(--k)) calc(34px * var(--k));
  width: 100%;
  max-width: calc(1000px * var(--k));
  margin: 0 auto;
}
.tile {
  width: calc(224px * var(--k));
  max-width: none;
  height: auto;                 /* content decides height â€” no collisions */
  padding: 0;
}

/* crowded: two across, four rows, Gables alone */
@media (max-width: 1360px) {
  .shelf {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    justify-items: center;
    gap: clamp(22px, 4vh, 46px) clamp(16px, 3vw, 40px);
    max-width: 620px;
  }
  .index-inner { max-width: 620px; }
  .tile { width: 100%; max-width: 250px; padding: 6px 0; }
}

/* very narrow: one per row */
@media (max-width: 620px) {
  .shelf { grid-template-columns: minmax(0, 1fr); max-width: 300px; }
  .index-inner { max-width: 300px; }
}


/* ==================================================================
   HOME INTRO — the mark turns in, then the word arrives
   The three artworks align from the top-right corner of their
   canvases, which is what lets the mark and the text be laid over
   each other and reconstruct the full logo exactly. Verified against
   the red "o": the text at 69.3% width, offset 30.7%, lands its o at
   x 85.3–106 y 69.9, against the whole logo's 86–106 y 70.
   The mark turns three times rather than the one it makes on About
   and Contact, and stops in the position it occupies in the finished
   logo — so the word can simply appear beside it with nothing moving.
   ================================================================== */
:root {
  --intro-spin: 600ms;    /* three turns, growing in     */
  --intro-text: 400ms;    /* the word fades up beside it */
  --intro-hold: 400ms;    /* the whole logo sits         */
  --intro-out:  750ms;    /* black lifts away            */
}

.olt-logo {
  position: relative;
  width: clamp(85px, 10vw, 200px);   /* the size it was before */
  aspect-ratio: 270 / 154;
  transform: translateX(14.444%);    /* keeps the red o centred */
}
.olt-intro img {
  position: absolute;
  top: 0;
  width: auto;
  display: block;
  animation: none;                   /* supersedes the old fade */
  transform: none;
}
.olt-mark {
  left: 0;
  width: 100%;
  transform-origin: 26.57% 50%;      /* the ring's own centre */
  animation: introSpin var(--intro-spin) ease-out 1ms both;
}
.olt-text {
  left: 30.7%;
  width: 69.3%;
  animation: introWord var(--intro-text) ease-out var(--intro-spin) both;
}

@keyframes introSpin {
  from { opacity: 0; transform: rotate(-1080deg) scale(.05); }
  to   { opacity: 1; transform: rotate(0deg) scale(1); }
}
@keyframes introWord { from { opacity: 0; } to { opacity: 1; } }

/* the black lifts once the logo is whole */
.olt-intro {
  animation: introLift var(--intro-out) ease-out
             calc(var(--intro-spin) + var(--intro-text) + var(--intro-hold)) forwards;
}

/* `.olt-intro img` is specificity (0,1,1) and was beating the bare
   `.olt-mark` / `.olt-text` classes (0,1,0), so both images fell back
   to width:auto and rendered at their natural pixel size. Qualifying
   them with the parent puts them at (0,2,0), which wins. */
.olt-intro .olt-mark {
  left: 0;
  width: 100%;
  transform-origin: 26.57% 50%;
  animation: introSpin var(--intro-spin) ease-out 1ms both;
}
.olt-intro .olt-text {
  left: 30.7%;
  width: 69.3%;
  animation: introWord var(--intro-text) ease-out var(--intro-spin) both;
}


/* ==================================================================
   INDEX — two across as soon as the wide layout stops fitting
   No intermediate stage: below the 4,3 it goes straight to
   2,2,2,1, with the marks a step larger. --k drives every
   measurement inside a tile, so raising it grows the mark, its hover
   thumbnail and the tile together rather than one at a time.
   ================================================================== */
@media (max-width: 1360px) {
  :root { --k: 1.45; }
  .shelf {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    justify-items: center;
    align-items: start;
    gap: clamp(24px, 4vh, 50px) clamp(14px, 2.5vw, 32px);
    max-width: min(840px, 100%);
  }
  .index-inner { max-width: min(840px, 100%); }
  .tile { width: 100%; max-width: calc(250px * var(--k)); padding: 6px 0; }
  .mark { height: calc(104px * var(--k)); }
}

@media (max-width: 620px) {
  :root { --k: 1.15; }
  .shelf { grid-template-columns: minmax(0, 1fr); max-width: min(340px, 100%); }
  .index-inner { max-width: min(340px, 100%); }
}

/* ---------------------------------------------------------------
   RAIL SUB-HEADERS — the project names under Selected Work
   These are anchors, not buttons, so the older `.rail-list button`
   rules never matched them and they were inheriting the main nav
   size. Sized here to the location line in the project sections
   (--cap-place), and indented so they read as subordinate to the
   headings above them.
   --------------------------------------------------------------- */
.rail-list a,
.rail-list button {
  font-size: var(--cap-place);
  line-height: 1.45;
  letter-spacing: .06em;
  padding-left: calc(var(--nav-size) * 1.1);   /* the indent */
  padding-bottom: calc(var(--cap-place) * 1.15);
  color: rgba(255,255,255,.62);
}
.rail-list a:hover,
.rail-list button:hover,
.rail-list a[aria-current="true"],
.rail-list button[aria-current="true"] { color: var(--signal); }

/* ==================================================================
   MOBILE — no left-hand rail
   The live site drops the rail entirely at 750px and puts a bar
   across the top: mark on the left, hamburger on the right. Tapping
   it opens the same navigation as a full-screen black sheet. That is
   what this rebuilds, and it reuses the rail's own markup rather
   than a second copy of the menu.

   All of it is scoped to .js-mobilenav, which the script only sets
   once the bar exists. If the script never runs the rail simply
   stays as it is — cramped on a phone, but navigable.
   ================================================================== */
.mobar { display: none; }

@media (max-width: 750px) {

  .js-mobilenav .mobar {
    position: fixed; top: 0; left: 0; right: 0; z-index: 60;
    display: flex; align-items: center; justify-content: space-between;
    height: 62px; padding: 0 18px;
    background: var(--ink);
  }
  /* over the hero the bar floats, with no band of its own */
  .js-mobilenav.home-page .mobar { background: transparent; }

  .js-mobilenav .mobar-logo img { height: 30px; width: auto; display: block; }

  .js-mobilenav .mobar-toggle {
    width: 34px; height: 34px; padding: 0;
    background: none; border: 0; cursor: pointer;
    display: grid; place-items: center;
    color: var(--paper);
  }
  .js-mobilenav .mobar-toggle span {
    position: relative;
    display: block; width: 21px; height: 1.5px;
    background: currentColor;
    transition: background .2s ease;
  }
  /* the two other strokes ride on the middle one */
  .js-mobilenav .mobar-toggle span::before,
  .js-mobilenav .mobar-toggle span::after {
    content: ""; position: absolute; left: 0;
    width: 100%; height: 100%;
    background: currentColor;
    transition: transform .28s ease;
  }
  .js-mobilenav .mobar-toggle span::before { top: -6.5px; }
  .js-mobilenav .mobar-toggle span::after  { top:  6.5px; }

  /* open: the middle stroke goes, the outer two cross into an X */
  .js-mobilenav.nav-open .mobar-toggle span { background: transparent; }
  .js-mobilenav.nav-open .mobar-toggle span::before { transform: translateY(6.5px) rotate(45deg); }
  .js-mobilenav.nav-open .mobar-toggle span::after  { transform: translateY(-6.5px) rotate(-45deg); }

  /* the rail, now a sheet that covers the page */
  .js-mobilenav .rail {
    top: 0; left: 0; right: 0; bottom: 0;
    width: 100%; height: 100%;
    border-right: 0;
    z-index: 55;
    padding: 88px 26px 26px;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    opacity: 0; visibility: hidden;
    transform: none;                     /* the desktop translateZ would trap it */
    transition: opacity .28s ease, visibility 0s linear .28s;
  }
  .js-mobilenav.nav-open .rail {
    opacity: 1; visibility: visible;
    transition: opacity .28s ease, visibility 0s;
  }
  .js-mobilenav .rail-logo { display: none; }   /* the bar already shows it */

  /* the menu reads at reading size, not rail size */
  .js-mobilenav .rail-nav {
    position: static;
    display: flex; flex-direction: column; align-items: flex-start;
  }
  .js-mobilenav .rail-nav > a,
  .js-mobilenav .rail-row a {
    font-size: 16px; line-height: 1.3; letter-spacing: .02em;
    padding-bottom: 0;
  }
  .js-mobilenav .rail-nav > a { padding: 13px 0; }
  .js-mobilenav .rail-row { padding: 13px 0; gap: 8px; align-items: center; }
  .js-mobilenav .rail-list a {
    font-size: 12px; letter-spacing: .04em;
    padding-left: 0; padding-bottom: 15px;
  }
  .js-mobilenav .rail-list > div { padding-top: 14px; }

  /* the socials sit at the foot of the sheet, as they do on the live site */
  .js-mobilenav .rail-social {
    position: static;
    margin-top: 44px;
    justify-content: flex-end;
    gap: 18px;
  }
  .js-mobilenav .rail-social a { width: 22px; height: 22px; }

  /* nothing is indented by a rail that is no longer there */
  .js-mobilenav .content { margin-left: 0; }
  .js-mobilenav .page    { padding-top: 92px; }

  /* the sheet owns the screen while it is open */
  .js-mobilenav.nav-open body,
  body.nav-locked { overflow: hidden; }
}

@media (max-width: 750px) {
  /* the sheet is a column so the socials can sit at its foot, where
     the live site puts them, rather than trailing the last link */
  .js-mobilenav .rail { display: flex; flex-direction: column; }
  .js-mobilenav .rail-social { margin-top: auto; }

  /* the pan cue outranks the sheet (z-index 60 against 55) and was
     showing through it. It prompts a scroll that cannot happen while
     the menu is up, so it goes for the duration. */
  .js-mobilenav.nav-open .cue { display: none; }
}

@media (max-width: 750px) {
  /* ---- About & Contact stack --------------------------------------
     Two columns of ~150px is not a layout, it is a pair of gutters.
     The live site drops the split on a phone and stacks it: the
     photograph full-bleed across the top, the heading centred under
     it, then the words at full width, with the mark bleeding off the
     bottom. The desktop rule pins these pages to exactly one
     viewport with overflow hidden; on a phone that would bury most
     of the text, so here the page is allowed its own height. */
  .js-mobilenav .split {
    display: block;
    height: auto; min-height: 0;
    /* clips the mark's transparent right half, which is pushed past
       the edge to bring the ring itself to the centre. Nothing
       overflows downward here -- the box grows to its content -- so
       this cannot swallow the page. */
    overflow: hidden;
    padding: 62px 0 0;                  /* clears the bar; photo runs edge to edge */
  }
  .js-mobilenav .split-photo { display: block; height: auto; }
  .js-mobilenav .split-photo img {
    width: 100%; height: auto;
    max-height: 58vh;
    object-fit: cover;
  }
  .js-mobilenav .split-body { padding: 36px 26px 0; }
  .js-mobilenav .split-body h1 {
    font-size: 34px;
    text-align: center;
    margin: 0 0 24px;
  }
  .js-mobilenav .split-body p { font-size: 13px; line-height: 21px; }
  .js-mobilenav .split-body .intro { margin-bottom: 22px; }

  /* the mark closes the page rather than sitting behind the words */
  .js-mobilenav .arc {
    position: relative; right: auto; bottom: auto;
    display: block;
    max-width: none;
  }

  /* the contact form has room to be one column here */
  .js-mobilenav .split .row { grid-template-columns: 1fr; }
  .js-mobilenav .split form { max-width: none; }
}

/* The arc's spin bakes translateX(46.81%) into its keyframes, to hold
   the mark in the desktop corner. Stacked on a phone the mark is
   centred instead, and that translate would drag it off the right
   edge — an animation replaces the whole transform, so `transform:
   none` alone cannot stop it. Same motion, without the offset. */
@keyframes oltSpinFlat {
  from { transform: rotate(-360deg) scale(.05); }
  to   { transform: rotate(0deg)    scale(1); }
}
@media (max-width: 750px) {
  .js-mobilenav .anim-spin {
    animation-name: oltSpinFlat, oltFade;
  }
}

/* The home sheet is built by script and would otherwise render as a
   desktop sidebar the moment the window is widened, since it carries
   the .rail class to borrow its styling. It exists only on a phone. */
.mobile-sheet { display: none !important; }

@media (max-width: 750px) {
  .js-mobilenav .mobile-sheet { display: flex !important; }

  /* the bar replaces the home page's own top strip of links, which
     wrapped onto two lines and collided with it */
  .js-mobilenav.home-page .topbar { display: none; }
}

@media (max-width: 750px) {

  /* ---- the bar ----------------------------------------------------
     A rule under the header everywhere but home, where the bar floats
     over the photograph and a line across it would cut the image. */
  .js-mobilenav .mobar { border-bottom: 1px solid var(--paper); }
  .js-mobilenav.home-page .mobar { border-bottom: 0; }

  /* the mark, larger than the 30px it started at */
  .js-mobilenav .mobar-logo img { height: 42px; }
  .js-mobilenav .mobar { height: 74px; }
  .js-mobilenav .rail  { padding-top: 100px; }
  .js-mobilenav .page  { padding-top: 104px; }
  .js-mobilenav .split { padding-top: 74px; }

  /* ---- About & Contact scroll -------------------------------------
     These pages are pinned to exactly one viewport on a desktop, with
     the body clipped so nothing scrolls. Stacked on a phone the copy
     runs well past one screen, so that clip was hiding most of the
     page: body was held at 812px against 1380px of content. */
  .js-mobilenav body.split-page,
  body.split-page {
    overflow: visible;
    height: auto;
  }

  /* ---- Press ------------------------------------------------------
     The card head behaves as the desktop row does: the outlet's mark
     alone on black, and the darkened photograph fading in behind it
     on hover. The photograph leaves the backdrop and takes a block of
     its own above the words, so when it does appear it spans the full
     width of the card rather than the width of the mark.

     It keeps its 190px while invisible, so revealing it never shifts
     the words beneath. */
  .js-mobilenav .press-shade {
    position: relative; inset: auto;
    transform: none;
    width: 100%; height: 190px;
    order: -1;
  }
  .js-mobilenav .press-row {
    display: flex; flex-direction: column;
    overflow: hidden;
  }
  .js-mobilenav .press-mark {
    margin-top: -190px;               /* sits over the photograph */
    height: 190px;
    padding: 20px;
  }
  .js-mobilenav .press-body { padding: 22px 20px 26px; }
}

@media (max-width: 750px) {

  /* ---- Selected Work panes ----------------------------------------
     The strip took three quarters of a phone screen and left the
     title, place and caption fighting over the last quarter. Nine
     points move from the photographs to the words. */
  .js-mobilenav .pane .holder { flex: 0 0 66%; }

  /* ---- the mark on About & Contact --------------------------------
     It was drawn at 443px against a 375px screen and pulled left by
     a further 82px, so the ring ran off both the left edge and the
     foot. Sized to the screen and left alone, the whole mark sits in
     the corner it is meant to occupy. */
  .js-mobilenav .arc {
    /* Ranged left and sitting on the foot of the page, whole. The
       artwork fills only the left 53.5% of the file, so the box may
       run wider than the ring looks -- .split clips the transparent
       tail rather than letting it widen the page. */
    width: min(100%, 400px);
    margin: 34px 0 0;
    padding-bottom: 0;
    transform: none;
  }

  /* ---- Press ------------------------------------------------------
     .press-row keeps align-items:center from the desktop grid, and as
     a flex column that stops its children stretching — so the scrim
     was only as wide as the logo inside it (240px of 375) instead of
     spanning the photograph. */
  .js-mobilenav .press-row { align-items: stretch; }

  /* the heading sat at 73px against a bar ending at 74px, tucked
     under the rule */
  .js-mobilenav .press { padding-top: 116px; }
}

@media (max-width: 750px) {
  /* the index heading sat at 73px against a bar ending at 74. Matches
     the Press heading's clearance now. */
  .js-mobilenav .index { padding-top: 116px; }

  /* ---- the red line under each project ----------------------------
     It is pinned to the foot of the pane on a desktop, level with the
     NEXT cue in the opposite corner. On a phone the pane's black band
     is short and that left it stranded at the bottom, far from the
     name it belongs to. Here it follows the location directly, 26px
     beneath — the same gap the cue keeps between NEXT and the project
     named under it. */
  .js-mobilenav .caption em {
    position: static;
    left: auto; bottom: auto;
    margin-top: 26px;
    max-width: none;
  }
}

/* ==================================================================
   ABOUT & CONTACT — the same sticky footer as Press
   The content freezes when its foot reaches the viewport floor and
   the footer rides up over it. Identical machinery: the wrapper is
   sticky by a negative top (site.js sets it, since it depends on the
   content's own height), the band sits above it at a higher z-index,
   and one --footer-h drives both the blank space and the footer, so
   the footer's top rule lands exactly on the rule under the mark.

   These pages were pinned to a single viewport with the body clipped
   — that was right when there was nothing below the fold, but a
   footer has to be scrolled to, so the clip is lifted here.
   ================================================================== */
body.split-page { height: auto; overflow: visible; }

.split-sticky {
  position: sticky;
  z-index: 1;
  padding-bottom: var(--footer-h);   /* the black band the footer covers */
}
/* the rule directly beneath the mark, at the top of that band */
.split-sticky::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  bottom: var(--footer-h);
  height: 1px;
  background: var(--rule);
}

/* the mark lifts clear of the rule so none of it is cut off, and so
   the line reads as sitting under it rather than through it */
.split .arc { bottom: 14px; }

.split-page .footer-band {
  position: relative;
  z-index: 2;                        /* over the frozen content */
  margin-top: 0;
}

/* ------------------------------------------------------------------
   One rule at the join, not two.
   The content's closing rule and the footer's opening rule land on
   the same spot when the footer is fully up — two 1px lines with a
   sub-pixel sliver of black between them (measured: content ends at
   984.31, footer began at 984.67), which reads as a doubled line.
   A negative margin does nothing here: at full scroll the footer and
   the pinned content are both anchored to the viewport floor, so
   moving the footer in the flow moves it not at all. Making it 6px
   taller than the band it covers does move its top edge up, over the
   rule beneath. 6px rather than the 1.36px measured, because that
   figure comes from fractional layout and shifts with the viewport,
   zoom and device pixel ratio -- wide enough that no size reopens it.
   ------------------------------------------------------------------ */
.footer-band { min-height: calc(var(--footer-h) + 6px); }

/* the mark keeps its 14px of daylight above the rule now that the
   footer reaches 6px higher */
.split .arc { bottom: 20px; }

@media (max-width: 750px) {
  /* ---- the mark waits to be seen ----------------------------------
     About and Contact scroll on a phone, and the mark sits well below
     the fold — its spin used to play out unwatched while the visitor
     was still on the first screen, so by the time they reached it the
     mark was already sitting still. Held at its first frame until it
     is actually on screen, then released.

     Scoped to .js-mobilenav, which only exists once the script has
     run: if the script never runs, nothing holds the mark and it
     simply animates on load as before. */
  .js-mobilenav .arc.await-view { animation-play-state: paused; }
}
