/* ==========================================================================
   suzannedolanart.com : site.css
   Tokens, skeleton and shared components. Owned by the design-system agent.
   Page-specific rules go in public/assets/p-<page>.css, never in here.
   Recipes and the reasoning: DESIGN.md at the repo root.
   ========================================================================== */

/* ---------------------------------------------------------------- fonts -- */
/* Self-hosted, latin subset only. Files verified as WOFF2 (magic "wOF2").   */

/* Round 13 (FIX-ROUND-13.md, the artist's 2026-09-11 direction: "sharper, more contemporary, simpler", no italic
   serif): Inter Tight for display and titles, Inter for the body, both one variable upright file (100-900), no italic
   file on the site at all; IBM Plex Mono stays for labels and folios. Newsreader and IBM Plex Sans are gone. */
@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/inter-tight-var.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/inter-var.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/plex-mono-regular.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/fonts/plex-mono-medium.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------- tokens -- */
:root {
  /* colour: six roles, one accent (council decision 6) */
  --paper: #F4F4F1;        /* page ground, cool off-white (round 13, her request; was the warm bisque #F4EEE6) */
  --paper-deep: #ECECE8;   /* the enquiry plate, one step into the paper    */
  --paper-raise: #FAFAF8;  /* form fields, one step out of the paper        */
  --ink: #1A1A1A;          /* text, neutral near-black (round 13; the warm #1E1A17 read brown on the cool paper) */
  --ink-soft: #6B6B68;     /* secondary text, 4.9:1 on paper                */
  --paper-soft: #C4C4C0;   /* secondary text on a dark ground (unused since round 13 removed the world layer; kept as a token) */
  --rule: #DADAD6;         /* hairlines only, never a control boundary      */
  --line: #8A8A86;         /* field borders, 3.2:1                          */
  --accent: #2E4A7D;       /* links and the focus ring. Nothing else: no button is blue (fix round 2, section D) */
  --accent-deep: #25406F;  /* unused since fix round 2 (was the Enquire hover); kept as a token */
  --accent-line: #9AAAC7;  /* resting underline under links                 */
  --error: #8A2E24;        /* form errors, 7.3:1 on paper                   */
  --selection: #DCE2EC;

  /* type */
  --font-display: 'Inter Tight', 'Inter', 'Helvetica Neue', Arial, sans-serif;
  --font-body: 'Inter', 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'IBM Plex Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;

  /* modular scale: ratio 1.2 on a 17px base at 375px, ratio 1.25 on an 18px base at 1180px */
  --t-mono: clamp(0.8125rem, 0.7834rem + 0.1242vw, 0.875rem);   /* 13px -> 14px */
  --t-sm: 0.9375rem;                                             /* 15px */
  --t-0: clamp(1.0625rem, 1.0334rem + 0.1242vw, 1.125rem);      /* 17px -> 18px */
  --t-1: clamp(1.275rem, 1.2139rem + 0.2609vw, 1.4062rem);      /* 20.4px -> 22.5px */
  --t-2: clamp(1.53rem, 1.4239rem + 0.4528vw, 1.7578rem);       /* 24.5px -> 28.1px */
  --t-3: clamp(1.836rem, 1.6677rem + 0.718vw, 2.1973rem);       /* 29.4px -> 35.2px */
  --t-4: clamp(2.2032rem, 1.9501rem + 1.08vw, 2.7466rem);       /* 35.3px -> 43.9px */
  --t-5: clamp(2.6438rem, 2.2761rem + 1.569vw, 3.4332rem);      /* 42.3px -> 54.9px */
  --t-6: clamp(3.1726rem, 2.6514rem + 2.224vw, 4.2915rem);      /* 50.8px -> 68.7px, the display cap */

  --measure: 66ch;

  /* spacing: 8px grid */
  --s-1: 0.5rem;   /* 8  */
  --s-2: 1rem;     /* 16 */
  --s-3: 1.5rem;   /* 24 */
  --s-4: 2rem;     /* 32 */
  --s-5: 2.5rem;   /* 40 */
  --s-6: 3rem;     /* 48 */
  --s-8: 4rem;     /* 64 */
  --s-10: 5rem;    /* 80 */
  --s-12: 6rem;    /* 96 */
  --s-16: 8rem;    /* 128 */
  --section: clamp(4rem, 2.3696rem + 6.9565vw, 7.5rem);   /* 64px -> 120px between sections */
  --gutter: clamp(1.25rem, 0.6677rem + 2.4845vw, 2.5rem);  /* 20px -> 40px side padding */
  --wrap: 1180px;

  /* motion */
  --ease: cubic-bezier(0.23, 1, 0.32, 1);
  --reveal-ms: 500ms;
}

/* round 13 (2026-09-15, the owner: "on a big screen the text sits squeezed and does not fill the page"): past 1600px
   the root size and the container grow together, so every rem-based size (the type scale, the spacing, the gutter,
   the photograph boxes) scales with the screen instead of leaving a 1180px column in a sea of paper. Measured at
   1920 x 1080 and 2560 x 1440 before: an 18px body in a 1260px wrap on a 2560px screen. */
