/* ==========================================================================
   Marela Glavaš — portfolio
   The magnetic field lives in field.js and reads --line / --north / --south
   from here at startup.

   GRID SYSTEM
   One 12-column grid governs every section: a section splits 3 columns
   (label rail) + 9 columns (content field). Inside the content field,
   layouts use halves or thirds of the field — services = 2 × half,
   about = a third + two-thirds. Gutter and vertical rhythm come from two
   tokens (--gut, --stack) so spacing is consistent, not per-section.

   TYPE SCALE (rationalized, ~1.25 ratio)
   --t-xs   labels / eyebrows / numbers      --t-l    h3, client names
   --t-s    meta, captions, footnotes        --t-xl   section h2
   --t-base body                             --t-sub  hero tagline
   --t-m    ledes / emphasis                 name     display, viewport-scaled
   Leading: display 1.02–1.1, subheads 1.25, body 1.5, lists 1.35.
   ========================================================================== */

:root {
  /* Palette: neutral paper, poles remapped to the lilac family. */
  --paper:   #EFEDE7;
  --paper-2: #E7E4DC;
  --ink:     #1B1F27;
  --ink-2:   #5B606B;
  --ink-3:   #8B8F98;
  --line:    #8A90A0;
  --north:   #6E56B4;
  --south:   #C9AEF2;
  --lilac:   #D4B8FF;
  --lilac-wash: rgba(212, 184, 255, 0.20);
  --violet:  #6E56B4;
  --rule:    rgba(27, 31, 39, 0.14);

  --display: "Hanken Grotesk", "Segoe UI", system-ui, sans-serif;
  --body:    "Hanken Grotesk", "Segoe UI", system-ui, sans-serif;

  /* Type scale */
  --t-xs:   0.72rem;
  --t-s:    0.875rem;
  --t-base: 1rem;
  --t-m:    1.125rem;
  --t-l:    clamp(1.2rem, 1.05rem + 0.7vw, 1.5rem);
  --t-xl:   clamp(1.55rem, 1.2rem + 1.5vw, 2.25rem);
  --t-sub:  clamp(1.2rem, 1rem + 1.1vw, 1.8rem);

  /* Layout */
  --gut:    clamp(1rem, 2vw, 1.5rem);
  --gutter: clamp(1.25rem, 5vw, 4.5rem);
  --stack:  clamp(2.75rem, 7vh, 5rem);
  --measure: 58ch;
}

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

/* The [hidden] attribute only carries a UA-level display: none, so ANY author
   display rule outranks it and the element stays on screen while JS believes
   it is closed. This has caught two elements already - .pr-panel and .brief,
   both display: grid - so enforce it once here rather than patching each new
   case as it is discovered. */
[hidden] { display: none !important; }

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  min-height: 100%;
  background: transparent;
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--t-base);
  line-height: 1.5;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

p, li { text-wrap: pretty; }        /* no orphans in copy blocks */

::selection { background: var(--lilac); color: var(--ink); }

/* --- the field ----------------------------------------------------------- */

#field {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: block;
  pointer-events: none;
}

.veil {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 40%,
      rgba(239, 237, 231, 0) 0%,
      rgba(239, 237, 231, 0.34) 62%,
      rgba(239, 237, 231, 0.72) 100%);
}

.page {
  position: relative;
  z-index: 2;
  padding: 0 var(--gutter);
  margin: 0 auto;
  max-width: 84rem;
}

/* --- shared bits --------------------------------------------------------- */

.eyebrow {
  font-family: var(--body);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--violet);
  margin: 0;
}

a {
  color: inherit;
  text-decoration-color: var(--rule);
  text-underline-offset: 0.28em;
  text-decoration-thickness: 1px;
  transition: text-decoration-color 160ms ease, color 160ms ease;
}

a:hover { text-decoration-color: var(--violet); }

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

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

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

.skip:focus {
  left: var(--gutter);
  top: 0.75rem;
  z-index: 20;
  background: var(--paper);
  padding: 0.5rem 0.85rem;
  font-size: var(--t-s);
  border: 1px solid var(--rule);
}

