/* ==========================================================================
   aobvr. — producer, media, management, web
   One screen, the same way as highpass.: the display type, the name set across
   the full column and standing on a 20 Hz – 20 kHz axis. The colours are the
   wallpaper's — a fisheye of the city at night: near-black, silver, the lit
   windows — and nothing else; the ground is the night, the type the light, the
   full stop silver. Behind the poster is that same photograph, out of focus,
   for the light; over everything, a film grain. The bubble menu sits in the top corner; its
   pills open the other pages by zooming into the name: the four sections in
   through a letter each (light pages), Contact in through the full stop (silver).

   Frames: Desktop wide 1920 · Desktop narrow 1280 · iPad 834 · iPhone 402
   ========================================================================== */

/* ==========================================================================
   Fonts — the two latin files, served locally. Both are variable on weight,
   so one file covers 400–700.
   ========================================================================== */
@font-face {
  font-family: "Instrument Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/instrument-sans-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Instrument Sans Fallback";
  src: local("Arial");
  ascent-override: 94.42%;
  descent-override: 24.33%;
  line-gap-override: 0.00%;
  size-adjust: 102.74%;
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/inter-latin.woff2") format("woff2");
}
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  ascent-override: 90.44%;
  descent-override: 22.52%;
  line-gap-override: 0.00%;
  size-adjust: 107.12%;
}

/* ==========================================================================
   Design tokens
   ========================================================================== */
:root {
  /* Viewport width minus the scrollbar — pinned by the script; 100vw without it */
  --vw: 100vw;

  /* Colours: five tones off the wallpaper, no hue */
  --night: #161b1e;                 /* the ground: the city's black, a little cold */
  --light: #ececea;                 /* the type, the bubbles: the lit windows */
  --silver: #c0c2bd;                /* the full stop, and the Contact page */
  --steel: #9d9f9b;                 /* small print on the night */
  --slate: #5d6060;                 /* small print on the light */
  --color-bg: var(--night);
  --color-ink: var(--light);
  --color-muted: var(--steel);
  --color-accent: var(--silver);    /* the accent: the full stop */
  --accent-rgb: 192, 194, 189;
  --deep-rgb: 0, 0, 0;
  --color-white: var(--light);

  /* Type */
  --font-display: "Instrument Sans", "Instrument Sans Fallback", ui-sans-serif, system-ui, sans-serif;
  --font-body: "Inter", "Inter Fallback", ui-sans-serif, system-ui, sans-serif;

  /* Desktop sizes: 1280 → 1920 (held below 1280 and above 1920) */
  --fs-title: clamp(48px, (var(--vw) * 0.01875) + 24px, 60px);
  --fs-line: 19px;
  --fs-btn: 16px;
  --fs-axis: 12px;
  --fs-foot: 14px;

  /* Spacing. The column is the poster's measure: the viewport less its
     margins, capped — and on a short landscape window held to what lets the
     name, which is set to the column, still fit the one screen. */
  --vh: 100vh;
  --column: min(1680px, var(--vw) - 160px, max(960px, (var(--vh) - 320px) * 3.4));
  --page-top: 48px;
  --page-bottom: 40px;
  --top-gap: 48px;                   /* the least air between the top row and the name */
  --bubble-h: 56px;                  /* the menu toggle */
  --shadow-bubble: 0 4px 16px rgba(0, 0, 0, 0.3);

  /* The name fills the column: the ink of "aobvr." is this many ems wide at
     the tracking below (measured; re-measure if the face or the tracking
     changes), so the size is the column over that. --mark-lsb is the a's
     side bearing, so the bowl sits on the column edge. */
  --mark-em: 2.525;
  --mark-lsb: 0.0375em;
  --mark-track: -0.055em;
  --fs-mark: calc(var(--column) / var(--mark-em));

  /* Radii + curves */
  --radius-pill: 999px;
  --ease-settle: cubic-bezier(0.22, 1, 0.36, 1);
}

@supports (height: 100svh) { :root { --vh: 100svh; } }

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

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

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-body);
  line-height: normal;
  -webkit-font-smoothing: antialiased;
  overflow: hidden;                   /* older Safari */
  overflow: clip;
}

h1, h2, p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }

.u-display { font-family: var(--font-display); font-weight: 700; }

/* Read, not seen: the whole sentence for a screen reader where the script has split the visible one into words. */
.u-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  -webkit-user-select: none;
  user-select: none;
}

::selection { background: rgba(var(--accent-rgb), 0.3); }
.view--light ::selection, .view--contact ::selection { background: var(--night); color: var(--light); }

/* ==========================================================================
   Page — one screen tall: the top row at the top, the name at the bottom
   ========================================================================== */
.page {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  padding: var(--page-top) 0 var(--page-bottom);
  background: var(--color-bg);
}

/* Arrival. Everything comes in the way the menu's pills do: words rise into
   place as they fade up, one after the other; the buttons pop from nothing
   with a back-out overshoot and their labels rise into them just after. The
   ground stays put. Each plays once, when the page loads. */
@keyframes rise-in { from { opacity: 0; transform: translateY(24px); } }
@keyframes pop-in { from { transform: scale(0); } }
@keyframes fade-in { from { opacity: 0; } }

.poster__title,
.mark,
.axis,
.footer > * {
  animation: rise-in 500ms cubic-bezier(0.215, 0.61, 0.355, 1) backwards;
  animation-delay: var(--enter, 0ms);
}
.poster__title { --enter: 60ms; }
.mark { --enter: 260ms; animation-duration: 700ms; }
.axis { --enter: 480ms; }
.footer > * { --enter: 580ms; }
.footer > :last-child { --enter: 660ms; }

