/* e-r.studio
   Two colours, one family, one scale.

   The hierarchy is carried by size and colour alone:
     - six type steps, each with one job and a visible gap between them
     - --ink-muted means metadata and navigation; --ink means things you read
     - no bold anywhere on the page; the wordmark is SVG
   Volume lives at the statement and the email. Everything between them is calm. */

@font-face {
  font-family: "Strawford";
  src: url("fonts/strawford-regular-webfont.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Strawford";
  src: url("fonts/strawford-bold-webfont.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  /* the brief's stated value: the supplied wordmark SVG carries no fill,
     so there was no yellow in the file to sample */
  --paper: #F5F24E;
  --ink: #1A1A1A;
  /* the same black at reduced opacity, never a grey.
     0.70 measures 5.95:1 on the yellow — comfortable AA. */
  --ink-muted: rgba(26, 26, 26, 0.70);

  /* ---- the scale ----
     One formula per step, so the ratios hold at every viewport width.
     Nothing on the page may set a font-size outside this list. */
  --t-meta:    clamp(0.875rem, 0.84rem + 0.16vw, 0.9375rem); /* labels, years, city, footer */
  --t-base:    clamp(1rem,     0.95rem + 0.25vw, 1.125rem);  /* leads, contact cta */
  --t-item:    clamp(1.125rem, 1.02rem + 0.52vw, 1.375rem);  /* every content item */
  --t-intro:   clamp(1.3rem,   1.10rem + 1.00vw, 1.75rem);   /* the intro lines */
  --t-action:  clamp(1.6rem,   1.25rem + 1.75vw, 2.25rem);   /* the email */
  --t-display: clamp(2rem,     1.28rem + 3.60vw, 3.5rem);    /* the statement */

  --rail: clamp(6rem, 10vw, 9.5rem);
  --gap: clamp(1.25rem, 3vw, 3rem);
  --measure: 76rem;
  --edge: clamp(1.5rem, 5vw, 4.5rem);

  /* zones group harder than the sections inside them */
  --rhythm-zone: clamp(4.5rem, 9vw, 8rem);
  --rhythm-section: clamp(2.75rem, 5.5vw, 4.5rem);

  --wave-ratio: 4.9229; /* exact bounding box of the path, w/h */
  --wave-tile: 40.117;  /* its width in user units: one marquee tile */
}

/* the scrollbar is part of the page, not chrome bolted onto it */
html {
  scrollbar-color: var(--ink) var(--paper);
  scrollbar-width: thin;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--paper); }
::-webkit-scrollbar-thumb { background: var(--ink); border: 0; }
::-webkit-scrollbar-thumb:hover { background: var(--ink-muted); }
::-webkit-scrollbar-corner { background: var(--paper); }

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

html {
  background: var(--paper);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Strawford", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-weight: 400;
  font-size: var(--t-base);
  line-height: 1.45;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: from-font;
}

a:hover { text-decoration-thickness: 0.09em; }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

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

.sprite { position: absolute; }

/* ---------- the wave ---------- */
/* Never a divider. It stands for the missing job title, and nothing else. */

.wave {
  display: inline-block;
  height: 0.5em;
  width: calc(0.5em * var(--wave-ratio));
  vertical-align: baseline; /* puts the glyph's centre at mid x-height */
  margin: 0 0.14em;
  fill: currentColor;
  flex: none;
  overflow: hidden; /* the marquee never leaves the mark's own box */
}

/* ---------- skip link / language toggle ---------- */

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
}

.skip:focus {
  left: var(--edge);
  top: 0.75rem;
  padding: 0.35rem 0;
  background: var(--paper);
}

.langtoggle {
  position: fixed;
  top: clamp(1rem, 2.5vw, 2rem);
  /* pinned to the measure, not the viewport: at 2560px a viewport-anchored
     toggle strands itself a full 1000px away from the content it belongs to */
  right: max(var(--edge), calc((100% - var(--measure)) / 2 + var(--edge)));
  z-index: 5;
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0.35rem;
  margin: 0;
  font: inherit;
  font-size: var(--t-meta);
  color: var(--ink-muted);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.langtoggle:hover { color: var(--ink); }

/* ---------- shape of the page ---------- */

.hero,
.section,
.footer,
.fallback {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--edge);
}