/* --- masthead ------------------------------------------------------------ */

/* Solid safe zone: the field never runs behind the nav. */
.masthead {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}

.masthead .bar {
  max-width: 84rem;
  margin: 0 auto;
  padding: 1rem var(--gutter);
  display: flex;
  align-items: baseline;   /* true baseline alignment; the mark is absolutely
                              positioned so it cannot skew the baseline */
  justify-content: space-between;
  gap: 1.5rem;
}

/* The Pull mark is always present; the name text only appears once the
   hero name has scrolled out of view, so it is never on screen twice. */
.wordmark {
  font-family: var(--display);
  font-weight: 650;
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  text-decoration: none;
  position: relative;
  padding-left: 1.85rem;   /* room for the absolutely-positioned mark */
}

.wm-mark {
  width: 1.3rem;
  height: 1.3rem;
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-54%);
}

.wm-text {
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.2em);
  transition: opacity 240ms ease, transform 240ms ease, visibility 0s 240ms;
}

.masthead.show-name .wm-text {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity 240ms ease, transform 240ms ease;
}

/* Lilac caron on the wordmark, same solid triangle as the hero's. */
.wm-s { position: relative; display: inline-block; line-height: 1; }

.wm-caron {
  position: absolute;
  left: 50%;
  top: -0.16em;
  width: 0.5em;
  height: 0.22em;
  transform: translateX(-50%);
  background: var(--lilac);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}

.masthead nav {
  display: flex;
  align-items: center;   /* links must not stretch to the pill's height */
  gap: clamp(0.8rem, 3vw, 2.25rem);
}

.masthead nav a {
  font-family: var(--body);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
  white-space: nowrap;
}

.masthead nav a:hover { color: var(--ink); border-bottom-color: var(--lilac); }

/* Contact as a lilac lozenge. */
.masthead nav a.nav-cta {
  background: var(--lilac);
  color: var(--ink);
  border-radius: 999px;
  padding: 0.5em 1.1em;
  border-bottom: 0;
  /* The plain links carry 3px of padding+border below their text, so their
     centred text rides high; lift the pill's text to the same optical line. */
  transform: translateY(-1.5px);
  transition: background-color 160ms ease, color 160ms ease;
}

.masthead nav a.nav-cta:hover {
  background: var(--violet);
  color: var(--paper);
}

/* Anchor clearance. The masthead is sticky, so a nav jump has to stop
   short of the section top or the bar sits over the first line.

   The label rail is sticky at 4.5rem and always clears it. The content
   field does not: it starts one --stack below the section top, and --stack
   is viewport-height based (44px on a short screen), which is less than the
   62px bar. Panelled sections get away with it on panel padding; Projects
   has no panel, so its h2 was landing behind the bar. 2rem covers the
   shortest case with air to spare and costs the taller ones nothing. */
.section, #top { scroll-margin-top: 2rem; }

/* Project rows need much more: a row has almost no padding of its own, so
   unlike a section it cannot absorb any of the bar. Its margin has to cover
   the whole bar height plus breathing room. */
.pr { scroll-margin-top: 5rem; }

/* Below 30rem the nav wraps to a second row and the bar grows to ~94px,
   so both jumps need more room again. */
@media (max-width: 30rem) {
  .section, #top { scroll-margin-top: 4rem; }
  .pr { scroll-margin-top: 7rem; }

  .masthead .bar { flex-wrap: wrap; row-gap: 0.6rem; }
  /* Full-width second row, spread edge to edge: Services stays under the
     mark and Contact reaches the right margin, so no trailing gap. */
  .masthead nav {
    flex: 1 0 100%;
    justify-content: space-between;
    gap: 0.4rem;
  }
  .masthead nav a { letter-spacing: 0.06em; }
  .masthead nav a.nav-cta { padding: 0.5em 0.85em; }
}

/* --- hero ---------------------------------------------------------------- */

