/* ============================================================
   tsojo.studio — shared stylesheet
   White / black grotesque system, one loud accent (#FF3300).
   Aesthetic reference: bleibtgleich.dev — own content & layout.
   ============================================================ */

/* ---------- Design tokens ---------- */

:root {
  /* color
     Contrast against --bg (#fff), measured:
       --ink        21.00:1  AA / AAA
       --ink-soft    5.10:1  AA for all text
       --accent-text 4.97:1  AA for small text
       --accent      3.67:1  large text (24px+) and non-text UI only
       --ink-faint   7.46:1  against --ink; never used on white */
  --bg: #ffffff;
  --ink: #000000;
  --ink-soft: #6e6e6e;
  --ink-faint: #9a9a9a;
  --line: #e0e0e0;
  --pill: #f1f1f1;
  --accent: #ff3300;
  --accent-text: #d92600;

  /* Surfaces that stay dark in both themes. Media wells sit behind photos
     and carry white labels + --ink-faint placeholder text, so flipping them
     to white would strand that text and flash bright before images load.
     The preloader is the same story: a white flash to open a dark page. */
  --media-bg: #000000;
  --overlay-bg: #000000;
  --overlay-ink: #ffffff;

  /* both themes are supported — lets UA widgets follow suit */
  color-scheme: light dark;

  /* type */
  --font-sans: "Helvetica Neue", Helvetica, Arial, -apple-system,
    BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono: "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas,
    monospace;

  /* type scale (fluid) */
  --text-xs: 0.75rem;                          /* 12px */
  --text-sm: 0.9375rem;                        /* 15px — small bold labels */
  --text-base: clamp(1rem, 0.9rem + 0.4vw, 1.125rem);
  --text-lg: clamp(1.25rem, 1.05rem + 0.9vw, 1.75rem);
  --text-xl: clamp(1.75rem, 1.2rem + 2.6vw, 3.5rem);
  --text-name: clamp(2.25rem, 1rem + 6.5vw, 6.5rem);
  --text-display: clamp(3rem, 0.5rem + 12.5vw, 12.5rem);

  /* display type settings */
  --display-tracking: -0.05em;
  --display-leading: 0.96;

  /* spacing scale — the three large steps are fluid so the vertical
     rhythm stays generous on desktop without stranding mobile in
     dead space. Sections use --space-7; hero/statement use --space-8. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2.5rem;
  --space-6: clamp(2.5rem, 1.6rem + 3.6vw, 4rem);
  --space-7: clamp(3.5rem, 1.6rem + 7.6vw, 7rem);
  --space-8: clamp(5rem, 2.2rem + 11.2vw, 11rem);

  /* layout */
  --gutter: clamp(1rem, 4vw, 3rem);
  --max-w: 100rem;

  /* motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur: 0.55s;
}

/* ---------- Dark theme ----------
   Follows the system setting; only tokens change. --ink-faint is deliberately
   left alone: it labels media wells, which stay dark in both themes.
   Contrast against --bg (#0b0b0b), measured:
     --ink         17.58:1  AA / AAA
     --ink-soft     8.28:1  AA / AAA
     --accent-text  6.40:1  AA for all text
     --accent       5.37:1  AA for large text (24px+) and non-text UI */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0b0b0b;
    --ink: #f2f2f2;
    --ink-soft: #a8a8a8;
    --line: #2a2a2a;
    --pill: #1c1c1c;
    --accent-text: #ff5c33;
    --media-bg: #161616;
  }
}

/* An explicit choice from the toggle wins in both directions. The token list
   is a copy of the one above and must stay in sync — CSS cannot alias one
   rule to another. */
:root[data-theme="dark"] {
  --bg: #0b0b0b;
  --ink: #f2f2f2;
  --ink-soft: #a8a8a8;
  --line: #2a2a2a;
  --pill: #1c1c1c;
  --accent-text: #ff5c33;
  --media-bg: #161616;
}