.nav__toggle {
  animation: pop-in 500ms cubic-bezier(0.34, 1.45, 0.64, 1) backwards;
  animation-delay: 180ms;
}

@media (prefers-reduced-motion: reduce) {
  .poster__title, .mark, .axis, .footer > *, .nav__toggle { animation: none; }
}

/* ==========================================================================
   Backdrop — behind the poster, the wallpaper: the fisheye of the city at
   night, out of focus, so what is left of it is its light (the fog, the lit
   windows, the dark of the lens's corners), drawn up a little over a minute
   and back, the way a still camera breathes. Over it, and over every page,
   a film grain: a tile of specks that jumps between a few offsets a few
   times a second, so the ground is never quite still.
   ========================================================================== */
.backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.backdrop__fog {
  position: absolute;
  inset: -4%;
  background: url("../img/fog.jpg") center / cover no-repeat;
  opacity: 0.4;                        /* the picture is cut to its lights (everything under 42 % is black): the fog lifts the night a little, the lens's corners sink it */
  animation: fog-breathe 70s ease-in-out infinite alternate;
}
@keyframes fog-breathe {
  from { transform: scale(1); }
  to { transform: scale(1.07); }
}

.backdrop__grain {
  position: absolute;
  inset: 0;
  background: var(--grain, url("../img/grain-light.png")) 0 0 / 256px 256px repeat;
  opacity: var(--grain-strength, 0.16);
  animation: grain-jump 0.7s steps(1, end) infinite;
}
@keyframes grain-jump {
  0% { background-position: 0 0; }
  17% { background-position: -71px 45px; }
  33% { background-position: 38px -96px; }
  50% { background-position: -120px -33px; }
  67% { background-position: 84px 118px; }
  83% { background-position: -26px 71px; }
}
.view--light, .view--contact { --grain: url("../img/grain-night.png"); --grain-strength: 0.09; }
@media (prefers-reduced-motion: reduce) { .backdrop__fog, .backdrop__grain { animation: none; } }

.column {
  position: relative;
  z-index: 1;
  width: var(--column);
  margin-right: auto;
  margin-left: auto;
}

/* ==========================================================================
   The poster
   ========================================================================== */
.poster {
  display: flex;
  flex: 1;
  flex-direction: column;
}

.poster__top {
  position: relative;
  z-index: 20;                      /* the menu layer lives in here */
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 32px;
}

/* What it is: the four things, each with its full stop, like the name. */
.poster__title {
  font-size: var(--fs-title);
  line-height: 1.04;
  letter-spacing: -0.03em;
  color: var(--color-ink);
  white-space: nowrap;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 18px 24px;              /* 36 each side less the 8 + 16 arrow: same width */
  border: 0;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--fs-btn);
  font-weight: 600;
  line-height: 19px;
  white-space: nowrap;
}

/* ==========================================================================
   The name — set across the full column, standing on the axis every EQ
   draws: 20 Hz to 20 kHz, log-spaced.
   ========================================================================== */
.poster__mark {
  position: relative;
  margin-top: auto;
  padding-top: var(--top-gap);
}

.mark {
  margin: 0 0 0 calc(-1 * var(--mark-lsb));      /* the a's side bearing: the bowl sits on the column edge */
  font-size: var(--fs-mark);
  line-height: 1;
  letter-spacing: var(--mark-track);
  color: var(--color-ink);
  white-space: nowrap;
  -webkit-user-select: none;
  user-select: none;
}
.mark__dot { color: var(--color-accent); }

/* The full stop is a door: it goes to Contact — the same page the menu
   opens, without the menu. It is a link the script lays over the square (it
   knows where the ink sits), a little larger than the square on small
   screens so a thumb can find it. Under the pointer the square says where it
   leads: an envelope, drawn in outline in the middle of it. Nothing moves or
   grows. */
.spot {
  position: absolute;
  display: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.spot.is-placed { display: block; }
.spot::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--w, 0px);
  height: var(--h, 0px);
  margin: calc(var(--h, 0px) / -2) 0 0 calc(var(--w, 0px) / -2);
  background: var(--spot);
  opacity: 0;
  transition: opacity 160ms ease;
}
.spot:focus-visible::before { opacity: 1; }

.spot__icon {
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--w, 0px) * 0.6);
  height: calc(var(--h, 0px) * 0.6);
  transform: translate(-50%, -50%);
  fill: none;
  stroke: var(--spot-ink);
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
  transition: opacity 160ms ease;
  pointer-events: none;
}
.spot__icon-dot { fill: var(--spot-ink); stroke: none; }
.spot:focus-visible .spot__icon { opacity: 1; }
@media (hover: hover) and (pointer: fine) {           /* a thumb has no hover: nothing stays lit after a tap */
  .spot:hover::before,
  .spot:hover .spot__icon { opacity: 1; }
}
.spot:focus-visible { outline: none; }               /* the ring goes round the square, not the (larger) touch box */
.spot:focus-visible::before { outline: 2px solid var(--color-ink); outline-offset: 6px; }
.spot--contact { --spot: var(--color-accent); --spot-ink: var(--night); }
@media (prefers-reduced-motion: reduce) { .spot::before, .spot__icon { transition: none; } }