.hero {
  min-height: min(88vh, 48rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(2.5rem, 8vh, 5rem) 0 var(--stack);
}

.hero-name {
  font-family: var(--display);
  font-weight: 700;
  text-transform: uppercase;
  font-size: clamp(3rem, 18vw, 14.5rem);
  font-kerning: none;   /* spans break kern pairs anyway; keep ALL pairs manual */
  line-height: 0.8;
  letter-spacing: -0.015em;
  /* Optical left alignment: the M's ink starts 0.0625em inside its box
     (measured side bearing), which reads as a broken margin at this size.
     Pull the whole block left by that amount so the ink hits the gutter;
     both lines shift equally, so the A/V apex alignment is untouched. */
  margin: clamp(1rem, 2.5vh, 1.75rem) 0 0 -0.0625em;
}

/* Staggered lockup: MARELA hugs the left edge; GLAVAS is indented so its
   V apex sits directly under the apex of MARELA's final A. The indent is
   in em — glyph geometry scales exactly with the type size, so the
   alignment is device-independent (ink-true: canvas glyph metrics with
   kerning and tracking give 1.3650em). The 18vw type size makes the 4.98em total lockup width fill
   the 90vw content column at every viewport. The final S clears MARELA's
   end, leaving open air for the caron. */
/* L-A base clearance: the default tracking lets the L's foot join the
   A's; open just that pair slightly in both lines. */
.k-la { letter-spacing: 0.01em; }
.k-av { letter-spacing: -0.05em; }
.k-va { letter-spacing: -0.05em; }

/* The name is a lockup, never a paragraph: nowrap stops the fallback face
   (wider than Hanken) from restructuring it into three lines during the
   font swap. With Hanken loaded there is 30px+ slack at every width. */
/* The hero name's visible glyphs spell "Glavas": the caron is a clip-path
   shape (.hn-caron), not a character, so anything extracting text from the
   h1 - crawlers included - loses the diacritic and reads the name wrong.

   The fix lives in the markup, not here: a clipped U+030C COMBINING CARON
   sits immediately after the bare "s", so extracted text composes to
   "Glavaš" while nothing renders. It reuses .visually-hidden rather than
   needing a rule of its own, and if the stylesheet ever fails to load the
   combining mark simply renders on the s - the correct spelling, unstyled. */

.hn-line { display: block; white-space: nowrap; }
.hn-line:last-of-type { margin-left: 1.316em; }

/* The caron is drawn, not typed: a lilac chevron floating in the tight gap
   above the final S — the diacritic as the graphic signature of the name.
   (The accessible name lives on the h1's aria-label.) */
.hn-s { position: relative; display: inline-block; }

/* Solid triangle, matching the mark's lower segment. */
.hn-caron {
  position: absolute;
  left: 50%;
  top: -0.135em;
  width: 0.30em;
  height: 0.135em;
  transform: translateX(-50%);
  background: var(--lilac);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}

.tagline {
  font-family: var(--display);
  font-weight: 500;
  font-size: var(--t-sub);
  line-height: 1.25;
  letter-spacing: -0.015em;
  /* One thought, one line on desktop; balanced wrap only when space runs
     out. */
  max-width: 60ch;
  text-wrap: balance;
  margin: clamp(1.5rem, 4vh, 2.5rem) 0 0;
}

.hero .lede {
  margin: 1.1rem 0 0;
  max-width: 52ch;
  color: var(--ink-2);
  font-size: var(--t-m);
}

/* --- sections: the 12-column master grid --------------------------------- */

.section {
  padding: var(--stack) 0;
  border-top: 1px solid var(--rule);
}

.section h2 {
  font-family: var(--display);
  font-weight: 620;
  font-size: var(--t-xl);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0 0 clamp(1.5rem, 4vh, 2.5rem);
  max-width: 28ch;
  text-wrap: balance;
}

@media (min-width: 52rem) {
  .section {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 0 var(--gut);
  }

  .section > .head {
    grid-column: 1 / 4;                /* label rail: columns 1–3 */
    align-content: start;
    position: sticky;
    top: 4.5rem;
    height: max-content;
  }

  .section > .body {
    grid-column: 4 / 13;               /* content field: columns 4–12 */
  }
}

@media (max-width: 51.99rem) {
  .section > .head { margin-bottom: 1rem; }
}

/* Safe zones: content-heavy sections sit on solid blocks so small text is
   never fighting the field. Hero and the project deck stay open to it. */
.panel {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 6px;
  padding: clamp(1.35rem, 3vw, 2.5rem);
}

#about .panel { background: var(--paper-2); }

#brief .panel {
  background: var(--lilac-wash);
  border-color: rgba(110, 86, 180, 0.18);
}