@media (min-width: 1600px) {
  :root { --wrap: 1400px; }
  html { font-size: 110%; }
}
@media (min-width: 2200px) {
  :root { --wrap: 1640px; }
  html { font-size: 120%; }
}

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

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

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-0);
  font-weight: 400;
  line-height: 1.55;
  font-kerning: normal;
  caret-color: var(--accent);
  isolation: isolate;
}

/* paper grain, 3 percent, under the content and never over a photograph */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.03;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='180' height='180' filter='url(%23g)'/></svg>");
  background-size: 180px 180px;
}

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

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

img, svg, video { display: block; max-width: 100%; height: auto; }
img { border-radius: 0; }
figure { margin: 0; }
p, h1, h2, h3, h4, ul, ol, dl, dd { margin: 0; }
button { font: inherit; color: inherit; }
hr, .rule { border: 0; border-top: 1px solid var(--rule); margin: 0; }

a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--accent-line);
  text-underline-offset: 0.16em;
}
a:hover { text-decoration-color: var(--accent); }

/* ----------------------------------------------------------- typography -- */
/* round 13: headings and titles in Inter Tight at weight 500, tighter with size; a title is marked by weight and
   position, never by italics (there is no italic file on the site any more) */
h1, h2, h3, h4, .display {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.08;
  color: var(--ink);
  text-wrap: balance;
}
h1 { font-size: var(--t-5); line-height: 1.04; letter-spacing: -0.022em; }
h2 { font-size: var(--t-3); letter-spacing: -0.016em; }
h3 { font-size: var(--t-1); line-height: 1.25; letter-spacing: -0.01em; }
h4 { font-size: var(--t-0); font-family: var(--font-body); font-weight: 500; }
.display { font-size: var(--t-6); line-height: 1.0; letter-spacing: -0.026em; }
.display--sm { font-size: var(--t-4); line-height: 1.06; letter-spacing: -0.02em; }
em, .italic { font-style: italic; }
cite { font-style: normal; font-weight: 500; }
.lead { font-size: var(--t-1); line-height: 1.45; text-wrap: pretty; max-width: 34ch; }
.lead--serif { font-family: var(--font-body); font-weight: 400; font-size: var(--t-2); line-height: 1.35; letter-spacing: -0.01em; }
.muted { color: var(--ink-soft); }
.mono { font-family: var(--font-mono); font-size: var(--t-mono); letter-spacing: 0.01em; }
.small { font-size: var(--t-sm); }

/* running head. One per page, not one per section. */
.folio {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: var(--s-3);
}
.folio + h1, .folio + h2, .folio + .display { margin-top: 0; }

/* the quiet TODO(proof) marker for facts we do not have yet */
.notice {
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-style: normal;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  text-decoration: underline dotted;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  white-space: nowrap;
}

/* prose: her words, one measure, more air above headings than below */
.prose { max-width: var(--measure); line-height: 1.6; text-wrap: pretty; }
.prose > * + * { margin-top: 1.1em; }
.prose > h2 { margin-top: 2em; }
.prose > h3 { margin-top: 1.8em; }
.prose > h2 + *, .prose > h3 + * { margin-top: 0.6em; }
.prose ul, .prose ol { padding-left: 1.2em; }
.prose li + li { margin-top: 0.35em; }
.prose blockquote {
  margin: 1.5em 0;
  padding-left: var(--s-3);
  border-left: 1px solid var(--rule);
  font-family: var(--font-body);
  font-size: var(--t-1);
  line-height: 1.4;
}
.prose blockquote p { margin: 0; }
.prose--sm { font-size: var(--t-sm); }
.flow > * + * { margin-top: var(--flow, 1em); }

/* --------------------------------------------------------------- layout -- */
.wrap {
  width: 100%;
  max-width: calc(var(--wrap) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: calc(51.25rem + 2 * var(--gutter)); }   /* 820px at the base size, grows with the root */
.section { padding-block: var(--section); }
.section--first { padding-top: clamp(1.5rem, 1rem + 2.5vw, 3.5rem); }
.section--tight { padding-block: var(--s-8); }
.section + .section { padding-top: 0; }
.section--ruled { border-top: 1px solid var(--rule); }
.section--ruled + .section--ruled, .section + .section--ruled { padding-top: var(--section); }

/* responsive object grid: 1 / 2 / 3 columns */
.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-6) var(--s-4);
}
@media (min-width: 620px) {
  .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1000px) {
  .grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-8) var(--s-5); }
  .grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* photo + text split: home opening object, work page (photo column sticky) */
.split { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-4); align-items: start; }
@media (min-width: 900px) {
  .split { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: var(--s-8); }
  .split--even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .split__sticky { position: sticky; top: var(--s-4); }
}

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