.axis {
  position: relative;
  height: 40px;
  margin-top: calc(var(--fs-mark) * -0.04);      /* nothing hangs below the baseline here: the name stands close on the line */
  font-family: var(--font-body);
  font-size: var(--fs-axis);
  font-weight: 500;
  letter-spacing: 0.02em;
  line-height: 14px;
  color: var(--color-muted);
}
.axis::before {                      /* the axis itself */
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: rgba(236, 236, 234, 0.28);
}
.axis i {
  position: absolute;
  top: 0;
  left: calc(var(--at) * 100%);
  width: 1px;
  height: 6px;
  background: rgba(236, 236, 234, 0.28);
}
.axis i:last-of-type { margin-left: -1px; }      /* the last tick stays inside the column */
.axis i.is-major { height: 11px; background: rgba(236, 236, 234, 0.6); }
.axis span {
  position: absolute;
  top: 19px;
  left: calc(var(--at) * 100%);
  transform: translateX(-50%);
  white-space: nowrap;
}
.axis span.is-start { transform: none; }
.axis span.is-end { transform: translateX(-100%); }

/* ==========================================================================
   Footer — the note on the left (with the speaker), the handle on the right
   ========================================================================== */
.footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  margin-top: 20px;
  font-family: var(--font-body);
  font-size: var(--fs-foot);
  line-height: 17px;
  color: var(--color-muted);
  white-space: nowrap;
}
.footer__left { display: flex; align-items: center; gap: 10px; }
.footer__link { transition: color 200ms ease; }
.footer__link:hover { color: var(--color-ink); }
.footer__link:focus-visible { outline: 2px solid var(--color-ink); outline-offset: 3px; border-radius: 4px; }