/* --- services ------------------------------------------------------------ */

.services {
  display: grid;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  align-items: start;
}

@media (min-width: 40rem) {
  .services { grid-template-columns: 1fr 1fr; }   /* two halves of the field */
}

.service h3 {
  font-family: var(--display);
  font-weight: 650;
  font-size: var(--t-l);
  letter-spacing: -0.015em;
  line-height: 1.15;
  margin: 0;
}

.service-verb {
  margin: 0.2rem 0 0.85rem;
  color: var(--ink-2);
  font-style: italic;
  font-size: var(--t-s);
}

.service > ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.service > ul > li {
  padding: 0.32rem 0;
  border-top: 1px solid var(--rule);
  line-height: 1.35;
  font-size: var(--t-s);
}

.service > ul > li:last-child { border-bottom: 1px solid var(--rule); }

/* The extras keep the main lists' rhythm but drop the keylines, so they
   read as a quieter appendix to Copywriting. */
.service-extras { margin-top: 1.1rem; }

.extras-lead {
  font-size: var(--t-s);
  font-style: italic;
  color: var(--ink-2);
  margin: 0 0 0.1rem;
}

.service-extras ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.service-extras li {
  padding: 0.32rem 0;
  line-height: 1.35;
  font-size: var(--t-s);
  color: var(--ink-3);
}

/* --- projects: the stacking deck ----------------------------------------- */

/* Footnote under the table rather than a subhead above it: it qualifies
   the rows, so it reads better after them and quieter than they do. */
.work-note {
  margin: 0.9rem 0 0;
  color: var(--ink-3);
  font-size: var(--t-xs);
  letter-spacing: 0.02em;
  max-width: 44ch;
}

/* Frosted glass on the TABLE only: a light paper tint plus backdrop blur
   keeps the rows readable while the field stays live around the heading
   and the brand grid. */
.projects-list {
  list-style: none;
  margin: 0;
  padding: 0 0.35rem;
  border-top: 1px solid var(--rule);
  background: rgba(239, 237, 231, 0.45);
  border-radius: 6px;
}

@supports (backdrop-filter: blur(4px)) or (-webkit-backdrop-filter: blur(4px)) {
  .projects-list {
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
  }
}

/* Compact rows: the contribution — the tasks delivered — carries the
   emphasis; imagery is reduced to a pair of small thumbnails. */
.pr {
  display: grid;
  grid-template-columns: 2.25rem minmax(11rem, 16rem) 1fr auto;
  gap: 0.35rem 1.25rem;
  align-items: start;
  padding: 0.95rem 0.25rem;
  border-bottom: 1px solid var(--rule);
}

.pr-no {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--violet);
  margin: 0;
  padding-top: 0.2rem;
}

.pr-client {
  font-family: var(--display);
  font-weight: 650;
  font-size: var(--t-base);
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin: 0;
}

.pr-project {
  color: var(--ink-2);
  font-size: var(--t-s);
  margin: 0.1rem 0 0;
}

.pr-category {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0;
  justify-self: end;
  text-align: right;
  padding-top: 0.2rem;
}

.pr-contribution {
  color: var(--ink);
  font-size: var(--t-s);
  line-height: 1.45;
  max-width: 44ch;
  margin: 0.1rem 0 0;
}

@media (max-width: 46rem) {
  .pr { grid-template-columns: 2.25rem 1fr; }
  .pr-id { grid-column: 2; }
  .pr-contribution { grid-column: 2; }
  .pr-category { grid-column: 2; justify-self: start; text-align: left; padding-top: 0; }
}

@media (hover: hover) and (pointer: fine) {
  .pr[data-images] { cursor: default; }
  .pr[data-images]:hover { background: rgba(212, 184, 255, 0.14); }
}