/* ---------- Reset ---------- */

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

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

html.lenis {
  height: auto;
}

.lenis.lenis-smooth {
  scroll-behavior: auto;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: 1.5;
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  overflow-x: hidden;
}

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

a {
  color: inherit;
  text-decoration: none;
}

ul,
ol {
  list-style: none;
}

button {
  font: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

::selection {
  background: var(--accent);
  color: #fff;
}

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

/* ---------- Utilities ---------- */

.skip-link {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 200;
  padding: 0.6em 1.1em;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg);
  font-size: var(--text-sm);
  font-weight: 700;
  transform: translateY(-160%);
  transition: transform 0.2s var(--ease-out);
}

/* :focus, not :focus-visible — a skip link must appear for any focus */
.skip-link:focus {
  transform: translateY(0);
}

.container {
  width: 100%;
  max-width: var(--max-w);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* small bold label (grotesque, sentence case) */
.mono,
.label {
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.muted {
  color: var(--ink-soft);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* section label — small bold + hairline */
.section-label {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: var(--space-5);
}

.section-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
  transform: translateY(-0.25em);
}

.section-label .index {
  color: var(--accent-text);
}

/* ---------- Pills ---------- */

.pill {
  display: inline-flex;
  align-items: center;
  padding: 0.55em 1.15em;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
  transition: background var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}

.pill:hover {
  background: var(--accent);
  color: #fff;
}

.pill--ghost {
  background: var(--pill);
  color: var(--ink);
}

.pill--ghost:hover {
  background: var(--accent);
  color: #fff;
}

.btn {
  /* legacy alias — renders as a black pill */
  display: inline-flex;
  align-items: center;
  padding: 0.55em 1.15em;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bg);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
  transition: background var(--dur) var(--ease-out);
}

.btn:hover {
  background: var(--accent);
  color: #fff;
}

.link-arrow {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4em;
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
  border-bottom: 2px solid var(--ink);
  padding-bottom: 0.2em;
}

.link-arrow::after {
  content: "→";
  transition: transform var(--dur) var(--ease-out);
}

.link-arrow:hover {
  color: var(--accent-text);
  border-color: var(--accent-text);
}

.link-arrow:hover::after {
  transform: translateX(0.35em);
}

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

/* wordmark scrolls away with the page; only the nav pills stay fixed
   (pills are self-contained black-on-white, so they work over anything) */
.site-header {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 50;
}

.site-header .container {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-block: var(--space-3);
}

.site-header .wordmark {
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
}

.site-header nav {
  position: fixed;
  top: var(--space-3);
  right: var(--gutter);
  z-index: 60;
  display: flex;
  gap: var(--space-2);
}

/* three pills + the wordmark don't fit a 375px row at full size */
@media (max-width: 26rem) {
  .site-header .wordmark {
    font-size: 1rem;
  }

  .site-header nav {
    gap: 0.3rem;
  }

  .site-header nav .pill,
  .site-header nav .theme-toggle {
    font-size: var(--text-xs);
  }
}

/* Phone and tablet: tighter pills than the desktop header — 2px off the
   text vertically, 8px horizontally. The toggle takes 2px all round, which
   keeps it circular AND the same height as the pills, since its icon is
   sized in em off the same font-size. Desktop keeps .pill's own padding. */
@media (max-width: 64rem) {
  .site-header nav .pill {
    padding: 2px 8px;
  }

  .site-header nav .theme-toggle {
    padding: 2px;
  }
}

/* ---------- Theme toggle ---------- */

/* Same plate as the nav pills — black on light, white on dark — with the
   accent carrying the icon. Sized in em off the pill's own font-size and
   padding so the circle always matches the pill height exactly.
   The icon shows the theme you would switch TO, not the current one. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0.55em;
  border-radius: 999px;
  background: var(--ink);
  color: var(--accent);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: background var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}

.theme-toggle:hover {
  background: var(--accent);
  color: #fff;
}

.theme-toggle svg {
  display: block;
  width: 1.5em;
  height: 1.5em;
  fill: currentColor;
}

/* the control does nothing without JS, so don't offer it */
html.no-js .theme-toggle {
  display: none;
}

/* light is the default state: offer the moon */
.theme-toggle .icon-sun {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun {
    display: block;
  }

  :root:not([data-theme="light"]) .theme-toggle .icon-moon {
    display: none;
  }
}

:root[data-theme="dark"] .theme-toggle .icon-sun {
  display: block;
}

:root[data-theme="dark"] .theme-toggle .icon-moon {
  display: none;
}

/* ---------- Hero ---------- */

/* Mobile: the hero is content-sized. Forcing 100svh with only three
   blocks left ~300px of blank space that read as a rendering fault,
   not as whitespace. Desktop keeps the full-viewport hero, where the
   floating tiles occupy the right half. */
.hero {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-top: calc(var(--space-6) + var(--space-3));
  padding-bottom: var(--space-7);
}

.hero-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-4);
  padding-bottom: var(--space-6);
}