/* nothing renders until the data is in, so the page never flashes an empty skeleton
   and empty controls never land in the tab order */
body:not(.ready) .hero,
body:not(.ready) main,
body:not(.ready) .footer { display: none; }

/* ---------- zone 1: identity ---------- */
/* wordmark, statement and intro all sit flush on the page's left edge, so the
   two-column grid visibly begins where the proof starts */

.hero {
  padding-top: clamp(4rem, 11vw, 12rem);
  padding-bottom: 0;
}

/* an <h1>, so the UA hands it font-size:2em and font-weight:bold. It holds only
   the SVG and a screen-reader name, but left alone those defaults add a seventh
   step to the scale and the page's only bold. Neutralise both. */
.wordmark {
  /* indented onto the content column, so its left edge lands on the same line as
     the leads and items below rather than at an arbitrary offset */
  /* the gap below must exceed the mark's own height, or the statement crowds it */
  margin: 0 0 clamp(3.5rem, 9vw, 9rem) calc(var(--rail) + var(--gap));
  font-size: var(--t-base);
  font-weight: 400;
  line-height: 0;
}

.wordmark svg {
  display: block;
  width: clamp(11rem, 26vw, 21rem);
  height: auto;
  fill: var(--ink);
}

.statement {
  font-size: var(--t-display);
  line-height: 1.1;
  letter-spacing: -0.02em;
  max-width: 18ch;
  text-wrap: balance;
}

/* doubled class on purpose: .section further down sets display:grid at the same
   specificity, and without this the intro becomes a grid item in the rail */
.section.section--intro {
  display: block;
  margin: clamp(1.75rem, 4vw, 3rem) auto var(--rhythm-zone);
}

/* Authored as four lines, so it has to READ as four. At 34ch every one of them
   wrapped to two, which turned the stanza into an eight-line block held together
   by a 7px gap. Wide enough that most land on one line, and a gap big enough that
   the four units still separate when they don't. */
.intro {
  font-size: var(--t-intro);
  line-height: 1.35;
  letter-spacing: -0.005em;
  max-width: 68ch;
}

.intro__line + .intro__line { margin-top: 0.65em; }

/* ---------- zone 2: proof ---------- */

.section {
  display: grid;
  grid-template-columns: var(--rail) 1fr;
  column-gap: var(--gap);
  margin-block: var(--rhythm-section);
}

/* navigation: recedes on purpose */
.label {
  grid-column: 1;
  margin: 0;
  font-size: var(--t-meta);
  font-weight: 400;
  color: var(--ink-muted);
  line-height: 1.5;
  letter-spacing: 0.02em;
}

/* the voice. Full ink: these carry the joke and should not recede. */
.lead {
  grid-column: 2;
  font-size: var(--t-base);
  color: var(--ink);
  margin-bottom: clamp(1.1rem, 2vw, 1.6rem);
  max-width: 46ch;
}

.body,
.nameset,
.list { grid-column: 2; }

/* every content item on the page, in every section, is set the same way */
.item {
  display: block;
  font-size: var(--t-item);
  line-height: 1.3;
  max-width: 40ch;
}

/* the conference rides the eyebrow beside the year; not a number, so no tabular */
.conf { font-variant-numeric: normal; }

/* metadata: the year or the span above its item */
.year {
  display: block;
  font-size: var(--t-meta);
  line-height: 1.4;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
  margin-bottom: 0.15em;
}

.entry + .entry { margin-top: clamp(1rem, 1.8vw, 1.5rem); }

/* writing: the one link on the page */
.claim {
  font-size: var(--t-item);
  line-height: 1.3;
  max-width: 40ch;
}

.claim-link {
  margin-top: 0.7em;
  font-size: var(--t-base);
}

/* clients: a name set, not a list */
.nameset {
  font-size: var(--t-item);
  line-height: 1.9;
  /* wide enough that the names read as a run rather than one per line */
  max-width: 52ch;
}