/* ------------------------------------------------------------- skeleton -- */
.skip {
  position: absolute;
  left: var(--gutter);
  top: -100px;
  z-index: 100;
  padding: 10px 14px;
  background: var(--accent);
  color: var(--paper);
  border-radius: 2px;
  font-size: var(--t-sm);
  font-weight: 500;
  text-decoration: none;
}
.skip:focus { top: 12px; }

.site-head {
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
}
.site-head__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  min-height: 72px;
}
/* round 13: the wordmark in Inter Tight 500 at 18px (the council's number); the nav in the label system's mono (13px, 0.06em) */
.wordmark {
  font-family: var(--font-display);
  font-size: 1.125rem;
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.012em;
  color: var(--ink);
  text-decoration: none;
  padding: 12px 0;
}
.site-nav { display: flex; gap: clamp(1.25rem, 3vw, 2.25rem); }
.site-nav__link {
  font-family: var(--font-mono);
  font-size: 0.8125rem;   /* 13px */
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0.06em;
  color: var(--ink);
  text-decoration: none;
  padding: 14px 0;
  text-underline-offset: 0.5em;
  text-decoration-thickness: 1.5px;
}
.site-nav__link:hover { text-decoration: underline; text-decoration-color: var(--ink-soft); }
.site-nav__link.is-active { text-decoration: underline; text-decoration-color: var(--ink); }

.menu-btn, .menu-close {
  display: none;
  align-items: center;
  min-height: 44px;
  padding: 0 8px;
  margin-right: -8px;
  border: 0;
  border-radius: 2px;
  background: transparent;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-weight: 400;
  letter-spacing: 0.06em;
  cursor: pointer;
}
.menu-sheet {
  position: fixed;
  inset: 0;
  z-index: 60;
  overflow-y: auto;
  background: var(--paper);
  opacity: 0;
  transition: opacity 160ms var(--ease);
}
.menu-sheet.is-open { opacity: 1; }
[hidden] { display: none !important; }
.menu-sheet[hidden] { display: none; }
.menu-sheet__inner {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  padding-top: 14px;
  padding-bottom: var(--s-6);
}
.menu-close { display: inline-flex; align-self: flex-end; }
.menu-sheet__nav { display: flex; flex-direction: column; margin-top: var(--s-6); }
.menu-sheet__link {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--t-5);
  line-height: 1.15;
  letter-spacing: -0.022em;
  color: var(--ink);
  text-decoration: none;
  padding: var(--s-1) 0;
}
.menu-sheet__link.is-active {
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 0.12em;
}
.menu-sheet__meta {
  margin-top: auto;
  padding-top: var(--s-8);
  font-size: var(--t-sm);
  line-height: 1.9;
}
html.menu-open { overflow: hidden; }

@media (max-width: 759.98px) {
  .site-nav { display: none; }
  .menu-btn { display: inline-flex; }
}

.site-main {
  flex: 1 0 auto; display: block; }

.site-foot {
  margin-top: var(--section);
  border-top: 1px solid var(--rule);
  padding: var(--s-6) 0 var(--s-8);
  font-size: var(--t-sm);
  line-height: 1.65;
}
.site-foot__grid { display: grid; gap: var(--s-4) var(--s-6); }
@media (min-width: 760px) {
  .site-foot__grid { grid-template-columns: 1.2fr 1fr 1fr; }
}
.site-foot__name {
  font-family: var(--font-display);
  font-size: 1.25rem;
  line-height: 1.2;
  margin-bottom: 4px;
}
.site-foot__line { margin: 0; }
.site-foot__tiny {
  margin-top: var(--s-2);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
}
.site-foot__tiny a { color: var(--ink-soft); }

/* ----------------------------------------------------------- components -- */
/* buttons (fix round 2, section D): paper ground, 1.5px ink border, ink text, mono label; hover, active and a
   pressed toggle fill with ink. Links keep the ink-blue; a button is never blue. The focus ring is the global one. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 12px 18px;
  border: 1.5px solid var(--ink);
  border-radius: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.8125rem;   /* 13px */
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: none;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: transform 120ms var(--ease);
}
.btn:hover, .btn:active, .btn[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.btn:active { transform: translateY(1px); }
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.5; cursor: default; transform: none; pointer-events: none; }
/* the quiet button: no border, a 1.5px underline under the label, transparent so it sits on the plate too */
.btn--quiet {
  border-color: transparent;
  background: transparent;
  text-decoration: underline;
  text-decoration-thickness: 1.5px;
  text-underline-offset: 0.3em;
  text-decoration-color: currentColor;
}
.btn--quiet:hover, .btn--quiet:active, .btn--quiet[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.btn--block { width: 100%; }
.actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }

/* the label schema: Title, Year / Materials / Dimensions / Status */
.label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink);
}
.label__title {
  font-family: var(--font-display);
  font-size: var(--t-1);
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.012em;
  margin: 0 0 4px;
  text-wrap: balance;
}
.label__title cite, i.label__title { font-style: normal; font-weight: 500; }
.label a { color: inherit; text-decoration: none; }
.label a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.12em; text-decoration-color: var(--ink-soft); }
.label__meta { margin: 0; }
.label__status { margin: 2px 0 0; color: var(--ink-soft); }
.label--lg .label__title { font-size: var(--t-4); line-height: 1.05; letter-spacing: -0.02em; margin-bottom: var(--s-2); }
.label--lg .label__status { margin-top: 4px; }