@media (min-width: 48rem) {
  .hero {
    min-height: 100svh;
    padding-bottom: var(--space-5);
  }

  .hero-top {
    padding-bottom: var(--space-5);
    margin-bottom: auto;
  }
}

.hero-based {
  font-size: var(--text-sm);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  border-left: 1px solid var(--ink);
  padding-left: var(--space-3);
}



/* Cursor trail. The tiles sit behind the type (z-index -1) so the
   headline stays fully legible while work flickers past underneath.
   Hidden until initCursorTrail() adds .is-trail — so touch devices and
   reduced-motion users never see them. */
.hero {
  position: relative;
}

.hero-tiles {
  display: none;
}

.hero-tiles.is-trail {
  display: block;
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: -1;
}

.hero-tile {
  position: absolute;
  top: 0;
  left: 0;
  width: clamp(7rem, 11vw, 12rem);
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--pill);
  opacity: 0;
  will-change: transform, opacity;
}

.hero-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.hero-name {
  font-size: var(--text-name);
  font-weight: 700;
  line-height: var(--display-leading);
  letter-spacing: var(--display-tracking);
  text-align: right;
}

/* lines melt in via a goo filter — no overflow masks (they'd clip the blur) */
.hero-name .line,
.hero-headline .line,
.contact-page h1 .line,
.case-hero h1 .line {
  display: block;
}

.hero-name .line > span,
.hero-headline .line > span {
  display: inline-block;
  will-change: transform;
}

/* Slightly gentler than --text-display: the headline's first word is
   swapped at runtime and the longest ("Digital content") has to fit on
   one line at 320px without wrapping. */
.hero-headline {
  font-size: clamp(2.75rem, 0.4rem + 11.6vw, 11rem);
  font-weight: 700;
  line-height: var(--display-leading);
  letter-spacing: var(--display-tracking);
}

.hero-headline .switch-word {
  white-space: nowrap;
}

.hero-sub {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-5);
  padding-top: var(--space-5);
}

.hero-role {
  font-size: var(--text-sm);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  flex-shrink: 0;
}

/* ---------- Sections ---------- */

.section {
  padding-block: var(--space-7);
}

.section-intro {
  max-width: 46ch;
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-6);
  text-wrap: pretty;
}

/* ---------- Work rail (pinned horizontal slider) ---------- */

.work-rail {
  overflow: hidden;
}

/* The full-viewport height is only needed where the section is actually
   pinned (desktop). On mobile the rail is a native swipe, so forcing
   100svh just stranded whitespace above the heading. */
.rail-pin {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-4);
  padding-block: var(--space-6);
}

@media (min-width: 48rem) {
  .rail-pin {
    min-height: 100svh;
    padding-block: var(--space-5);
  }
}