/* The speaker: every button answers a click with a soft note, so the way to turn that off stands by the © line. */
.footer__sound {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin: -8px 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--color-muted);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 200ms ease;
}
.footer__sound[hidden] { display: none; }
.footer__sound:hover { color: var(--color-ink); }
.footer__sound:focus-visible { outline: 2px solid var(--color-ink); outline-offset: 2px; }
.footer__sound svg { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.footer__sound[aria-pressed="true"] .footer__sound-off,
.footer__sound[aria-pressed="false"] .footer__sound-on { display: none; }
.footer__sound[aria-pressed="false"] { opacity: 0.6; }

/* ==========================================================================
   Bubble menu — seeder's: a round toggle whose two lines cross into an X,
   and the pills that pop out under it one after the other, each tilted its
   own way. All of it runs on the checkbox: :checked opens it, so nothing
   here needs a script; the script only adds Escape and click-outside.
   ========================================================================== */
.nav {
  position: relative;
  display: flex;
  flex: none;
  align-items: center;
  height: var(--bubble-h);
}

.nav__toggle {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  gap: 6px;
  order: 2;
  width: var(--bubble-h);
  height: var(--bubble-h);
  border-radius: 50%;
  background: var(--color-white);
  box-shadow: var(--shadow-bubble);
  cursor: pointer;
}

/* The checkbox sits exactly over the bubble, so the menu opens and closes
   with no script. */
.nav__toggle-input {
  order: 3;
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  width: var(--bubble-h);
  height: var(--bubble-h);
  margin: 0 0 0 calc(-1 * var(--bubble-h));
  padding: 0;
  border: 0;
  border-radius: 50%;
  opacity: 0;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}

.nav:has(.nav__toggle-input:focus-visible) .nav__toggle {
  outline: 2px solid var(--color-ink);
  outline-offset: 3px;
}

.nav__toggle-bar {
  display: block;
  width: 26px;
  height: 2px;
  border-radius: 2px;
  background: var(--night);
  transform-origin: center;
  transition: transform 300ms ease;
}
.nav__toggle-input:checked ~ .nav__toggle .nav__toggle-bar:nth-child(1) { transform: translateY(4px) rotate(45deg); }
.nav__toggle-input:checked ~ .nav__toggle .nav__toggle-bar:nth-child(2) { transform: translateY(-4px) rotate(-45deg); }

/* The pills: five of them, so they stand in a row under the toggle on
   desktop (beside it they would run into the statement) and in a stack under
   it on smaller screens — nearest pill first either way. */
.menu {
  position: absolute;
  top: calc(100% + 12px);
  right: 0;
  z-index: 5;
  visibility: hidden;
  transition: visibility 0s linear 220ms;
}
.nav__toggle-input:checked ~ .menu { visibility: visible; transition-delay: 0s; }

.menu__list {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
}

/* Each pill pops from nothing with a back-out overshoot, starting from the
   one next to the button, and folds back in 200ms. */
.menu__col {
  display: flex;
  transform: scale(0);
  transform-origin: 50% 50%;
  transition: transform 200ms cubic-bezier(0.55, 0.055, 0.675, 0.19);
  will-change: transform;
}
.menu__col:nth-last-child(1) { --pop-delay: 0ms; }
.menu__col:nth-last-child(2) { --pop-delay: 80ms; }
.menu__col:nth-last-child(3) { --pop-delay: 160ms; }
.menu__col:nth-last-child(4) { --pop-delay: 240ms; }
.menu__col:nth-last-child(5) { --pop-delay: 320ms; }
.menu__col:nth-last-child(6) { --pop-delay: 400ms; }
.nav__toggle-input:checked ~ .menu .menu__col {
  transform: none;
  transition: transform 500ms cubic-bezier(0.34, 1.45, 0.64, 1) var(--pop-delay);
}

.menu__pill {
  display: inline-flex;
  align-items: center;
  padding: 18px 24px;
  border-radius: var(--radius-pill);
  background: var(--color-white);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
  font-family: var(--font-body);
  font-size: var(--fs-btn);
  font-weight: 600;
  line-height: 19px;
  color: var(--night);
  white-space: nowrap;
  --rot: 0deg;
}
.menu__pill.glow-btn {
  transform: rotate(var(--rot));
  --glow-spot: rgba(255, 255, 255, 0.5);
  --glow-rim: rgba(255, 255, 255, 0.95);
  --glow-shadow: 0 4px 14px rgba(0, 0, 0, 0.3), 0 12px 28px -12px rgba(255, 255, 255, 0.25);
}
.menu__pill.glow-btn:hover { transform: rotate(var(--rot)) translateY(-1px); }
.menu__pill.glow-btn:active { transform: rotate(var(--rot)) scale(0.985); }

/* The label rises into the pill just after it lands. */
.menu__label {
  display: inline-block;
  transform: translateY(24px);
  opacity: 0;
  transition: transform 200ms cubic-bezier(0.55, 0.055, 0.675, 0.19), opacity 200ms cubic-bezier(0.55, 0.055, 0.675, 0.19);
  will-change: transform, opacity;
}
.nav__toggle-input:checked ~ .menu .menu__label {
  transform: none;
  opacity: 1;
  transition:
    transform 500ms cubic-bezier(0.215, 0.61, 0.355, 1) calc(var(--pop-delay) + 50ms),
    opacity 500ms cubic-bezier(0.215, 0.61, 0.355, 1) calc(var(--pop-delay) + 50ms);
}

@media (min-width: 1025px) {
  .menu__col:nth-last-child(odd) .menu__pill { --rot: 6deg; }
  .menu__col:nth-last-child(even) .menu__pill { --rot: -6deg; }
}

/* Tablet and phone: a stack under the toggle, right-aligned, top pill first. */
@media (max-width: 1024px) {
  .menu__list { flex-direction: column; align-items: flex-end; gap: 10px; }
  .menu__col:nth-child(1) { --pop-delay: 0ms; }
  .menu__col:nth-child(2) { --pop-delay: 80ms; }
  .menu__col:nth-child(3) { --pop-delay: 160ms; }
  .menu__col:nth-child(4) { --pop-delay: 240ms; }
  .menu__col:nth-child(5) { --pop-delay: 320ms; }
  .menu__col:nth-child(6) { --pop-delay: 400ms; }
}

@media (prefers-reduced-motion: reduce) {
  .menu, .menu__col, .menu__pill, .menu__label, .nav__toggle-bar { transition: none !important; }
}

/* ==========================================================================
   The other pages. Each is a full-screen <dialog> whose ground is the
   colour of the piece of the name it is reached through: the light of a
   letter for the four sections, the silver of the full stop for Contact.
   The script zooms the poster into that piece until it fills the window,
   opens the page over it, and zooms back out on the way home. They open
   from their address (#music, #media, #management, #web, #contact), so the
   back button closes them and the links can be shared; without the script
   the same addresses show them through :target, with no zoom.
   ========================================================================== */
.view {
  display: none;
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  overflow: auto;
  overscroll-behavior: contain;
}
.view[open] { display: block; }
.view:focus { outline: none; }            /* the page itself is never the control */
.view::backdrop { background: transparent; }
.view > .backdrop { position: fixed; z-index: 4; }   /* the grain stays with the window when a short screen scrolls the page, and lies over
                                                        the whole page, the echo included: its bands carry the grain like the ground does */
html:not(.js) .view:target { display: block; z-index: 50; }

.view--light {
  --view-ground: var(--light);
  background: var(--view-ground);
  color: var(--night);
  color-scheme: light;
  --view-soft: var(--slate);
  --view-lead: #7e8280;                  /* the name at the head of a statement: turned down, the way "high" is on highpass. */
  --view-focus: var(--night);
  --close-bg: var(--night);
  --close-fg: var(--light);
}
.view--contact {
  --view-ground: var(--silver);
  background: var(--view-ground);
  color: var(--night);
  color-scheme: light;
  --view-soft: #363d42;
  --view-focus: var(--night);
  --close-bg: var(--night);
  --close-fg: var(--silver);
}

.view__inner {
  position: relative;                 /* (no z-index: the page is no layer of its own, so the echo can come between it and the line it echoes) */
  display: flex;
  flex-direction: column;
  width: var(--column);
  min-height: 100%;
  margin: 0 auto;
  padding: var(--page-top) 0 var(--page-bottom);
}

.view__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--bubble-h);
}
.view__mark { font-size: 28px; letter-spacing: -0.04em; white-space: nowrap; }
.view--light .view__mark-dot { color: var(--steel); }
.view--contact .view__mark-dot { color: var(--light); }

/* The close bubble: the toggle's X, in the page's own colours — night with
   a light X on the light pages, night with a silver X on the silver one. */
.view__close {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: none;
  gap: 6px;
  width: var(--bubble-h);
  height: var(--bubble-h);
  border-radius: 50%;
  background: var(--close-bg);
  box-shadow: var(--shadow-bubble);
  cursor: pointer;
  transition: transform 260ms var(--ease-settle);
}
.view__close:hover { transform: scale(1.06); }
.view__close:active { transform: scale(0.96); }
.view__close:focus-visible { outline: 2px solid var(--view-focus); outline-offset: 3px; }
.view__close span {
  display: block;
  width: 26px;
  height: 2px;
  border-radius: 2px;
  background: var(--close-fg);
}
.view__close span:nth-child(1) { transform: translateY(4px) rotate(45deg); }
.view__close span:nth-child(2) { transform: translateY(-4px) rotate(-45deg); }

.view__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  padding: 48px 0 calc(var(--bubble-h) + 24px);     /* the top bar's height again underneath: optically centred */
}

.view__label {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  line-height: 16px;
  text-transform: uppercase;
  color: var(--view-soft);
}