/* Wider than 3:2, so the sides get cropped: hold the window left of centre
   to leave air around the phones rather than trimming them evenly. */
img[src*="afp-2"] { object-position: 20% 50%; }

/* Hovered row's imagery appears in the otherwise-empty label rail,
   riding with the sticky section head. */
.proj-preview {
  margin-top: 1rem;
  display: grid;
  gap: 0.5rem;
  opacity: 0;
  transition: opacity 200ms ease;
}

.proj-preview.is-on { opacity: 1; }

.proj-preview img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: 3px;
  border: 1px solid var(--rule);
}

@media (max-width: 51.99rem) {
  .proj-preview { display: none; }   /* no rail on small screens */
}

/* Touch: the tapped row's image pair opens inline beneath it. */
.pr[role="button"] { cursor: pointer; }

.pr.is-open { background: rgba(212, 184, 255, 0.14); }

.pr-panel {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

.pr-panel img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: 3px;
  border: 1px solid var(--rule);
}

/* --- client grid (placeholder text until logos arrive) -------------------- */

.clients { margin-top: clamp(2rem, 5vh, 3rem); }

.clients-label {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--violet);
  margin: 0 0 0.85rem;
}

.client-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: 0.5rem;
}

@media (max-width: 46rem) {
  .client-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 0.4rem 0.6rem;
  }
  .client-grid li { padding: 0.6rem 0.4rem; min-height: 2.9rem; font-size: var(--t-xs); }
  .client-grid img { max-height: 1.15rem; max-width: 92%; }
  .client-grid img[src*="one-nz"]        { max-height: 0.8rem; }
  .client-grid img[src*="air-new-zealand"] { max-height: 0.85rem; }
  .client-grid img[src*="evt"]           { max-height: 0.9rem; }
  .client-grid img[src*="koko-body"]     { max-height: 1.1rem; }
  .client-grid img[src*="nzmp"]          { max-height: 1rem; }
  .client-grid img[src*="tal"]           { max-height: 0.85rem; }
  .client-grid img[src*="yamaha-agriculture"] { max-height: 1.4rem; }
  .client-grid img[src*="floridays"]     { max-height: 1.6rem; }
}

.client-grid li {
  border: 1px solid var(--rule);
  border-radius: 3px;
  background: rgba(239, 237, 231, 0.8);
  padding: 0.85rem 1rem;
  font-size: var(--t-s);
  color: var(--ink-2);
  text-align: center;
  line-height: 1.3;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 3.4rem;
}

/* No boxes anywhere: a name set as type reads as a wordmark beside the
   marks, where a bordered tile read as a gap waiting to be filled. */
.client-grid li {
  border: 0;
  background: none;
  font-family: var(--display);
  font-weight: 560;
  font-size: 1rem;
  letter-spacing: -0.005em;
  color: var(--ink);
  opacity: 0.78;
}

/* Every mark renders as a black silhouette, so the wall reads as one set.
   Logos built from solid shapes (the Anchor FP badge, Fonterra's swoosh)
   would blob under this, because brightness(0) keeps only the alpha - those
   two are pre-processed into line art by tools/ink-extract.py instead, so
   their white knockouts stay as transparent holes. */
/* brightness(0) drives every mark to pure black; the wall's one piece of
   real type (Openly) is brand ink #1B1F27, so the two did not match and
   pure black is off-palette anyway. 0.68 opacity composites black onto the
   paper at rgb(76,76,74) - the same value ink at 0.78 lands on - so the
   marks and the wordmark now read as one colour. */
.client-grid img {
  max-height: 1.5rem;
  max-width: 82%;
  width: auto;
  filter: brightness(0);
  opacity: 0.68;
}

/* Optical balancing: equalise visual mass, not box height. Wide wordmarks
   come down; stacked lockups get more; TAL is sized so its 65px source
   renders pixel-sharp on hi-DPI screens. */