.rail-title,
.rail-years {
  font-size: var(--text-display);
  font-weight: 700;
  line-height: var(--display-leading);
  letter-spacing: var(--display-tracking);
  text-align: center;
}

.rail-track {
  display: flex;
  gap: var(--space-4);
  padding-inline: var(--gutter);
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}

.rail-track::-webkit-scrollbar {
  display: none;
}

/* when JS pins the section, the track slides via transform instead */
.work-rail.is-pinned .rail-track {
  overflow: visible;
  width: max-content;
}

.rail-card {
  position: relative;
  flex-shrink: 0;
  width: min(78vw, 46rem);
  aspect-ratio: 16 / 10;
  background: var(--media-bg);
  color: var(--overlay-ink);
  overflow: hidden;
  display: block;
}

.rail-card::before {
  content: attr(data-placeholder);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-faint);
}

.rail-card img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.rail-card .card-label {
  position: absolute;
  z-index: 2;
  left: var(--space-4);
  bottom: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  color: #fff;
}

.rail-card .card-year {
  position: absolute;
  z-index: 2;
  right: var(--space-4);
  bottom: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-faint);
}

.rail-card .card-label,
.rail-card .card-year {
  text-shadow: 0 0 12px rgba(0, 0, 0, 0.35);
}

/* ---------- Work grid (work index page) ---------- */

.work-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6) var(--space-4);
}

.work-card {
  display: block;
}

.work-card .cover {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: var(--media-bg);
}

.work-card .cover::before {
  content: attr(data-placeholder);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-faint);
}

.work-card .cover img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
}

.work-card .meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  margin-top: var(--space-3);
}

.work-card h3 {
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.05;
}

.work-card .tags {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink-soft);
  text-align: right;
  flex-shrink: 0;
}

.work-card:hover h3 {
  color: var(--accent-text);
}

@media (min-width: 48rem) {
  .work-grid {
    grid-template-columns: repeat(2, 1fr);
    padding-bottom: var(--space-6);
  }

  .work-grid > .work-card:nth-child(even) {
    transform: translateY(var(--space-6));
  }
}

/* ---------- Statement (giant scattered lines + clock) ---------- */

.statement {
  padding-block: var(--space-8);
}

.statement .st-line {
  display: block;
  font-size: var(--text-display);
  font-weight: 700;
  line-height: var(--display-leading);
  letter-spacing: var(--display-tracking);
}

.statement .st-line.right {
  text-align: right;
}

.statement .st-line.center {
  text-align: center;
}

/* big two-timezone clock row: TPE on the left, MNG on the right */
.clock-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-7);
}

.clock-row .tz-label {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

.clock-row .clock {
  font-size: clamp(2.75rem, 10vw, 9rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  line-height: 1;
}

.clock-note {
  margin-top: var(--space-4);
  text-align: right;
  color: var(--ink-soft);
}

/* live clock: rolling digits + blinking colons + timezone tag */
.clock .digit-mask {
  display: inline-block;
  overflow: hidden;
  height: 1.05em;
  line-height: 1.05;
  vertical-align: bottom;
}

.clock .digit {
  display: inline-block;
  min-width: 0.62em;
  text-align: center;
  will-change: transform;
}

.clock .colon {
  display: inline-block;
  animation: clock-blink 1s steps(1) infinite;
}


@keyframes clock-blink {
  50% {
    opacity: 0.25;
  }
}

@media (prefers-reduced-motion: reduce) {
  .clock .colon {
    animation: none;
  }
}

.statement .st-tail {
  display: block;
  margin-top: var(--space-5);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-soft);
  text-align: right;
}

/* ---------- PDF / document feature (portfolio.uni section) ---------- */

.doc-feature {
  display: grid;
  gap: var(--space-5);
  align-items: start;
}

.doc-feature .doc-cover {
  aspect-ratio: 16 / 10;
  background: var(--pill);
  overflow: hidden;
}

.doc-feature .doc-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.doc-feature .doc-body p {
  max-width: 55ch;
  margin-bottom: var(--space-4);
  font-weight: 500;
}

.doc-feature .doc-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  color: var(--ink-soft);
  margin-bottom: var(--space-4);
}