/* name + its trailing wave: never broken apart */
.nameset .pair { white-space: nowrap; }

.nameset .wave {
  color: var(--ink-muted);
  margin: 0 0.4em;
}

/* the wink at the end of the run: muted so it can never be mistaken for a client */
.nameset .invite { color: var(--ink-muted); }

/* ---------- zone 3: action ---------- */

#sec-contact { margin-top: var(--rhythm-zone); }

.cta {
  font-size: var(--t-base);
  margin-bottom: 0.7em;
}

.email {
  font-size: var(--t-action);
  line-height: 1.2;
  letter-spacing: -0.015em;
}

/* the city rides the email's line at the email's size, muted — dropping it to
   metadata size next to 36px type just reads as a mistake */
.email .dash,
.city { color: var(--ink-muted); }

/* ---------- footer ---------- */

.footer {
  padding-bottom: clamp(3rem, 6vw, 5rem);
  padding-top: clamp(2rem, 4vw, 3rem);
  font-size: var(--t-meta);
  color: var(--ink-muted);
}

/* ---------- fallbacks ---------- */
/* Deliberately carries nothing dated: a duplicate of the lists is exactly what
   would go stale. Name, what it is, and a way to write. */

.fallback {
  padding-top: clamp(3rem, 8vw, 6rem);
  padding-bottom: clamp(3rem, 8vw, 6rem);
  display: grid;
  gap: 0.9em;
  justify-items: start;
}

/* sits on the same left edge as the rest of the page, not centred */
.fallback > p { max-width: 46ch; }

.fallback__name {
  font-weight: 700;
  font-size: var(--t-action);
}

.fallback--js { display: none; }
body.failed .fallback--js { display: grid; }

/* ---------- impressum ---------- */

/* same left edge and outer measure as index, so the two pages sit on one grid */
.legal {
  max-width: var(--measure);
  margin-inline: auto;
  padding: clamp(3rem, 8vw, 6rem) var(--edge) clamp(3rem, 8vw, 6rem);
}

.legal h1,
.legal h2,
.legal p { max-width: 52ch; }

.legal h1 {
  font-weight: 700;
  font-size: var(--t-action);
  margin: 0 0 1.5em;
  letter-spacing: -0.015em;
}

.legal h2 {
  font-weight: 400;
  font-size: var(--t-meta);
  color: var(--ink-muted);
  letter-spacing: 0.02em;
  margin: 2.25em 0 0.4em;
}

.legal p { margin-bottom: 0.35em; }

.legal .back { margin-top: 3em; }

/* ---------- one column ---------- */

@media (max-width: 720px) {
  :root {
    --rhythm-zone: 3.25rem;
    --rhythm-section: 2.25rem;
  }

  .hero { padding-top: 3rem; }

  /* no rail to align to on one column */
  .wordmark { margin-left: 0; margin-bottom: 2rem; }

  .section { grid-template-columns: 1fr; column-gap: 0; }

  .label,
  .lead,
  .body,
  .nameset,
  .list { grid-column: 1; }

  .label { margin-bottom: 0.35em; }

  /* full-width content would scroll underneath a pinned toggle, and there are no
     boxes to hide behind — so it scrolls away with the hero instead */
  .langtoggle { position: absolute; }

  .statement,
  .intro { max-width: none; }
}

/* ---------- motion: a marquee inside the mark ---------- */
/* The wave is about half an ex tall, so hover would essentially never fire.
   The script measures how close the pointer is and adds .is-near, which starts
   the marquee. Two copies of the glyph sit one tile apart inside a viewBox
   exactly one tile wide, so translating by one tile loops seamlessly, and the
   SVG viewport clips it to the space the mark already occupies. Nothing reflows,
   and nothing moves outside its own box. */

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .wave__track {
    animation: wave-marquee 2.4s linear infinite;
    animation-play-state: paused;
    will-change: transform;
  }

  .wave.is-near .wave__track { animation-play-state: running; }
}

@keyframes wave-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(var(--wave-tile) * -1px)); }
}