.client-grid img[src*="one-nz"]        { max-height: 0.95rem; }
.client-grid img[src*="air-new-zealand"] { max-height: 1.05rem; }
.client-grid img[src*="evt"]           { max-height: 1.1rem; }
.client-grid img[src*="koko-body"]     { max-height: 1.35rem; }
.client-grid img[src*="nzmp"]          { max-height: 1.25rem; }
.client-grid img[src*="tal"]           { max-height: 1.05rem; }
.client-grid img[src*="yamaha-agriculture"] { max-height: 1.8rem; }
.client-grid img[src*="floridays"]     { max-height: 2.1rem; }
.client-grid img[src*="anchor-food"]   { max-height: 2rem; }
.client-grid img[src*="fonterra"]      { max-height: 2.2rem; }

/* --- quotes marquee (inside About) ---------------------------------------- */

.quotes {
  margin-top: clamp(1.5rem, 4vh, 2.25rem);
  border-top: 1px solid var(--rule);
  padding-top: 1.1rem;
  overflow: hidden;
}

/* Spacing is a trailing margin on each item, NOT a flex gap. A gap sits
   only BETWEEN items, so the track measures Sum + gap x (n-1) while a
   seamless loop must travel Sum/2 + gap x (n/2) - translateX(-50%) then
   lands half a gap short and the loop visibly jumps. A trailing margin
   makes the track Sum + gap x n, so half of it is exactly one repeat. */
.marquee {
  display: flex;
  gap: 0;
  width: max-content;
  white-space: nowrap;
  /* Hold the strip on its own compositor layer: without this the browser
     can re-rasterise a 4400px-wide run of text at the loop restart, which
     reads as a stutter even though the geometry is exact. */
  will-change: transform;
  backface-visibility: hidden;
}

/* The animation only starts once JS has duplicated the track, which it does
   after the web font is ready - so the loop never runs on fallback metrics
   that would shift under it. */
.marquee.is-running {
  animation: marquee var(--marquee-time, 48s) linear infinite;
}

.marquee > * { margin-right: 3rem; }

.marquee.rev { animation-direction: reverse; }

/* Durations are tuned to a target speed in px/s, not to a number: the
   track is duplicated to fill the viewport, so adding or cutting quotes
   changes how far the animation travels and would otherwise drift the
   speed. Mobile ~45px/s, desktop ~70px/s. */
/* Duration, not speed: the animation covers one repeat of the track in
   --marquee-time, so ADDING A QUOTE MAKES IT SCROLL FASTER unless this is
   scaled with it. Two quotes grew the track 31.9%, so 50s/32s became 66s/42s
   to hold the tuned ~45 px/s mobile and ~70 px/s desktop. */
.quotes .marquee { --marquee-time: 66s; }

@media (min-width: 52rem) {
  .quotes .marquee { --marquee-time: 42s; }
}

/* translate3d keeps the move on the compositor. -50% is exactly one repeat
   because every item carries a trailing margin (see .marquee above), and
   a percentage self-corrects if the web font changes item widths. */
@keyframes marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .marquee, .marquee.is-running { animation: none; }
}

.quotes .marquee span {
  font-style: italic;
  font-size: var(--t-s);
  color: var(--ink-2);
}

/* --- about ---------------------------------------------------------------- */

.about-grid {
  display: grid;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  align-items: start;
}

@media (min-width: 46rem) {
  /* a third + two-thirds of the content field */
  .about-grid { grid-template-columns: minmax(9rem, 13rem) 1fr; }
}

.portrait { margin: 0; }

@media (max-width: 46rem) {
  .portrait { max-width: 13rem; }   /* was full-bleed and too dominant */
}

.portrait img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 4px;
}

.prose p {
  max-width: var(--measure);
  margin: 0 0 0.9rem;
  color: var(--ink-2);
  font-size: var(--t-s);
  line-height: 1.55;
}

.prose p:last-child { margin-bottom: 0; }

.prose-sign {
  color: var(--ink) !important;
}

/* --- start a brief -------------------------------------------------------- */

.brief {
  display: grid;
  gap: clamp(1.1rem, 2.5vh, 1.6rem);
  max-width: 44rem;
}

.brief fieldset {
  border: 0;
  margin: 0;
  padding: 0;
}