@media (min-width: 48rem) {
  .doc-feature {
    grid-template-columns: 1fr 1fr;
  }
}

/* ---------- Contact block ---------- */

.contact-cta {
  padding-block: var(--space-7);
}

.contact-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-5);
}

.contact-lede {
  font-size: var(--text-xl);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.03em;
  max-width: 14ch;
}

.contact-write .write-to {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-soft);
  margin-bottom: var(--space-2);
}

.email-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.55em 1.1em;
  border-radius: 999px;
  background: var(--pill);
  font-size: clamp(1rem, 0.8rem + 1.2vw, 1.5rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  transition: background var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}

.email-pill:hover {
  background: var(--accent);
  color: #fff;
}

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

.site-footer {
  padding-top: var(--space-6);
  padding-bottom: var(--space-3);
  overflow: hidden;
}

.footer-wordmark {
  font-size: clamp(2.5rem, 14vw, 16rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.06em;
  text-align: center;
  white-space: nowrap;
  /* real room for the j descender — the footer clips overflow */
  padding-bottom: 0.2em;
  margin-bottom: -0.08em;
}

.footer-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: var(--space-4);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* ---------- Case-study pages ---------- */

.case-hero {
  padding-top: var(--space-8);
  padding-bottom: var(--space-6);
}

.case-hero h1 {
  font-size: var(--text-display);
  font-weight: 700;
  line-height: var(--display-leading);
  letter-spacing: var(--display-tracking);
  text-wrap: balance;
}

.case-hero .hero-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin-top: var(--space-5);
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-soft);
}

.case-cover {
  overflow: hidden;
  background: var(--media-bg);
  aspect-ratio: 16 / 9;
  position: relative;
}

.case-cover::before {
  content: attr(data-placeholder);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-faint);
}

.case-cover img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.meta-row {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
  padding-block: var(--space-5);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  margin-block: var(--space-6);
}

.meta-row dt {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--ink-soft);
  margin-bottom: var(--space-1);
}

.meta-row dd {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.01em;
}

@media (min-width: 48rem) {
  .meta-row {
    grid-template-columns: repeat(4, 1fr);
  }
}

.case-section {
  padding-block: var(--space-6);
}

.case-section h2 {
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.02;
  margin-bottom: var(--space-4);
  max-width: 24ch;
}

.case-section .prose {
  max-width: 62ch;
}

.case-section .prose p + p {
  margin-top: var(--space-3);
}

/* gallery */
.gallery {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: 1fr;
}

.gallery figure {
  position: relative;
  overflow: hidden;
  background: var(--media-bg);
  aspect-ratio: 4 / 3;
}

.gallery figure.wide {
  aspect-ratio: 21 / 9;
}

.gallery figure::before {
  content: attr(data-placeholder);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-faint);
  text-align: center;
  padding: var(--space-3);
}

.gallery img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.gallery figcaption {
  position: absolute;
  z-index: 2;
  left: var(--space-3);
  bottom: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 700;
  color: #fff;
  text-shadow: 0 0 12px rgba(0, 0, 0, 0.45);
}

/* design flats (posters, print pieces): show the whole artwork */
.gallery figure.poster {
  background: var(--pill);
}

.gallery figure.poster img {
  object-fit: contain;
}

.gallery figure.poster figcaption {
  color: var(--ink);
  text-shadow: none;
}

@media (min-width: 48rem) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
  }

  .gallery figure.wide {
    grid-column: 1 / -1;
  }
}

/* next project */
.next-project {
  border-top: 1px solid var(--line);
  padding-block: var(--space-7);
}