/* photographs: square corners, never cropped, credit line underneath */
.photo { display: block; width: 100%; height: auto; border-radius: 0; }
.photo--missing {
  aspect-ratio: 4 / 5;
  display: grid;
  place-items: center;
  border: 1px dashed var(--rule);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
}
.credit, figcaption {
  margin-top: var(--s-1);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
}
/* the quiet paper plate for a work with no photograph yet (Home opening object, work cards). One rule for every page;
   never a grey box, never a stock image, and since round 5 no production note (it reads "No photograph yet"). */
.paper-plate {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  padding: var(--s-3);
  background: var(--paper-deep);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  letter-spacing: 0.01em;
  text-align: center;
}

/* ------------------------------------------- colour grounds and objects -- */
/* Round 3 (FIX-ROUND-3.md calls these "fields"; the class is .ground because .field is the form field above).
   A GROUND is a full-bleed colour mixed from a work's glaze accent: --glaze arrives per work from the page's one
   inline <style> (.f-<slug>, src/lib/field.ts), a FLAT mix 20% into the paper on desktop and 22% on phones since
   round 5 (FIX-ROUND-5.md section C: no vignette, no halo, only the grain over it). Ink text stays ink; the ground's
   soft ink darkens to --ink-soft-field so the status line and captions keep 4.5:1 on every accent (the site's soft
   ink reaches only 4.2:1 to 4.6:1 at these mixes; the six ratios are in src/lib/field.ts). An OBJECT is the studio
   cutout standing on the ground with a contact shadow and a faint lift shadow, or, for a work without a cutout, the
   photograph itself with no frame and radius 0. */
:root {
  --ink-soft-field: #5A5A57;   /* soft ink on a colour ground: 5.4:1 to 5.9:1 on the six grounds at 20/22%, 6.3:1 on paper */
  --kiln: #1F1F1E;             /* a dark ground (unused since round 13) */
  --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='180' height='180' filter='url(%23g)'/></svg>");
}
.ground {
  --glaze: var(--paper);
  --mix-d: 20%;
  --mix-m: 22%;
  --mix: var(--mix-d);
  --ink-soft: var(--ink-soft-field);
  position: relative;
  background: color-mix(in oklab, var(--glaze) var(--mix), var(--paper));
  color: var(--ink);
}
@media (max-width: 759.98px) {
  .ground { --mix: var(--mix-m); }
}
/* the grain (4.5% on a ground since round 4, up from 3%, so it reads as paper and not as a flat panel) sits between the
   ground and the content; the round-3 radial vignette (.ground::after) went in round 5: a ground is flat */
.ground::before, .ground--deep::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.045;
  background-image: var(--grain);
  background-size: 180px 180px;
}
.ground > *, .ground--deep > * { position: relative; z-index: 1; }
/* the shallow ground behind a page title (About, Exhibitions, Contact): the opening work's accent at 14% */
.ground.ground--shallow { --mix: 14%; }
/* the kiln ground of the statement chapter: paper text, a hard cut at both edges, no gradient */
.ground--deep {
  position: relative;
  background: var(--kiln);
  color: var(--paper);
  --ink-soft: rgba(244, 244, 241, 0.72);
}
.ground--deep a { color: var(--paper); text-decoration-color: rgba(244, 244, 241, 0.55); }
.ground--deep a:hover { text-decoration-color: var(--paper); }
.ground--deep :focus-visible { outline-color: var(--paper); }

/* the object: its box takes the work's proportions from --ratio (the same inline <style>) and the img's own
   width/height, so nothing shifts while the file loads. --obj-h is the object's height where it stands; the box is
   sized by width (height * ratio, never wider than its column) so a squat pot on a narrow phone keeps its shape. */
.object { position: relative; display: block; width: 100%; line-height: 0; }
.object__body {
  position: relative;
  display: block;
  width: min(100%, calc(var(--obj-h, 46vh) * var(--ratio, 0.8)));
  aspect-ratio: var(--ratio, 0.8);
  margin-inline: auto;
  isolation: isolate;
}
.object__img, .object__body .photo { display: block; width: 100%; height: 100%; object-fit: contain; border-radius: 0; }
/* the cutout: a faint lift shadow that follows its silhouette, above the contact shadow */
.object--cut .object__img { position: relative; z-index: 1; filter: drop-shadow(0 24px 48px rgba(26, 26, 26, 0.10)); }
/* the contact shadow: an ellipse under the base, 70% of the object wide, ink at 18% fading to nothing, blurred 18px */
.object__shadow {
  position: absolute;
  left: 15%;
  bottom: 0;
  width: 70%;
  aspect-ratio: 5 / 1;
  transform: translateY(46%);
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(26, 26, 26, 0.18) 0%, rgba(26, 26, 26, 0.08) 42%, rgba(26, 26, 26, 0) 70%);
  filter: blur(18px);
}
.object--empty .object__body { aspect-ratio: 4 / 5; }
.object--empty .paper-plate { height: 100%; line-height: 1.5; }