.view__statement {
  max-width: 21em;
  margin-top: 24px;
  font-size: clamp(30px, var(--column) / 28, 52px);
  line-height: 1.14;
  letter-spacing: -0.035em;
}
.view__lead { color: var(--view-lead, var(--steel)); }
.view__copy {
  max-width: 30em;
  margin-top: 28px;
  font-size: clamp(19px, var(--column) / 56, 26px);
  font-weight: 500;
  line-height: 1.34;
  letter-spacing: -0.015em;
  color: var(--view-soft);
}

/* The big lines: the page's links, set large, one line-pitch apart — the
   platforms on Music, the sites on Web, the handles on Contact. Each page
   sizes them to what it has to fit on the one screen. */
.view__line {
  font-family: var(--font-body);
  font-size: var(--fs-line);
  line-height: 1.55;
  color: var(--view-soft);
  transition: opacity 240ms ease;
}
.view__bigs {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  margin: 6px 0 0 -0.04em;
  font-size: var(--bigs);
}
.view__big {
  position: relative;
  display: block;
  line-height: 1.1;
  letter-spacing: -0.04em;
  white-space: nowrap;
  transition: opacity 240ms ease;
}
.view__big:focus-visible { outline: 2px solid var(--view-focus); outline-offset: 6px; border-radius: 6px; }
/* A glyph where a superscript would sit after the handle. */
.view__glyph {
  position: absolute;
  top: 0.3em;
  left: calc(100% + 0.16em);
  width: clamp(20px, 0.3em, 44px);
  height: clamp(20px, 0.3em, 44px);
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.view__glyph-dot { fill: currentColor; stroke: none; }

/* A row: the big line with, after it, a few words of what the site is. */
.view__row {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  column-gap: 0.4em;
}
.view__caption {
  font-family: var(--font-body);
  font-size: var(--fs-line);
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--view-soft);
  transition: opacity 240ms ease;
}

/* Music: two lines of statement at most, the player, then the three platforms. */
.view--music { --bigs: min(112px, var(--column) / 8, max(44px, (var(--vh) - 560px) / 3.3)); }
.view--music .view__bigs { margin-top: 20px; }
/* Web: one line of statement, then five sites. */
.view--web { --bigs: min(88px, var(--column) / 11, max(36px, (var(--vh) - 460px) / 5.6)); }
.view--web .view__bigs { margin-top: 20px; }
/* Contact: the address and the two handles — "alex@aobvr.com" is 7.2em wide at this tracking. */
.view--contact { --bigs: min(150px, var(--column) / 8.8, max(56px, (var(--vh) - 404px) / 3.3)); }

/* The player: Spotify's compact card, in a rounded slot; the frame only
   arrives when the page opens (the poster makes no request of its own). */
.embed {
  position: relative;
  width: 100%;
  max-width: 520px;
  height: 80px;
  margin-top: 12px;
  border-radius: 12px;
  overflow: hidden;
  background: rgba(22, 27, 30, 0.06);
  transition: opacity 240ms ease;
}
.embed iframe { display: block; width: 100%; height: 80px; border: 0; }
.embed__fallback {
  display: flex;
  align-items: center;
  height: 100%;
  padding: 0 20px;
  font-size: var(--fs-line);
  color: var(--view-soft);
}
.embed:has(iframe) .embed__fallback { display: none; }
.view__player { width: 100%; max-width: 520px; margin-top: 28px; }

/* Media: the proof. A row of posts, each in a frame of its own shape (a
   phone, a square, a landscape) but all one height, with the number it did
   underneath, set large, and what was done on it. The height comes from the
   window's, so the row and its captions fit under the statement on the one
   screen — and from the column's width, so the row (4.02 heights wide with
   these five, plus the gaps) never runs past it. On a phone the row scrolls
   sideways instead, edge to edge, one post at a time. */
.view--media .view__body { padding-top: 24px; padding-bottom: 24px; }
.view--media { --post-h: min(clamp(220px, var(--vh) - 560px, 480px), (var(--column) - 80px) / 4.1); }
.posts {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  width: 100%;
  margin-top: 24px;
}
.posts > li { flex: none; width: calc(var(--post-h) * var(--ratio, 0.5625)); }      /* the frame's own shape, at the row's height */
.post { margin: 0; width: 100%; }
.post__frame {
  position: relative;
  display: block;
  width: 100%;
  height: var(--post-h);
  border-radius: 18px;
  overflow: hidden;
  background: var(--night);
  color: var(--light);
  -webkit-tap-highlight-color: transparent;
}
.post__frame:focus-visible { outline: 2px solid var(--view-focus); outline-offset: 4px; }
.post__video { display: block; width: 100%; height: 100%; object-fit: cover; }
.post__caption { margin-top: 12px; }
.post__stat {
  display: block;
  font-size: clamp(26px, var(--column) / 32, 40px);
  line-height: 1.05;
  letter-spacing: -0.03em;
}
.post__what {
  display: block;
  margin-top: 4px;
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--view-soft);
}
.post__link {
  font-weight: 600;
  color: var(--view-soft);
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color 200ms ease;
}
.post__link:hover { color: var(--night); }
.post__link:focus-visible { outline: 2px solid var(--view-focus); outline-offset: 3px; border-radius: 3px; }
/* A placeholder, until the post is in: the frame drawn in outline on the grid, its number in the middle. */
.post--empty .post__frame {
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1.5px dashed rgba(22, 27, 30, 0.35);
  color: rgba(22, 27, 30, 0.35);
}
.post__index { font-size: 22px; letter-spacing: -0.02em; }
.post--empty .post__stat, .post--empty .post__what { color: rgba(22, 27, 30, 0.35); }
.view.is-echo .posts { opacity: 0.14; transition: opacity 240ms ease; }
.view.view--media .btn { margin-top: 24px; }         /* (outweighs the pages' common 40px, which is written later) */