.next-project .label {
  color: var(--ink-soft);
  display: block;
  margin-bottom: var(--space-3);
}

.next-project a {
  font-size: var(--text-display);
  font-weight: 700;
  line-height: var(--display-leading);
  letter-spacing: var(--display-tracking);
}

.next-project a:hover {
  color: var(--accent);
}

/* ---------- Contact page ---------- */

.contact-page {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: var(--space-8);
}

.contact-page h1 {
  font-size: var(--text-display);
  font-weight: 700;
  line-height: var(--display-leading);
  letter-spacing: var(--display-tracking);
  margin-bottom: var(--space-5);
}

/* ---------- CV teaser (homepage) ---------- */

.cv-teaser-list {
  display: grid;
  gap: var(--space-4);
  border-top: 1px solid var(--line);
  padding-top: var(--space-4);
  margin-bottom: var(--space-5);
}

.cv-teaser-list dt {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-soft);
  margin-bottom: var(--space-1);
}

.cv-teaser-list dd {
  font-size: var(--text-base);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.3;
}

@media (min-width: 48rem) {
  .cv-teaser-list {
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-5);
  }
}

/* ---------- CV page (Oxford format) ---------- */

.cv {
  max-width: 52rem;
  margin-inline: auto;
  padding-top: var(--space-8);
  padding-bottom: var(--space-7);
}

.cv-head {
  text-align: center;
  padding-bottom: var(--space-5);
  border-bottom: 2px solid var(--ink);
  margin-bottom: var(--space-6);
}

.cv-head h1 {
  font-size: clamp(2rem, 1.2rem + 3.2vw, 3.25rem);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1.05;
}

.cv-head .cv-field {
  font-size: var(--text-lg);
  font-weight: 500;
  color: var(--ink-soft);
  letter-spacing: -0.02em;
  margin-top: var(--space-2);
}

.cv-head .cv-contact {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-2) var(--space-4);
  margin-top: var(--space-4);
  font-size: var(--text-sm);
  font-weight: 700;
}

.cv-head .cv-contact a:hover {
  color: var(--accent-text);
}

.cv-section {
  margin-bottom: var(--space-6);
}

.cv-section > h2 {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--space-4);
}

.cv-section .cv-lede {
  font-size: var(--text-base);
  line-height: 1.6;
  max-width: 64ch;
}

/* one entry: title block left, dates right */
.cv-entry {
  display: grid;
  gap: var(--space-1) var(--space-4);
  margin-bottom: var(--space-4);
}

.cv-entry:last-child {
  margin-bottom: 0;
}

.cv-entry h3 {
  font-size: var(--text-base);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.cv-entry .cv-org {
  font-size: var(--text-base);
  color: var(--ink-soft);
}

.cv-entry .cv-dates {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-soft);
  white-space: nowrap;
}

.cv-entry .cv-detail {
  font-size: var(--text-base);
  line-height: 1.55;
  max-width: 62ch;
  margin-top: var(--space-2);
}

@media (min-width: 40rem) {
  .cv-entry {
    grid-template-columns: 1fr auto;
    align-items: baseline;
  }

  .cv-entry .cv-dates {
    text-align: right;
  }

  .cv-entry .cv-detail {
    grid-column: 1 / -1;
  }
}

/* skills / languages: label + values */
.cv-grid {
  display: grid;
  gap: var(--space-3);
}

.cv-grid > div {
  display: grid;
  gap: var(--space-1);
}

.cv-grid dt {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-soft);
}

.cv-grid dd {
  font-size: var(--text-base);
  line-height: 1.5;
}

@media (min-width: 40rem) {
  .cv-grid > div {
    grid-template-columns: 12rem 1fr;
    gap: var(--space-4);
    align-items: baseline;
  }
}


/* ---------- Client banner (scrolling strip above the footer) ---------- */