/* work card (round 3): a field, the object standing on it, the label; one link; hover lift (see motion) */
.work-card { margin: 0; }
.work-card__link { display: block; height: 100%; padding: 8% 8% 7%; color: inherit; text-decoration: none; }
.work-card__link:focus-visible { outline-offset: -4px; }
.work-card__stage {
  --obj-h: 100%;
  position: relative;
  aspect-ratio: 4 / 5;
  container-type: inline-size;
}
.work-card__stage .object {
  position: absolute;
  inset: 5% 0 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
/* never wider than the stage: a squat pot (Hold Still) stands lower, at the same base line */
.work-card__stage .object__body { width: auto; height: min(100%, calc(100cqw / var(--ratio, 0.8))); }
.work-card__stage .object--photo .object__body { height: 100%; }
/* round 4: a hairline shelf line under every object's base, across the whole card (the link pads 8% of the card, so
   the stage is 84% of it: 9.5% of the stage each side reaches the card's edges); a field with an object standing on
   a shelf, not a bordered box. Cutouts stand on it; a photograph sits with its bottom on it. */
.work-card__stage::after {
  content: '';
  position: absolute;
  left: -9.5%;
  right: -9.5%;
  bottom: 0;
  z-index: 0;
  border-top: 1px solid var(--ink);
  opacity: 0.35;
  pointer-events: none;
}
.work-card__stage .object { z-index: 1; }
/* round 5: the label attached to the image, 12px under the shelf line, not a padded band */
.work-card .label { margin-top: 12px; }
.work-card__link:hover .label__title cite {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.12em;
  text-decoration-color: var(--ink-soft);
}

/* the hero field (Home: the opening work at 100 svh under a transparent header; the work page: at least 78 svh).
   The folio is the page's h1 on Home; the object stands right of centre; the italic title at poster scale sits
   lower-left and may overlap the object's lower edge by a tenth of the object's width, never the painted scene. */
.hero {
  --obj-h: 74vh;
  --hero-top: 96px;
  --hero-bottom: 12vh;     /* where the object's base stands */
  --hero-right: 12vw;      /* the object's margin from the right edge before site.js measures the title */
  --hero-under: 3.4rem;    /* what sits under the title inside the text block (the label; on the work page the button too) */
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
}
.hero__inner { position: relative; min-height: 100vh; min-height: 100svh; }
.hero__folio {
  position: absolute;
  top: var(--hero-top);
  left: var(--gutter);
  z-index: 3;
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.02em;
  color: var(--ink);
}
.hero__link { position: absolute; inset: 0; display: block; color: inherit; text-decoration: none; }
.hero__link:focus-visible { outline-offset: -6px; }
.hero__obj {
  position: absolute;
  right: var(--hero-right);
  bottom: var(--hero-bottom);
  z-index: 1;
  width: calc(var(--obj-h) * var(--ratio, 0.8));
  height: var(--obj-h);
  perspective: 1200px;
}
.hero__obj .object__body { width: 100%; }
/* the title's last line ends about 3vh below the object's base, so what it overlaps is the foot and its shadow,
   never the painted body; the label (and the button on the work page) hangs under it */
.hero__text {
  position: absolute;
  left: var(--gutter);
  bottom: max(2vh, calc(var(--hero-bottom) - 3vh - var(--hero-under)));
  z-index: 2;
  width: calc(100vw - var(--hero-right) - var(--obj-h) * var(--ratio, 0.8) * 0.9 - var(--gutter));
}
@media (min-width: 900px) {
  /* once site.js has measured the title's ink (--title-right), the cutout stands so the title reaches 8% of the
     way across it (the brief's 8 to 12%; the low end, because the first line meets the pot's belly, not its foot),
     never left of the centre line and never past its right margin; a photograph, which has no transparent margin
     to lend, stands 4vw clear of the title instead */
  .hero.is-measured .hero__obj {
    right: auto;
    left: min(max(50vw, calc(var(--title-right, 60vw) - var(--obj-h) * var(--ratio, 0.8) * 0.08)), calc(100vw - var(--hero-right) - var(--obj-h) * var(--ratio, 0.8)));
  }
  .hero--photo.is-measured .hero__obj {
    left: min(max(50vw, calc(var(--title-right, 60vw) + 4vw)), calc(100vw - var(--hero-right) - var(--obj-h) * var(--ratio, 0.8)));
  }
}
.hero__title {
  margin: 0 0 0 -0.03em;
  padding: 0.05em 0.14em 0.14em 0.03em;   /* room for the italic overhang and the descenders inside the clip */
  overflow: hidden;
  font-family: var(--font-display);
  font-size: clamp(3.2rem, 8.5vw, 8.5rem);
  font-weight: 400;
  font-style: normal;
  line-height: 0.98;
  letter-spacing: -0.02em;
  text-wrap: balance;
  color: var(--ink);
}
.hero__title cite { font-style: normal; }
.hero__title-in { display: block; }
.hero__label { margin-top: var(--s-2); }
.hero__label .label__status { margin-top: 2px; }
.hero--empty .hero__obj { --ratio: 0.8; }
.hero--empty .hero__link { display: none; }
@media (max-width: 899.98px) {
  /* phones (round 5, FIX-ROUND-5.md section A): the object gets a protected full-width STRIP, the box (52svh tall,
     centred in it) plus its contact shadow, and the type block (title, status, button) sits strictly below the strip,
     never over it, whatever the scroll position; the top padding is a third smaller than round 4's, so the empty
     space above the pot shrinks by about a third. The scroll-turn (the canvas) stays bound to the strip's box. */
  .hero { --obj-h: 52vh; --obj-h: 52svh; --hero-top: 62px; }
  .hero__inner { display: flex; flex-direction: column; padding: var(--hero-top) var(--gutter) 5vh; }
  .hero__folio { position: static; margin-bottom: var(--s-3); }
  .hero__link { position: relative; inset: auto; display: flex; flex-direction: column; flex: 1 0 auto; }
  .hero__obj {
    position: relative;
    right: auto;
    bottom: auto;
    align-self: stretch;
    width: 100%;
    height: auto;
    margin-top: auto;
    /* the strip ends under the contact shadow: the ellipse (70% of the object wide, 5:1, pushed 46% of its own height
       below the base, blurred 18px) reaches about 8% of the object's width plus the blur under the box */
    padding-bottom: calc(var(--obj-h) * var(--ratio, 0.8) * 0.08 + 14px);
  }
  .hero__obj .object__body { width: min(100%, calc(var(--obj-h) * var(--ratio, 0.8))); }
  /* the stage's spare height is shared: half above the strip, half under the type block (both margins auto), so the
     pot stands about a third higher than in round 4 (measured at 505 x 900: 277px down, now about 175px) */
  .hero__text { position: relative; left: auto; bottom: auto; width: auto; margin-top: var(--s-3); margin-bottom: auto; }
}
/* ------------------------------------------------------ the turning vessel -- */
/* Round 4 (FIX-ROUND-4.md): a work with a turntable frame sequence opens on the vessel turning under the scroll. The
   section (.hero--turn, a ground like the round-3 hero) is --turn-h tall (Home 260vh, 220svh on phones; the work page
   180vh since round 5); the stage inside it (.hero__inner) sticks at the top for 100svh; the box (.object__body, --obj-h tall,
   aspect-ratio from --turn-ratio, the frame's proportions) holds the canvas, the poster (the studio cutout, shown until
   frame 0 is drawn) and the round-3 contact shadow; the type block sits lower-left as before. public/assets/turn.js
   maps the scroll progress through the section to a frame and draws it (lerped at 0.14 per animation frame). Without
   JS the html.js class is absent and nothing below applies: the section is the round-3 static hero with the poster.
   Round 6 (FIX-ROUND-6.md section A): under prefers-reduced-motion (html.no-motion) the turn still runs, because the
   visitor's own scrolling drives it (turn.js then maps the scroll to the frame directly, no easing); only ?static=1
   (html.static) keeps it still. turn.js adds .is-static when it cannot run, .is-live once the first frame is on the
   canvas (the poster then hides in the same frame, so it never flashes). */
.hero.hero--turn { --ratio: var(--turn-ratio, 0.46); }   /* the box takes the frame's proportions, not the cutout's */
.hero--turn { --turn-h: 260vh; }
html.js .hero--turn:not(.is-static) { min-height: 0; height: var(--turn-h); overflow: visible; }
html.js .hero--turn:not(.is-static) .hero__inner {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  min-height: 0;
  overflow: hidden;
}
@media (max-width: 899.98px) {
  /* phones stack the folio, the object and the type block in flow: the stage grows past 100svh on a short screen
     rather than clipping the status line (it still sticks at the top) */
  html.js .hero--turn:not(.is-static) .hero__inner { height: auto; min-height: 100vh; min-height: 100svh; }
}
/* no vignette on the stage either since round 5 (section C): the field behind the turning vessel is flat */
/* the box: the poster fills it (contained, so the cutout stands as tall as the box and centred); the canvas covers it
   with 2% to spare on each side, because the vessel wanders a little sideways as it turns; turn.js crops every frame so
   frame 0's silhouette stands exactly where the poster stood (measured from its alpha), and the swap moves nothing */
.object--turn .object__body { overflow: visible; }
.object--turn .turn__poster { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; z-index: 1; }
.turn__canvas {
  position: absolute;
  top: 0;
  left: -2%;
  width: 104%;
  height: 100%;
  z-index: 2;
  display: block;
  pointer-events: none;
  filter: drop-shadow(0 24px 48px rgba(26, 26, 26, 0.10));   /* the same lift shadow as the cutout */
}
html:not(.js) .turn__canvas, .hero--turn.is-static .turn__canvas { display: none; }
.hero--turn.is-live .turn__poster { visibility: hidden; }
@media (max-width: 899.98px) {
  .hero--turn { --turn-h: 220vh; --turn-h: 220svh; }
}

/* the enquiry plate: the one bordered panel on the site */
.plate {
  border: 1px solid var(--rule);
  background: var(--paper-deep);
  padding: clamp(1.5rem, 1rem + 2.5vw, 3rem);
}
.plate__title { font-size: var(--t-3); line-height: 1.1; letter-spacing: -0.008em; }
.plate__text { margin-top: var(--s-2); max-width: 48ch; text-wrap: pretty; }
.plate .actions { margin-top: var(--s-4); }
@media (max-width: 479.98px) { .plate .btn { width: 100%; } }

/* form fields: 44px tap height, 16px+ type so iOS does not zoom, text under each field */
.field { display: grid; gap: 6px; }
.field + .field { margin-top: var(--s-3); }
.field__label { font-size: var(--t-sm); font-weight: 500; line-height: 1.4; color: var(--ink); }
.field__label small, .field__optional { font-weight: 400; color: var(--ink-soft); }
.field input:not([type="checkbox"]):not([type="radio"]):not([type="file"]),
.field textarea,
.field select {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 2px;
  background: var(--paper-raise);
  color: var(--ink);
  font: inherit;
  font-size: max(16px, var(--t-0));
  line-height: 1.4;
  -webkit-appearance: none;
  appearance: none;
}
.field select { padding-right: 36px; }
.field textarea { min-height: 160px; resize: vertical; }
.field input[type="file"] { font-size: max(16px, var(--t-sm)); padding: 8px 0; }
.field__help { font-size: 0.875rem; line-height: 1.5; color: var(--ink-soft); }
.field__error { display: none; font-size: 0.875rem; line-height: 1.5; color: var(--error); font-weight: 500; }
.field.is-error input, .field.is-error textarea, .field.is-error select {
  border-color: var(--error);
  box-shadow: inset 0 0 0 1px var(--error);
}
.field.is-error .field__error { display: block; }
.field.is-error .field__label { color: var(--error); }

.choice { display: flex; align-items: flex-start; gap: 10px; min-height: 44px; padding: 10px 0; }
.choice input[type="checkbox"], .choice input[type="radio"] {
  width: 20px; height: 20px;
  margin: 1px 0 0;
  flex: none;
  accent-color: var(--accent);
}
.choice__text { font-size: var(--t-0); line-height: 1.4; }
.choice__help { display: block; font-size: 0.875rem; color: var(--ink-soft); }
.choices { display: grid; gap: 0; }
.form-status { font-size: var(--t-sm); color: var(--ink-soft); }
.form-status.is-error { color: var(--error); }

/* lists without bullets, ruled rows (CV, admin lists) */
.list-plain { list-style: none; margin: 0; padding: 0; }
.rows { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.rows > li { padding: var(--s-2) 0; border-bottom: 1px solid var(--rule); }

/* aliases so the page stubs (src/views/pages/_shared.ts and friends) sit on the system until they adopt
   ui.ts components: ul.grid, .figure, .figure__placeholder, .label__credit, .btn--danger, .radio, .checkbox,
   .hp (honeypot), .form-error */
ul.grid, ol.grid { list-style: none; margin: 0; padding: 0; }
.grid__item { margin: 0; }
.figure { margin: 0; }
.figure img { display: block; width: 100%; height: auto; }
.figure a { display: block; }
.figure figcaption { margin-top: var(--s-2); color: var(--ink); }
.figure__placeholder {
  aspect-ratio: 4 / 5;
  display: grid;
  place-items: center;
  border: 1px dashed var(--rule);
  color: var(--ink-soft);
  font-family: var(--font-mono);
  font-size: var(--t-mono);
  text-align: center;
  padding: var(--s-3);
}
.label__credit { margin-top: var(--s-1); color: var(--ink-soft); }
.btn--danger { background: transparent; border-color: var(--error); color: var(--error); }
.btn--danger:hover { background: transparent; border-color: var(--error); color: var(--error); text-decoration: underline; }
.radio, .checkbox { display: flex; align-items: flex-start; gap: 10px; min-height: 44px; padding: 10px 0; }
.radio input, .checkbox input { width: 20px; height: 20px; margin: 1px 0 0; flex: none; accent-color: var(--accent); }
.hp { position: absolute !important; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }
.form-error { font-size: var(--t-sm); font-weight: 500; color: var(--error); }

/* --------------------------------------------------------------- motion -- */
/* Entry reveal: visible without JS. The head bootstrap adds html.js before first paint, and html.no-motion beside it
   under prefers-reduced-motion, ?shots=1 or ?static=1 (round 6: html.js always marks JavaScript, html.no-motion only
   switches the autonomous motion off, so a page with both is complete and still from the first paint); site.js adds
   .is-in when the element enters the viewport. Only opacity and transform ever transition. */
[data-reveal]:not([data-reveal="hero"]) {
  transition: opacity var(--reveal-ms) var(--ease), transform var(--reveal-ms) var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}
.js:not(.no-motion) [data-reveal]:not([data-reveal="hero"]):not(.is-in) { opacity: 0; transform: translateY(12px); }

/* The one page-load reveal, on the hero field (round 3): the object rises 28px and fades in over 900ms; the title
   clip-reveals from its baseline from 150ms; the label, button and credit follow; the folio comes last. site.js adds
   .is-in to [data-reveal="hero"] on the second frame. The parallax and the pointer tilt write their transform on
   .hero__obj (the outer box), so they never fight this transition on the .object inside it. */
.hero__obj .object { transition: opacity 900ms cubic-bezier(0.2, 0.7, 0.2, 1), transform 900ms cubic-bezier(0.2, 0.7, 0.2, 1); }
.hero__title-in { transition: transform 800ms cubic-bezier(0.2, 0.7, 0.2, 1) 150ms; }
.hero__label, .hero__actions, .hero__credit { transition: opacity 600ms var(--ease) 380ms, transform 600ms var(--ease) 380ms; }
.hero__folio { transition: opacity 500ms var(--ease) 620ms; }
/* the object starts at 0.01, not 0: Chrome never counts an image first painted at opacity 0 as a Largest Contentful
   Paint candidate, and the compositor-driven fade does not repaint it, so on a fast connection the LCP became the
   folio (measured 2026-09-02, checker); at 0.01 the cutout is the LCP and the fade looks the same */
.js:not(.no-motion) [data-reveal="hero"]:not(.is-in) .hero__obj .object { opacity: 0.01; transform: translateY(28px); }
.js:not(.no-motion) [data-reveal="hero"]:not(.is-in) .hero__title-in { transform: translateY(112%); }
.js:not(.no-motion) [data-reveal="hero"]:not(.is-in) .hero__label,
.js:not(.no-motion) [data-reveal="hero"]:not(.is-in) .hero__actions,
.js:not(.no-motion) [data-reveal="hero"]:not(.is-in) .hero__credit { opacity: 0; transform: translateY(12px); }
.js:not(.no-motion) [data-reveal="hero"]:not(.is-in) .hero__folio { opacity: 0; }

/* hover lift on cards: -6px and a larger shadow over 350ms, once the entry reveal is done (.is-in). This rule comes
   after the [data-reveal] transition above on purpose: same specificity, later wins. */
.work-card.is-in { transition: transform 350ms var(--ease), box-shadow 350ms var(--ease); transition-delay: 0ms; }
.work-card:hover, .work-card:focus-within { transform: translateY(-6px); box-shadow: 0 18px 40px rgba(26, 26, 26, 0.14); }

@media (prefers-reduced-motion: reduce) {
  [data-reveal], .hero__obj .object, .hero__title-in, .hero__label, .hero__actions, .hero__credit, .hero__folio, .menu-sheet, .btn, .work-card, .site-head { transition: none; }
  .js [data-reveal]:not(.is-in),
  .js [data-reveal="hero"]:not(.is-in) .hero__obj .object,
  .js [data-reveal="hero"]:not(.is-in) .hero__title-in,
  .js [data-reveal="hero"]:not(.is-in) .hero__label,
  .js [data-reveal="hero"]:not(.is-in) .hero__actions,
  .js [data-reveal="hero"]:not(.is-in) .hero__credit,
  .js [data-reveal="hero"]:not(.is-in) .hero__folio { opacity: 1; transform: none; }
  .work-card:hover, .work-card:focus-within { transform: none; }
}
/* the same stillness under ?shots=1 and ?static=1 (html.no-motion without the media query) */
html.no-motion [data-reveal], html.no-motion .hero__obj .object, html.no-motion .hero__title-in, html.no-motion .hero__label, html.no-motion .hero__actions, html.no-motion .hero__credit, html.no-motion .hero__folio, html.no-motion .work-card, html.no-motion .site-head { transition: none; }

/* ---------------------------------------------------------------- print -- */
.print-only { display: none; }
@media print {
  body::before { display: none; }
  .site-head, .site-foot, .skip, .menu-sheet, .menu-btn, .btn, .no-print { display: none !important; }
  .print-only { display: block; }
  html, body { background: #fff; color: #000; }
  .ground, .ground--deep { background: none; color: #000; --ink-soft: #000; }
  .ground::before, .ground--deep::before, .object__shadow { display: none; }
  .object--cut .object__img { filter: none; }
  body { font-size: 11pt; line-height: 1.45; }
  a { color: #000; text-decoration: none; }
  .wrap { max-width: none; padding: 0; }
  .section { padding-block: 1.5rem; }
  [data-reveal], [data-hero-photo], [data-hero-label] { opacity: 1 !important; transform: none !important; transition: none !important; }
  .split { grid-template-columns: 1fr; }
  .split__sticky { position: static; }
  h1, h2, h3 { break-after: avoid; }
  .rows > li, .work-card, figure { break-inside: avoid; }
}