.brief legend {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--violet);
  padding: 0;
  margin-bottom: 0.55rem;
}

.pills label { display: inline-block; margin: 0 0.5rem 0.5rem 0; }

.pills input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.pills span {
  display: inline-block;
  padding: 0.4rem 1rem;
  border: 1px solid var(--rule);
  border-radius: 999px;
  font-size: var(--t-s);
  color: var(--ink-2);
  cursor: pointer;
  transition: background-color 140ms ease, border-color 140ms ease, color 140ms ease;
  background: var(--paper);
}

.pills span:hover { border-color: var(--ink-3); color: var(--ink); }

.pills input:checked + span {
  background: var(--lilac);
  border-color: var(--lilac);
  color: var(--ink);
}

.pills input:focus-visible + span {
  outline: 2px solid var(--violet);
  outline-offset: 3px;
}

.field { display: grid; gap: 0.45rem; }

.field label {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--violet);
}

.field input,
.field textarea {
  font: inherit;
  font-size: var(--t-s);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 3px;
  padding: 0.65rem 0.8rem;
  width: 100%;
  transition: border-color 140ms ease, background-color 140ms ease;
}

.field textarea { resize: vertical; min-height: 6.5rem; }

.field input::placeholder,
.field textarea::placeholder { color: var(--ink-3); opacity: 1; }

.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--violet);
  background: #FDFCFA;
}

.field-pair {
  display: grid;
  gap: clamp(1.1rem, 2.5vh, 1.6rem);
}

@media (min-width: 40rem) {
  .field-pair { grid-template-columns: 1fr 1fr; }
}

.send {
  justify-self: start;
  font-family: var(--body);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--paper);
  background: var(--violet);
  border: 1px solid var(--violet);
  border-radius: 999px;
  padding: 0.75rem 2.2rem;
  cursor: pointer;
  transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease;
}

.send:hover { background: var(--ink); border-color: var(--ink); }

.send[disabled] { opacity: 0.55; cursor: progress; }

.form-status { margin: 0; color: var(--violet); min-height: 1.2em; font-size: var(--t-s); }
.form-status:empty { display: none; }

.brief-done {
  font-family: var(--display);
  font-weight: 620;
  font-size: var(--t-l);
  letter-spacing: -0.015em;
  margin: 0;
}

/* --- contact (merged into the brief panel) -------------------------------- */

.contact-block {
  margin-top: clamp(1.75rem, 4vh, 2.5rem);
  border-top: 1px solid rgba(110, 86, 180, 0.18);
  padding-top: clamp(1.25rem, 3vh, 1.75rem);
}

.contact-mail {
  font-family: var(--display);
  font-weight: 650;
  font-size: clamp(1.5rem, 4.6vw, 3rem);
  letter-spacing: -0.025em;
  line-height: 1.1;
  display: inline-block;
  margin: 0 0 1.2rem;
  text-decoration-color: var(--lilac);
  text-decoration-thickness: 2px;
  word-break: break-word;
}

.contact-mail:hover { text-decoration-color: var(--violet); }

/* Under the address, paired with the location line rather than floating
   beside the display type where a 30px mark reads as an orphan. Centred on
   the paragraph so the two sit as one row however many lines it wraps to. */
.contact-foot {
  display: flex;
  align-items: center;
  gap: clamp(0.9rem, 2.5vw, 1.4rem);
}

.contact-social {
  display: inline-flex;
  color: var(--ink-2);
  text-decoration: none;
  transition: color 160ms ease, transform 160ms ease;
}

.contact-social svg {
  width: clamp(1.4rem, 2.6vw, 1.85rem);
  height: auto;
  fill: currentColor;
  display: block;
}

.contact-social:hover,
.contact-social:focus-visible { color: var(--violet); }

.contact-where {
  margin: 0;
  color: var(--ink-2);
  font-size: var(--t-s);
  max-width: 44ch;
}

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

.colophon {
  border-top: 1px solid var(--rule);
  padding: 1.5rem 0 2.25rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  justify-content: space-between;
  font-size: var(--t-xs);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.colophon p { margin: 0; }