.client-banner {
  overflow: hidden;
  padding-block: var(--space-5);
  border-top: 1px solid var(--line);
  /* soften both edges so names enter and leave instead of being cut off */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

/* holds two identical tracks; shifting it -50% lands copy two exactly
   where copy one started, so the loop is seamless */
.client-marquee {
  display: flex;
  width: max-content;
  animation: client-scroll 45s linear infinite;
}

.client-banner:hover .client-marquee {
  animation-play-state: paused;
}

.client-track {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.client-track li {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  padding-inline: var(--space-5);
  font-size: var(--text-base);
  font-weight: 500;
  white-space: nowrap;
  color: var(--ink-soft);
}

/* logos, when they replace the names, sit on the same baseline */
.client-track img {
  display: block;
  max-height: 2.25rem;
  width: auto;
}

@keyframes client-scroll {
  to {
    transform: translateX(-50%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .client-marquee {
    animation: none;
    transform: none;
  }

  /* not moving, so let the names be reached by scrolling instead */
  .client-banner {
    overflow-x: auto;
    -webkit-mask-image: none;
    mask-image: none;
  }
}

@media print {
  .client-banner {
    display: none;
  }
}

.cv-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
}

/* printing the CV should give a clean sheet, not a website */
@media print {
  .site-header,
  .site-footer,
  .skip-link,
  .cv-actions,
  .cursor {
    display: none !important;
  }

  .cv {
    padding: 0;
    max-width: none;
  }

  body {
    font-size: 10.5pt;
  }

  .cv-section {
    break-inside: avoid;
  }

  .cv-entry {
    break-inside: avoid;
  }
}

/* ---------- Link list (external project links) ---------- */

.link-list li {
  border-top: 1px solid var(--line);
}

.link-list li:last-child {
  border-bottom: 1px solid var(--line);
}

.link-list a {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  padding-block: var(--space-3);
}

.link-list .ll-name {
  font-size: var(--text-base);
  font-weight: 700;
  letter-spacing: -0.02em;
}

.link-list .ll-cat {
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--ink-soft);
  margin-left: auto;
  text-align: right;
}

.link-list .ll-ext {
  font-size: var(--text-sm);
  font-weight: 700;
  flex-shrink: 0;
  min-width: 5.5em;
  text-align: right;
}

.link-list a:hover .ll-name,
.link-list a:hover .ll-ext {
  color: var(--accent-text);
}

/* ---------- Preloader ---------- */

.preloader {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--overlay-bg);
  color: var(--overlay-ink);
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  padding: var(--gutter);
  clip-path: inset(0% 0% 0% 0%);
}

.preloader .counter {
  font-size: var(--text-display);
  font-weight: 700;
  letter-spacing: var(--display-tracking);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* Scroll-lock is scoped to .js: only JS clears .is-loading, so without
   JS this would leave the page permanently unscrollable. */
html.js body.is-loading {
  overflow: hidden;
}

.no-js .preloader {
  display: none;
}

/* ---------- Custom cursor ---------- */

.cursor {
  position: fixed;
  z-index: 200;
  top: 0;
  left: 0;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--accent);
  pointer-events: none;
  transform: translate(-50%, -50%);
  transition: width 0.25s var(--ease-out), height 0.25s var(--ease-out),
    opacity 0.25s var(--ease-out);
  opacity: 0;
  mix-blend-mode: multiply;
}

.cursor.is-visible {
  opacity: 1;
}

.cursor.is-hover {
  width: 56px;
  height: 56px;
}

@media (hover: none), (pointer: coarse) {
  .cursor {
    display: none;
  }
}

/* Must sit after the rule above to win on source order: multiply darkens
   whatever is beneath it, so on a dark ground the dot would disappear. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .cursor {
    mix-blend-mode: screen;
  }
}

:root[data-theme="dark"] .cursor {
  mix-blend-mode: screen;
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .cursor {
    display: none;
  }
}