/* Pointing at a line (or tabbing to it) turns everything else down and
   repeats the line up and down the window in outline: a strict column, one
   line-pitch apart, each copy on a band of the page's own ground laid right
   across the window, so whatever the column passes over goes under it. The
   line itself stays on top, in its own slot. The script lays the bands and
   the rows out (it knows where the line sits); this only draws them. */
.view__top, .view__label, .view__copy { transition: opacity 240ms ease; }
.view.is-echo .view__top,
.view.is-echo .view__label,
.view.is-echo .view__line,
.view.is-echo .view__copy,
.view.is-echo .view__caption,
.view.is-echo .embed,
.view.is-echo .btn,
.view.is-echo .view__big:not(.is-active) { opacity: 0.14; }
.view.is-echo .is-active { position: relative; z-index: 3; }   /* over the echo (2), under the grain (4) */

/* The name at the head of a statement does it too, in its own grey: the rest of the sentence steps back. */
.w { transition: opacity 240ms ease; }
html.js .view.is-echo .view__statement .w { opacity: 0.14; }
html.js .view.is-echo .view__statement .view__lead .w { opacity: 1; }

.echo {
  position: fixed;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
}
.echo__group { position: absolute; inset: 0; line-height: 1.1; letter-spacing: -0.04em; }   /* (the script sets the line's own measure where it differs) */
.echo__rows { position: absolute; inset: 0; }                                                  /* (and, where the column is to thin out towards its ends, a mask on this) */
.echo__row {
  position: absolute;
  font-family: var(--font-display);
  font-weight: 700;
  line-height: inherit;
  letter-spacing: inherit;
  white-space: nowrap;
  /* The outline: a stroke twice the wanted weight with the ground's colour
     filled back over it. Only the outer half shows — and none of the seams
     a variable font's overlapping contours leave inside a plain stroke. */
  color: var(--view-ground);
  -webkit-text-stroke: clamp(1.6px, 0.024em, 3.2px) var(--echo-ink, var(--color-ink));   /* half of it shows: hairline at any size */
  paint-order: stroke fill;
  opacity: 0;
  transition: opacity 160ms ease;
}
.echo__group.is-on .echo__row { opacity: 1; transition: opacity 320ms ease calc(var(--d, 0) * 34ms); }
/* The bands: the page's own ground, laid over everything above the line's slot and everything below it, the full
   width of the window — so the page goes under the column, and only the line's own slot is left open. They come
   in with the first rows, and go with them. */
.echo__band {
  position: absolute;
  left: 0;
  right: 0;
  background: var(--view-ground);
  opacity: 0;
  transition: opacity 160ms ease;
}
.echo__group.is-on .echo__band { opacity: 1; transition: opacity 280ms ease 20ms; }
@media (prefers-reduced-motion: reduce) {
  .echo__row, .echo__group.is-on .echo__row, .echo__band, .echo__group.is-on .echo__band,
  .view__top, .view__label, .view__line, .view__big, .view__copy, .view__caption, .embed, .w { transition: none; }
}

.view .btn { margin-top: 40px; }
.view .glow-btn:focus-visible { outline-color: var(--view-focus); }
/* Silver: the full stop again — the door to Contact, on a light page. */
.btn--accent {
  background: var(--silver);
  color: var(--night);
  --glow-spot: rgba(255, 255, 255, 0.4);
  --glow-shadow: 0 12px 28px -10px rgba(22, 27, 30, 0.35);
}
/* Night: a plain button on a light or silver page. */
.btn--ink {
  background: var(--night);
  color: var(--light);
  --glow-spot: rgba(255, 255, 255, 0.2);
  --glow-rim: rgba(255, 255, 255, 0.85);
  --glow-shadow: 0 12px 28px -10px rgba(22, 27, 30, 0.55);
}

/* Arrival: the zoom lands on a flat field of colour; then the grain
   fades up and the page comes in the way the poster does — the words rise,
   the bubble and the button pop, one after the other. Leaving, they go first. */
.view[open] .backdrop { animation: fade-in 700ms ease both; }
.view[open] .view__mark,
.view[open] .view__label,
.view[open] .view__statement,
.view[open] .view__copy,
.view[open] .view__line,
.view[open] .view__player,
.view[open] .view__row,
.view[open] .post,
.view[open] .view__big {
  animation: rise-in 500ms cubic-bezier(0.215, 0.61, 0.355, 1) backwards;
  animation-delay: calc(var(--i, 0) * 80ms + 60ms);
}
.view[open] .view__row .view__big { animation: none; }         /* (the row carries it) */
.view[open] .view__close,
.view[open] .btn {
  animation: pop-in 500ms cubic-bezier(0.34, 1.45, 0.64, 1) backwards;
  animation-delay: calc(var(--i, 0) * 80ms + 60ms);
}
.view[open] .btn .glow-btn__label,
.view[open] .btn .glow-btn__arrow {
  animation: rise-in 500ms cubic-bezier(0.215, 0.61, 0.355, 1) backwards;
  animation-delay: calc(var(--i, 0) * 80ms + 110ms);
}
.view__mark { --i: 0; }
.view__close, .view__label, .view__line { --i: 1; }
.view__statement { --i: 2; }
.view__copy { --i: 3; }
.view__player { --i: 3; }
.posts > :nth-child(1) .post { --i: 3; }
.posts > :nth-child(2) .post { --i: 4; }
.posts > :nth-child(3) .post { --i: 5; }
.posts > :nth-child(4) .post { --i: 6; }
.posts > :nth-child(5) .post { --i: 7; }
.view--media .btn { --i: 8; }
.view__bigs > :nth-child(1) { --i: 3; }
.view__bigs > :nth-child(2) { --i: 4; }
.view__bigs > :nth-child(3) { --i: 5; }
.view__bigs > :nth-child(4) { --i: 6; }
.view__bigs > :nth-child(5) { --i: 7; }
.view--contact .view__bigs > :nth-child(3) { --i: 4; }
.view--music .view__bigs > :nth-child(1) { --i: 4; }
.view--music .view__bigs > :nth-child(2) { --i: 5; }
.view--music .view__bigs > :nth-child(3) { --i: 6; }
.view .btn { --i: 4; }
.view--contact .view__bigs > :nth-child(1) { --i: 2; }
.view--contact .view__bigs > :nth-child(2) { --i: 3; }
.view--contact .view__bigs > :nth-child(3) { --i: 4; }
.view--contact .btn { --i: 5; }

/* A page someone arrived on is already painted (through :target) when the
   script takes it over: nothing replays. */
.view.is-static .backdrop,
.view.is-static .view__inner * { animation: none !important; }

.view.is-leaving .backdrop,
.view.is-leaving .view__inner,
.view.is-leaving .echo { animation: view-out 180ms ease both; }
@keyframes view-out { to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .view[open] .backdrop,
  .view[open] .view__mark, .view[open] .view__label, .view[open] .view__statement, .view[open] .view__copy, .view[open] .view__line,
  .view[open] .view__player, .view[open] .view__row, .view[open] .post, .view[open] .view__big,
  .view[open] .view__close, .view[open] .btn,
  .view[open] .btn .glow-btn__label, .view[open] .btn .glow-btn__arrow,
  .view.is-leaving .backdrop, .view.is-leaving .view__inner, .view.is-leaving .echo { animation: none; }
  .view__close { transition: none; }
  .view__close:hover { transform: none; }
}

/* The zoom's stand-in: a flat rectangle laid exactly over the piece of the
   name (or over a door on a page) and scaled with it, so the field of
   colour that fills the window has clean edges however far the bitmap
   behind it is stretched. */
.iris {
  position: fixed;
  z-index: 40;
  pointer-events: none;
  will-change: transform;
}
.iris__icon {                          /* the pressed square's icon, carried into the zoom and faded there */
  position: absolute;
  top: 50%;
  left: 50%;
  width: 60%;
  height: 60%;
  transform: translate(-50%, -50%);
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.iris__icon .spot__icon-dot { fill: currentColor; stroke: none; }
html.is-zooming body { pointer-events: none; }

/* ==========================================================================
   Glow button — the label moves over to make room for an arrow, and a light
   follows the cursor across the face (--x / --y): a soft spot and a bright
   rim. Without a script, eight hover strips inside the button set --x and
   the registered property glides between them; with the script the pointer
   sets both exactly.
   ========================================================================== */
@property --x { syntax: "<length-percentage>"; inherits: true; initial-value: 50%; }
@property --y { syntax: "<length-percentage>"; inherits: true; initial-value: 50%; }

.glow-btn {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  gap: 8px;
  cursor: pointer;
  --x: 50%;
  --y: 50%;
  --glow-spot: rgba(255, 255, 255, 0.28);
  --glow-rim: rgba(255, 255, 255, 0.95);
  --glow-shadow: 0 12px 28px -10px rgba(var(--deep-rgb), 0.6);
  transition:
    transform 260ms var(--ease-settle),
    box-shadow 300ms ease,
    opacity 240ms ease,
    --x 160ms ease-out,
    --y 160ms ease-out;
}
.glow-btn:hover { transform: translateY(-1px); box-shadow: var(--glow-shadow); }
.glow-btn:active { transform: translateY(0) scale(0.985); }
.glow-btn:focus-visible { outline: 2px solid var(--color-ink); outline-offset: 3px; }

.glow-btn__glow,
.glow-btn__ring {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
}
.glow-btn__glow { background: radial-gradient(140px circle at var(--x) var(--y), var(--glow-spot), transparent 60%); }
.glow-btn__ring {
  padding: 1.5px;
  background: radial-gradient(110px circle at var(--x) var(--y), var(--glow-rim), transparent 70%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
}
/* The light fades out where the pointer left it. With the script the last
   position stays set; the hover-strip fallback cannot remember it, so there
   the light simply goes out on leave and only fades in. */
.glow-btn:hover .glow-btn__glow,
.glow-btn:hover .glow-btn__ring { opacity: 1; transition: opacity 300ms ease; }
.glow-btn.is-js .glow-btn__glow,
.glow-btn.is-js .glow-btn__ring { transition: opacity 300ms ease; }

.glow-btn__label,
.glow-btn__arrow { position: relative; z-index: 1; }
.glow-btn__arrow {
  flex: none;
  width: 16px;
  height: 16px;
  transition: transform 260ms var(--ease-settle);
}
.glow-btn:hover .glow-btn__arrow { transform: translateX(2px); }
.glow-btn__arrow--out { transform: rotate(-45deg); }                     /* a link that leaves the site: the arrow points out of the corner */
.glow-btn:hover .glow-btn__arrow--out { transform: rotate(-45deg) translateX(2px); }

.glow-btn__zones { position: absolute; inset: 0; z-index: 2; display: none; }
.glow-btn__zones i { flex: 1; }

@media (hover: hover) and (pointer: fine) {
  .glow-btn__zones { display: flex; }
  .glow-btn:not(.is-js):has(.glow-btn__zones i:nth-child(1):hover) { --x: 6.25%; }
  .glow-btn:not(.is-js):has(.glow-btn__zones i:nth-child(2):hover) { --x: 18.75%; }
  .glow-btn:not(.is-js):has(.glow-btn__zones i:nth-child(3):hover) { --x: 31.25%; }
  .glow-btn:not(.is-js):has(.glow-btn__zones i:nth-child(4):hover) { --x: 43.75%; }
  .glow-btn:not(.is-js):has(.glow-btn__zones i:nth-child(5):hover) { --x: 56.25%; }
  .glow-btn:not(.is-js):has(.glow-btn__zones i:nth-child(6):hover) { --x: 68.75%; }
  .glow-btn:not(.is-js):has(.glow-btn__zones i:nth-child(7):hover) { --x: 81.25%; }
  .glow-btn:not(.is-js):has(.glow-btn__zones i:nth-child(8):hover) { --x: 93.75%; }
  .glow-btn.is-js { transition-property: transform, box-shadow, opacity; }
}

@media (prefers-reduced-motion: reduce) {
  .glow-btn, .glow-btn__arrow,
  .glow-btn:hover .glow-btn__glow, .glow-btn:hover .glow-btn__ring,
  .glow-btn.is-js .glow-btn__glow, .glow-btn.is-js .glow-btn__ring { transition: none; }
  .glow-btn:hover { transform: none; }
  .glow-btn:hover .glow-btn__arrow { transform: none; }
  .glow-btn:hover .glow-btn__arrow--out { transform: rotate(-45deg); }
}

/* ==========================================================================
   Tablet — ≤1024 (iPad 834; column = width − 64): the statement wraps to
   two lines beside the toggle.
   ========================================================================== */
@media (max-width: 1024px) {
  :root {
    --column: calc(var(--vw) - 64px);
    --fs-title: 44px;
    --fs-line: 17px;
    --page-top: 40px;
    --page-bottom: 32px;
  }
  .poster__title { white-space: normal; max-width: 9.4em; }
}

/* ==========================================================================
   Mobile — ≤768 (iPhone 402; column = width − 32): the statement sized to
   sit beside the toggle in two lines, the axis keeps only its decades.
   ========================================================================== */
@media (max-width: 768px) {
  :root {
    --column: calc(var(--vw) - 32px);
    --bubble-h: 48px;
    --fs-title: clamp(24px, (var(--vw) - 32px - 48px - 16px) / 9, 44px);     /* "Management. Web." (8.82em set) fits beside the toggle */
    --fs-line: 16px;
    --fs-axis: 11px;
    --fs-foot: 13px;
    --page-top: 32px;
    --page-bottom: 24px;
    --top-gap: 56px;
  }

  .poster__top { gap: 16px; }
  .menu { top: calc(100% + 30px); }          /* clear of the statement's second line */

  .axis { height: 36px; }
  .axis .is-minor-label { display: none; }

  .footer { margin-top: 14px; }

  .view__mark { font-size: 24px; }
  .view__body { padding: 40px 0 calc(var(--bubble-h) + 16px); }
  .view__statement { margin-top: 20px; font-size: clamp(26px, var(--column) / 13, 34px); }
  .view__copy { margin-top: 22px; font-size: 18px; }
  .view .btn { align-self: stretch; margin-top: 32px; }
  .view__caption { flex-basis: 100%; margin-top: -0.2em; margin-bottom: 0.35em; font-size: 14px; }
  .view--media { --post-h: 240px; }             /* a phone-shaped post is then 135 wide: two and a bit in view, and the page still one screen */
  .posts {
    gap: 14px;
    width: calc(100% + 32px);                     /* (said outright: left to fit its content, the row would grow to the posts' full width and drag the page sideways) */
    margin: 20px -16px 0;                         /* edge to edge: the column's margin is 16 */
    padding: 0 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: 16px;                  /* the snap lines sit inside the padding, or the first post is pulled to the screen's edge */
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .posts::-webkit-scrollbar { display: none; }
  .posts > li { scroll-snap-align: start; }
  .view--media .view__body { padding-bottom: calc(var(--bubble-h) + 16px); }
  .view--music { --bigs: min(64px, var(--column) / 6.2); }
  .view--web { --bigs: min(36px, var(--column) / 10.6); }      /* "909supremacy.com" is the widest: 9.9em */
  .view--contact { --bigs: min(88px, var(--column) / 7.4); }
}

/* ==========================================================================
   Contrast themes (forced colours): backgrounds and shadows are dropped, so
   the bubbles, their bars and the pills get real edges in system colours.
   ========================================================================== */
@media (forced-colors: active) {
  .nav__toggle, .view__close, .btn, .menu__pill { border: 1px solid ButtonText; }
  .spot:hover::before, .spot:focus-visible::before { background: Highlight; }
  .spot__icon { stroke: HighlightText; }
  .spot__icon-dot { fill: HighlightText; }
  .nav__toggle-bar, .view__close span { background: ButtonText; }
  .mark__dot, .view__lead { color: Highlight; }
  .btn--accent, .btn--ink { background: ButtonFace; color: ButtonText; }
  .echo__row { color: Canvas; -webkit-text-stroke-color: CanvasText; }
  .echo__band { background: Canvas; }
  .axis::before, .axis i, .axis i.is-major { background: CanvasText; }
  .footer__sound { color: ButtonText; }
}
