/* ==========================================================================
   Ariana Olaya, artist portfolio
   Design system: warm paper gallery. Palette sampled from the paintings.
   ========================================================================== */

/* --- Soria, display face ---------------------------------------------------
   Soria by Bydani. The Font Squirrel package ships SIL Open Font License 1.1,
   included beside the file at assets/fonts/. Served as the original unmodified
   TTF, since the font's own metadata also cites CC BY-ND 4.0 and that reading
   would not permit a converted or subset copy.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Soria";
  src: url("../assets/fonts/soria.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* --- Tokens ------------------------------------------------------------- */
:root {
  /* Surface */
  --paper:        #FAF6EF;
  --paper-2:      #F4EEE4;
  --paper-3:      #EDE4D6;
  --card:         #FFFCF7;

  /* Ink */
  --ink:          #1C1815;
  --ink-soft:     #3A332C;
  --muted:        #6A6058;   /* 5.69:1 on paper */

  /* Accents, sampled from Wallpariy and Iskay Apu.
     Three roles: terracotta is identity, blue is interaction, deep red is structure.
     Ochre, gold and green are surfaces and never set text on the linen ground. */
  --terracotta:      #A8431C; /* identity, 5.59:1 on paper */
  --terracotta-deep: #8F3A18;
  --blue:            #3A5F93; /* interaction, 6.02:1 on paper */
  --blue-deep:       #2F4C78;
  --red-deep:        #66190B; /* structure, 11.41:1 on paper */
  --ochre:           #D0851A; /* surface only, 2.76:1 on paper */
  --gold:            #C8862A;
  --gold-light:      #E1AE1E;
  --green:           #466C4E;
  --umber:           #3D150F;
  --indigo:          #2F4470;

  /* Lines */
  --border:        #E4DACA;
  --border-strong: #D6C9B4;

  /* Type */
  --font-display: "Soria", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --font-body: "Gambetta", Georgia, "Times New Roman", serif;

  --step--1: clamp(0.78rem, 0.76rem + 0.10vw, 0.84rem);
  --step-0:  clamp(1rem, 0.97rem + 0.15vw, 1.075rem);
  --step-1:  clamp(1.15rem, 1.09rem + 0.30vw, 1.35rem);
  --step-2:  clamp(1.45rem, 1.32rem + 0.65vw, 1.9rem);
  --step-3:  clamp(1.9rem, 1.62rem + 1.4vw, 2.9rem);
  --step-4:  clamp(2.4rem, 1.85rem + 2.7vw, 4.4rem);
  --step-5:  clamp(3.4rem, 2.1rem + 6.6vw, 9.5rem);

  /* Space, spacious scale */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;
  --sp-9: 96px; --sp-10: 128px; --sp-11: 176px;

  --shell: 1280px;
  --gutter: clamp(20px, 5vw, 72px);

  /* Motion. --ease is a strong ease-out, used for anything entering or responding.
     --ease-in-out is for things travelling across the screen, --ease-drawer for panels. */
  --ease: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
  --dur: 220ms;
  --dur-press: 160ms;

  --shadow-sm: 0 1px 2px rgba(61, 21, 15, 0.05), 0 6px 18px rgba(61, 21, 15, 0.05);
  --shadow-md: 0 2px 4px rgba(61, 21, 15, 0.06), 0 18px 44px rgba(61, 21, 15, 0.10);
  --shadow-lg: 0 4px 8px rgba(61, 21, 15, 0.07), 0 36px 80px rgba(61, 21, 15, 0.16);
}

/* --- Reset -------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  font-synthesis: none;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
img { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4 { margin: 0; font-weight: 400; }
p { margin: 0; }
a { color: inherit; }
button { font: inherit; color: inherit; }

/* Paper grain, applied once over the whole page */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.35;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)' opacity='0.28'/%3E%3C/svg%3E");
}

/* --- Accessibility helpers ---------------------------------------------- */
.skip-link {
  position: absolute; left: var(--sp-4); top: -100px;
  z-index: 200; padding: 12px 20px;
  background: var(--ink); color: var(--paper);
  text-decoration: none; border-radius: 2px;
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--sp-4); }

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

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

/* --- Layout primitives -------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.shell--wide { max-width: 1560px; }

/* Section rhythm: about 100px at 1440, 64px on phones. Was 11vw (158px each side),
   which put over 300px of empty paper between neighbouring sections. */
.section { padding-block: clamp(var(--sp-8), 7vw, var(--sp-10)); position: relative; z-index: 2; }
.section--tight { padding-block: clamp(var(--sp-7), 5vw, var(--sp-9)); }
/* A section title with its link on the same line, so the link reads as part of
   the section instead of floating alone under it */
.section-head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3) var(--sp-6);
}
.section--paper-2 { background: var(--paper-2); }
/* Deep red paint, the same as the footer, so the closing band and the copyright
   strip read as one block. The paint is dark enough that cream type sits at
   11:1 or better without a scrim. */
.section--red {
  background-color: var(--red-deep);
  background-size: cover;
  background-position: center;
  position: relative;
}
.section--red > * { position: relative; z-index: 1; }
.section--red .eyebrow--accent { color: rgba(250, 246, 239, 0.8); }
.section--red .section-title { color: var(--paper); }
.section--red .lede { color: rgba(250, 246, 239, 0.9); }
.section--red .is-placeholder { color: rgba(250, 246, 239, 0.78); }
/* The band is already paint, so the button on it stays solid: cream, the one
   light shape in the band. */
.section--red .btn--primary {
  background-image: none;
  background-color: var(--paper);
  color: var(--ink);
}
.section--red .btn--primary:hover { background-color: var(--terracotta); color: var(--paper); }
/* Homepage: the inquiries band and the copyright line share one painted block */
.site-footer.site-footer--inquiries { padding-block: clamp(var(--sp-7), 5vw, var(--sp-9)) var(--sp-6); }
.site-footer--inquiries .site-footer__bottom {
  margin-top: clamp(var(--sp-7), 5vw, var(--sp-9));
  padding-top: var(--sp-6);
  border-top: 1px solid rgba(233, 223, 208, 0.14);
}

.eyebrow {
  font-size: var(--step--1);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
}
.eyebrow--accent { color: var(--terracotta-deep); }
@media (max-width: 480px) { .eyebrow { letter-spacing: 0.13em; } }

.rule {
  width: 56px; height: 1px;
  background: var(--terracotta);
  border: 0; margin: var(--sp-5) 0 0;
}

.display {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: 0.005em;
}
.section-title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--red-deep);
  font-size: var(--step-3);
  line-height: 1.1;
  letter-spacing: 0.01em;
}
.lede {
  font-size: var(--step-1);
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: 48ch;
}
.prose p + p { margin-top: var(--sp-5); }
.prose { color: var(--ink-soft); max-width: 52ch; line-height: 1.72; }
/* The bio reads at the same size as the statement below it */
.prose--bio { font-size: var(--step-1); line-height: 1.7; max-width: 50ch; text-wrap: pretty; }

/* Artist statement: a band of Andean blue from Iskay Apu's sky, white type, and
   her words set between large quotation marks. A 30% deep-blue wash keeps white
   body text at 4.5:1 or better even over the palest strokes (measured at the
   99th percentile of the texture), while the paint keeps its colour. */
.section--statement {
  background-color: var(--blue);
  background-size: cover;
  background-position: center;
  color: #FFFFFF;
}
.section--statement::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(18, 30, 58, 0.30);
  pointer-events: none;
}
.section--statement > .shell { position: relative; }
.section--statement .section-title,
.section--statement .prose,
.section--statement .lede { color: #FFFFFF; }
.section--statement .prose,
.section--statement .lede { text-shadow: 0 1px 2px rgba(18, 30, 58, 0.35); }
.section--statement .is-placeholder { color: rgba(255, 255, 255, 0.86); }
.section--statement .link-arrow { color: #FFFFFF; }
.section--statement .link-arrow:hover { text-decoration: underline; text-underline-offset: 6px; }

.statement-quote { position: relative; }
/* A matching pair of marks set inline with the words, as in a well-set book: the
   opening mark before the first word, the closing mark after the last. They are
   Soria's own curly quotes drawn as outlines (assets/quote-*.svg), since the font
   sets them at a sixth of the type size. Sized and raised to sit at cap height. */
p.statement-quote::before,
.statement-lead::before,
.statement-quote > p:last-child::after,
p.statement-quote::after {
  content: "";
  display: inline-block;
  width: 0.62em;
  aspect-ratio: 201 / 160;
  vertical-align: 0.26em;
  background: rgba(255, 255, 255, 0.94);
}
p.statement-quote::before,
.statement-lead::before {
  margin-right: 0.22em;
  -webkit-mask: url("../assets/quote-open.svg") center / contain no-repeat;
          mask: url("../assets/quote-open.svg") center / contain no-repeat;
}
.statement-quote > p:last-child::after,
p.statement-quote::after {
  margin-left: 0.18em;
  -webkit-mask: url("../assets/quote-close.svg") center / contain no-repeat;
          mask: url("../assets/quote-close.svg") center / contain no-repeat;
}

/* Homepage: her sentence as a centred pull quote, the centrepiece of the band */
.statement--feature .shell { text-align: center; }
.statement--feature .statement-quote {
  max-width: 30ch;
  margin-inline: auto;
  font-size: clamp(1.5rem, 0.95rem + 2.1vw, 2.85rem);
  line-height: 1.3;
  letter-spacing: -0.005em;
  text-wrap: balance;
}
.statement--feature .link-arrow { justify-content: center; }

/* About: the full statement as a centred reading column, opening on a larger
   first sentence the way a chapter does */
.statement--reading .section-title { text-align: center; }
.statement--reading .statement-quote {
  max-width: 60ch;
  margin-inline: auto;
  font-size: var(--step-1);
  text-wrap: pretty;
}
.statement--reading .statement-lead { font-size: 1.4em; text-wrap: balance; }
.statement-lead {
  display: block;
  font-size: var(--step-2);
  line-height: 1.4;
  margin-bottom: var(--sp-5);
}

/* --- Paint fill ----------------------------------------------------------
   Type cut out of the painting, the same idea as the curtain but done with
   background-clip so it works on ordinary text anywhere on the site. The
   texture is a strip of Wallpariy's ember field, chosen because it measures
   8.6:1 against linen on average and still 5.6:1 at its lightest paint, so
   filled type stays legible rather than only looking good.
   Guarded by @supports: without background-clip the text keeps its own colour
   instead of disappearing.
   ------------------------------------------------------------------------ */
.paint { background-image: url("../assets/paint-ember-1600.jpg"); }

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .paint {
    background-image: image-set(url("../assets/paint-ember-1600.webp") type("image/webp"),
                                url("../assets/paint-ember-1600.jpg") type("image/jpeg"));
    background-size: cover;
    background-position: center 58%;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

/* --- Buttons ------------------------------------------------------------ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  min-height: 48px;
  padding: 0 var(--sp-6);
  border-radius: 2px;
  border: 1px solid transparent;
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.btn svg { width: 16px; height: 16px; transition: transform var(--dur) var(--ease); }
.btn:hover svg { transform: translateX(4px); }

.btn--primary {
  color: var(--paper);
  border-color: transparent;
  background-color: var(--blue);   /* the fallback if the texture cannot load */
  /* The deep base of Wallpariy's field: no highlights anywhere in it, so a cream
     label clears 6.7:1 at the worst pixel rather than only on average. */
  background-size: cover;
  background-position: center 50%;
  transition: background-position 450ms var(--ease), color var(--dur) var(--ease),
              transform var(--dur-press) var(--ease);
}
.btn--primary:hover { background-position: center 25%; }

.actions {
  display: flex; flex-wrap: wrap; gap: var(--sp-4);
  margin-top: var(--sp-7);
}

.btn--ghost { border-color: var(--border-strong); color: var(--ink); background: transparent; }

/* The other way round: paint fills the button, the label stays cream. Reads as
   painted at any size, where filled type needs scale to read as anything. */
.btn--paintbg {
  border-color: transparent;
  color: var(--paper);
  background-size: cover;
  background-position: center 50%;
}
.btn--paintbg:hover { background-position: center 15%; }

/* Painted label on a quiet frame, so the texture reads rather than the fill */
.btn--paint {
  border-color: var(--border-strong);
  background: var(--card);
}
.btn--paint:hover { border-color: var(--terracotta); background: var(--paper-2); }
.btn--paint svg { color: var(--terracotta-deep); }
.btn--ghost:hover { border-color: var(--blue); background: rgba(58, 95, 147, 0.06); }

.link-arrow {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  font-size: var(--step--1); letter-spacing: 0.12em; text-transform: uppercase;
  font-weight: 500; color: var(--blue); text-decoration: none;
  min-height: 44px;
}
.link-arrow svg { width: 16px; height: 16px; transition: transform var(--dur) var(--ease); }
.link-arrow:hover svg { transform: translateX(5px); }
.link-arrow::after {
  content: ""; position: absolute;
}

/* --- Site header -------------------------------------------------------- */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              border-color var(--dur) var(--ease);
  border-bottom: 1px solid transparent;
}
/* Frosted glass, on its own layer under the header. It cannot live inside the
   header: that carries a view-transition-name for page transitions, and a named
   element blocks the backdrop blur of everything inside it, which is why the bar
   read flat on every device. The tint fades in; animating the opacity of the
   blurred layer itself drops the blur on iOS Safari. */
.header-glass {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 77px;
  z-index: 99;
  pointer-events: none;
  background-color: rgba(250, 246, 239, 0);
  transition: background-color var(--dur) var(--ease);
}
body.scrolled .header-glass {
  background-color: rgba(250, 246, 239, 0.62);
  -webkit-backdrop-filter: blur(17px) saturate(1.8);
          backdrop-filter: blur(17px) saturate(1.8);
}
/* The homepage holds its bar back until the first scroll, glass included */
body.home:not(.scrolled) .header-glass { opacity: 0; }

.site-header.is-stuck {
  border-bottom-color: rgba(28, 24, 21, 0.08);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset, 0 8px 24px -18px rgba(28, 24, 21, 0.35);
}
/* Without backdrop-filter a see-through bar would ghost, so stay solid */
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  body.scrolled .header-glass { background-color: var(--paper); }
}
.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  min-height: 76px;
}
.wordmark {
  font-family: var(--font-display);
  font-size: 1.15rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
/* The mark is the curtain's opening A (assets/mark-a.svg, the same Soria
   outline) in solid terracotta, the site's identity colour. */
.wordmark__a {
  display: block;
  height: 28px;
  aspect-ratio: 514 / 687;
  background: var(--terracotta);
  -webkit-mask: url("../assets/mark-a.svg") center / contain no-repeat;
          mask: url("../assets/mark-a.svg") center / contain no-repeat;
  transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease);
}
.wordmark:hover .wordmark__a { transform: translateY(-1px); }
/* The homepage opens on her name and the work, with nothing over them: the bar
   fades in on the first scroll. Inner pages keep it from the top. It also
   appears for keyboard focus, so the navigation is never out of reach. */
body.home .site-header {
  transition: opacity 360ms var(--ease), transform 360ms var(--ease),
              box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
body.home:not(.nav-open) .site-header:not(.is-stuck):not(:focus-within) {
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
}

.nav { display: flex; align-items: center; gap: clamp(var(--sp-5), 3vw, var(--sp-7)); }
.nav a {
  position: relative;
  display: inline-flex; align-items: center;
  min-height: 44px;
  font-size: var(--step--1);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--ink-soft);
  transition: color var(--dur) var(--ease);
}
.nav a::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 10px;
  height: 1px; background: var(--blue);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.nav a:hover { color: var(--ink); }
.nav a:hover::after, .nav a[aria-current="page"]::after { transform: scaleX(1); }
.nav a[aria-current="page"] { color: var(--ink); }

.nav-toggle {
  display: none;
  width: 48px; height: 48px;
  align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer;
}
.nav-toggle span {
  display: block; width: 22px; height: 1px; background: var(--ink);
  position: relative; transition: background 120ms linear;
}
.nav-toggle span::before, .nav-toggle span::after {
  content: ""; position: absolute; left: 0; width: 22px; height: 1px; background: var(--ink);
  transition: transform var(--dur) var(--ease);
}
.nav-toggle span::before { top: -7px; }
.nav-toggle span::after  { top: 7px; }
body.nav-open .nav-toggle span { background: transparent; }
body.nav-open .nav-toggle span::before { transform: translateY(7px) rotate(45deg); }
body.nav-open .nav-toggle span::after  { transform: translateY(-7px) rotate(-45deg); }

@media (max-width: 800px) {
  .nav-toggle { display: inline-flex; }
  .nav {
    position: fixed;
    inset: 0;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: var(--sp-4);
    padding: var(--gutter);
    background: var(--paper);
    transform: translateY(-100%);
    transition: transform 420ms var(--ease);
    z-index: 99;
  }
  body.nav-open .nav { transform: translateY(0); }
  .nav a {
    font-family: var(--font-display);
    font-size: var(--step-3);
    letter-spacing: 0.06em;
    text-transform: none;
  }
  .nav a::after { bottom: 12px; }
  .site-header__inner { position: relative; z-index: 101; }
  /* The panel is a child of the header row, so lift the mark and the close
     button above it or the open menu covers its own way out */
  .wordmark, .nav-toggle { position: relative; z-index: 100; }
  body.nav-open .site-header { background: var(--paper); border-bottom-color: transparent; }
}

/* --- Curtain ------------------------------------------------------------
   A separate entry screen that sits over the whole site until you click in,
   then lifts away like a real curtain. The name is drawn as outlines with a
   viewBox trimmed to the glyphs, so at width 100% it spans the viewport
   exactly, edge to edge, at any size and without waiting on the webfont.
   ------------------------------------------------------------------------ */
.curtain {
  position: fixed;
  inset: 0;
  z-index: 300;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  transition: transform 1000ms cubic-bezier(0.76, 0, 0.24, 1);
}
.curtain.is-open { transform: translateY(-100%); }
.curtain[hidden],
.curtain-seen .curtain { display: none; }

.curtain__art { position: absolute; inset: 0; margin: 0; }
/* Four works take turns behind the name, each crossfading in over the last.
   The first stays lit underneath, so there is never a gap between them. */
.curtain__art--b, .curtain__art--c, .curtain__art--d { opacity: 0; transition: opacity 620ms var(--ease); }
.curtain[data-art="1"] .curtain__art--b,
.curtain[data-art="2"] .curtain__art--c,
.curtain[data-art="3"] .curtain__art--d { opacity: 1; }
.curtain__art img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 42%;
}
.curtain__scrim {
  position: absolute; inset: 0;
  /* An even wash with a very gradual lift through the middle. Spread across many
     stops so it reads as the painting in shadow rather than a band laid over it. */
  background:
    linear-gradient(to bottom,
      rgba(28, 24, 21, 0)    0%,
      rgba(28, 24, 21, 0.03) 12%,
      rgba(28, 24, 21, 0.08) 24%,
      rgba(28, 24, 21, 0.14) 34%,
      rgba(28, 24, 21, 0.22) 44%,
      rgba(28, 24, 21, 0.22) 56%,
      rgba(28, 24, 21, 0.13) 66%,
      rgba(28, 24, 21, 0.07) 78%,
      rgba(28, 24, 21, 0.02) 90%,
      rgba(28, 24, 21, 0)    100%),
    linear-gradient(rgba(28, 24, 21, 0.38), rgba(28, 24, 21, 0.38));
}

/* Inverted state: a sheet of paper with the name cut out of it, so the painting
   shows through the letters. The cut is built in JS from the same path
   data as the visible name, so the texture inside the letters lines up exactly
   with the painting behind, which is what makes the flip read as one image. */
.curtain__knockout {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity 420ms var(--ease);
}
.curtain__scrim,
.curtain__name { transition: opacity 420ms var(--ease); }

.curtain.is-inverted .curtain__knockout { opacity: 1; }
.curtain.is-inverted .curtain__name { opacity: 0; }
.curtain.is-inverted .curtain__scrim { opacity: 0; }
.curtain.is-inverted .curtain__eyebrow { color: var(--muted); text-shadow: none; }
.curtain.is-inverted .curtain__enter { background: var(--ink); color: var(--paper); }
.curtain.is-inverted .curtain__enter:hover { background: var(--terracotta); }

.curtain__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  text-align: center;
}
.curtain__rule { width: 100%; }
/* The eyebrow hangs above the name instead of sharing its box, so the name
   itself sits on the true centre of the screen, where the opening A starts. */
.curtain__inner > .curtain__rule {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
}
.curtain__eyebrow {
  color: rgba(250, 246, 239, 0.92);
  text-shadow: 0 1px 12px rgba(28, 24, 21, 0.8);
  margin-bottom: clamp(var(--sp-4), 2.2vh, var(--sp-6));
}
.curtain__eyebrow span { white-space: nowrap; }
/* Phones: two clean lines, breaking between the phrases rather than inside one */
@media (max-width: 479px) {
  .curtain__eyebrow span { display: block; }
  .curtain__dot { display: none !important; }
}

/* No gutter here on purpose: the name runs to both edges of the screen */
.curtain__name { display: block; width: 100%; color: var(--paper); }
.curtain__word {
  display: block;
  width: 100%;
  height: auto;
  /* A soft shadow hugging the letters buys legibility over the busiest paint
     without dimming the canvas any further. */
  filter: drop-shadow(0 1px 3px rgba(28, 24, 21, 0.55))
          drop-shadow(0 4px 26px rgba(28, 24, 21, 0.75));
}
.curtain__word--tall { display: none; }
@media (max-aspect-ratio: 4 / 5) {
  .curtain__word--wide { display: none; }
  .curtain__word--tall { display: block; }
}

/* The button drops to the foot of the screen, clear of the centred name.
   On short windows it stays in the flow so the two cannot collide. */
.curtain__foot { text-align: center; position: relative; z-index: 3; }
@media (min-height: 560px) {
  .curtain__foot {
    position: absolute;
    left: 0;
    right: 0;
    bottom: max(clamp(var(--sp-6), 7vh, var(--sp-9)), calc(env(safe-area-inset-bottom) + var(--sp-5)));
  }
  .curtain__foot .curtain__enter { margin-top: 0; }
}

.curtain__enter {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: clamp(var(--sp-5), 3vh, var(--sp-7));
  min-height: 52px;
  padding: 0 var(--sp-7);
  /* Cream rather than the usual blue: against the painting a blue fill loses its
     edge, and this is the single way into the site. */
  background: var(--paper);
  color: var(--ink);
  border-radius: 2px;
  font-size: var(--step--1);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
  text-decoration: none;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.curtain__enter { transition: background var(--dur) var(--ease), color var(--dur) var(--ease), transform var(--dur-press) var(--ease); }
.curtain__enter svg { width: 16px; height: 16px; transition: transform var(--dur) var(--ease); }
.curtain__enter:hover { background: var(--terracotta); color: var(--paper); }
.curtain__enter:hover svg { transform: translateX(5px); }

/* Opening: paper with only the A cut out, the rest of the name to follow.
   Set from the head before first paint, so the painting never flashes first;
   JS removes the class when the name is complete and the cycle takes over. */
/* Eyebrow and Enter settle in as the name lands: a long ease-out fade with a
   short rise, the button a beat after the line above it. */
.curtain__eyebrow,
.curtain__foot {
  transition: opacity 1100ms cubic-bezier(0.22, 1, 0.36, 1),
              transform 1100ms cubic-bezier(0.22, 1, 0.36, 1);
}
.curtain__foot { transition-delay: 220ms; }
.curtain-intro .curtain__knockout { opacity: 1; transition: none; }
.curtain-intro .curtain__knockout:empty { background: var(--paper); }
.curtain-intro .curtain__name,
.curtain-intro .curtain__scrim { opacity: 0; transition: none; }
.curtain-intro .curtain__eyebrow { opacity: 0; transform: translateY(10px); }
.curtain-intro .curtain__foot { opacity: 0; transform: translateY(14px); }

body.curtain-up { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .curtain { transition: none; }
}

/* --- Opening hero, the first screen inside the curtain ------------------ */
.opening { padding-top: clamp(var(--sp-9), 13vh, 150px); }
.opening__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(var(--sp-6), 4.5vw, var(--sp-8));
}
.opening__name {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--terracotta);   /* the fallback if background-clip is unavailable */
  font-size: clamp(3.4rem, 3.3rem + 7.4vw, 12rem);
  line-height: 0.96;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  margin-top: var(--sp-4);
}
.opening__name span { display: block; }
.opening__name .is-indent { padding-left: 0.14em; }
.opening .lede { margin-top: var(--sp-6); }

.opening__art { margin: 0; }
.opening__art .frame { box-shadow: var(--shadow-lg); }
.opening__art figcaption {
  margin-top: var(--sp-4);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-3);
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: 0.06em;
}
.opening__art figcaption b {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--red-deep);
}

/* Stack a little earlier, so the name is never squeezed into a narrow column */
@media (max-width: 1000px) {
  .opening__inner { grid-template-columns: 1fr; gap: var(--sp-7); }
}
/* Phones: the work comes straight after the name, so it is whole and in view on
   the first screen; the line and the buttons follow under it. */
@media (max-width: 760px) {
  .opening { padding-top: var(--sp-6); }
  .opening__inner { gap: var(--sp-4); }
  .opening__text { display: contents; }
  .opening__name { order: 1; font-size: clamp(3rem, 2.2rem + 5.6vw, 4.4rem); }
  .opening__art { order: 2; }
  .opening__art figcaption { margin-top: var(--sp-3); }
  .opening .lede { order: 3; margin-top: 0; }
  .opening .actions { order: 4; margin-top: 0; }
}

/* Framed artwork */
.frame {
  position: relative;
  background: var(--card);
  padding: clamp(10px, 1.4vw, 18px);
  border: 1px solid var(--border);
  overflow: hidden;
}
.frame img {
  width: 100%; height: auto;
  transition: transform 500ms var(--ease);
}
.frame::after {
  content: "";
  position: absolute; inset: 0;
  box-shadow: inset 0 0 0 1px rgba(28, 24, 21, 0.06);
  pointer-events: none;
}

/* --- Work grid ---------------------------------------------------------- */
.work-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
  gap: clamp(var(--sp-7), 4vw, var(--sp-8)) clamp(var(--sp-6), 4vw, var(--sp-8));
  margin-top: var(--sp-7);
}
/* Hang the works on a shared line so captions align across the row */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 780px) {
    .work-grid > .work-card { grid-row: span 2; display: grid; grid-template-rows: subgrid; }
  }
}
.work-card {
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  cursor: zoom-in;
  border: 0;
  background: none;
  padding: 0;
  text-align: left;
  width: 100%;
}
.work-card__media {
  display: block;
  position: relative;
  align-self: end;
  overflow: hidden;
  background: var(--paper-3);
  border: 1px solid var(--border);
  transition: box-shadow 420ms var(--ease), border-color var(--dur) var(--ease);
}
.work-card__media img { width: 100%; height: auto; transition: transform 500ms var(--ease); }
.work-card:hover .work-card__media { box-shadow: var(--shadow-md); border-color: var(--border-strong); }
.work-card:hover .work-card__media img { transform: scale(1.035); }
.work-card__badge {
  position: absolute;
  left: var(--sp-4); top: var(--sp-4);
  padding: 6px 12px;
  background: rgba(250, 246, 239, 0.92);
  font-size: 0.75rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--ink-soft);
}
.work-card__body { display: block; padding-top: var(--sp-5); }
.work-card__index {
  display: block;
  font-size: var(--step--1); letter-spacing: 0.2em; color: var(--gold);
  font-weight: 600;
}
.work-card__title {
  font-family: var(--font-display);
  color: var(--red-deep);
  font-size: var(--step-2);
  line-height: 1.15;
  margin-top: var(--sp-2);
}
.work-card__meta {
  display: block;
  font-size: var(--step--1);
  color: var(--muted);
  letter-spacing: 0.08em;
  margin-top: var(--sp-2);
}
/* --- Category banners --------------------------------------------------- */
.banners { display: grid; gap: var(--sp-4); margin-top: var(--sp-7); }
.banner {
  position: relative;
  display: block;
  overflow: hidden;
  text-decoration: none;
  border: 1px solid var(--border);
  --banner-h: clamp(220px, 27vw, 340px);
  min-height: var(--banner-h);
  background: var(--paper-3);
  isolation: isolate;
}
.banner__bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 900ms var(--ease);
  z-index: -2;
}
.banner__wash {
  position: absolute; inset: 0;
  z-index: -1;
  transition: opacity 420ms var(--ease);
}
.banner--photo .banner__wash {
  background: linear-gradient(100deg, rgba(28, 24, 21, 0.88) 0%, rgba(61, 21, 15, 0.62) 46%, rgba(61, 21, 15, 0.28) 100%);
}
/* Real paint rather than synthetic gradients. Each is a close-up from Iskay Apu,
   picked so one colour family dominates and neither grey road nor white wall
   creeps in. The wash on each is sized from measurement, not taste: enough for
   cream type to clear 4.5:1 against the lightest paint in that crop. */
.banner--ochre {
  background-size: cover; background-position: center;
}
.banner--indigo {
  background-size: cover; background-position: center;
}
.banner--green {
  background-size: cover; background-position: center;
}
.banner--red {
  background-size: cover; background-position: center;
}
.banner--red .banner__wash {
  background: linear-gradient(to right, rgba(28,24,21,0.38) 0%, rgba(28,24,21,0.24) 46%, rgba(28,24,21,0.12) 100%);
}
.banner--clay   { background: linear-gradient(115deg, #4A1B10 0%, #8F3A18 55%, #A8431C 100%); }
/* Weighted to the left, where the type sits, so the paint stays bright on the
   right. Strengths set from measuring the rendered banners, not the source crop. */
.banner--ochre .banner__wash {
  background: linear-gradient(to right, rgba(28,24,21,0.24) 0%, rgba(28,24,21,0.13) 46%, rgba(28,24,21,0.06) 100%);
}
.banner--indigo .banner__wash {
  background: linear-gradient(to right, rgba(28,24,21,0.40) 0%, rgba(28,24,21,0.26) 46%, rgba(28,24,21,0.15) 100%);
}
.banner--green .banner__wash {
  background: linear-gradient(to right, rgba(28,24,21,0.62) 0%, rgba(28,24,21,0.44) 46%, rgba(28,24,21,0.26) 100%);
}
.banner--clay .banner__wash {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='4'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23g)' opacity='0.5'/%3E%3C/svg%3E");
  opacity: 0.18;
  mix-blend-mode: overlay;
}
.banner:hover .banner__bg { transform: scale(1.05); }
.banner:hover .banner__wash { opacity: 0.82; }

.banner__title {
  /* One size for all four. Two-word titles stack, so the type pairs across the
     stack instead of each word being fitted to the width at its own scale.
     The binding constraint is height: two lines have to sit inside the banner,
     which is why the size caps before the width limit is reached. */
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  top: 50%;
  transform: translateY(-50%);
  z-index: 1;
  display: block;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.5rem, -0.25rem + 12.1vw, 10rem);
  line-height: 0.92;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: rgba(250, 246, 239, 0.98);
  text-shadow: 0 2px 22px rgba(28, 24, 21, 0.45);
  pointer-events: none;
}
.banner__title span { display: block; }

/* --- Split section (about teaser, contact) ------------------------------ */
.split {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(var(--sp-7), 6vw, var(--sp-9));
  align-items: start;
}
@media (max-width: 900px) { .split { grid-template-columns: 1fr; } }

.portrait { position: relative; }
.portrait img { width: 100%; }

.pull-quote {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.35;
  color: var(--red-deep);
  border-left: 2px solid var(--terracotta);
  padding-left: var(--sp-5);
  margin: var(--sp-7) 0;
  max-width: 42ch;
}

/* --- Page hero (interior pages) ----------------------------------------- */
.page-hero {
  padding-top: clamp(116px, 15vh, 168px);
  padding-bottom: clamp(var(--sp-6), 4vw, var(--sp-8));
  position: relative;
  z-index: 2;
}
.page-hero__title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--red-deep);
  font-size: var(--step-4);
  line-height: 1.02;
  letter-spacing: 0.02em;
  margin-top: var(--sp-4);
}
.page-hero .lede { margin-top: var(--sp-5); }

.breadcrumb {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  font-size: var(--step--1); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); text-decoration: none; min-height: 44px;
}
.breadcrumb svg { width: 15px; height: 15px; transition: transform var(--dur) var(--ease); }
.breadcrumb:hover { color: var(--ink); }
.breadcrumb:hover svg { transform: translateX(-4px); }

/* --- Upcoming category panel -------------------------------------------- */
.upcoming {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--sp-7), 6vw, var(--sp-9));
  align-items: center;
  background: var(--card);
  border: 1px solid var(--border);
  padding: clamp(var(--sp-6), 5vw, var(--sp-9));
}
@media (max-width: 860px) { .upcoming { grid-template-columns: 1fr; } }
.upcoming__swatch {
  aspect-ratio: 4 / 3;
  position: relative;
  overflow: hidden;
}
.upcoming__swatch::after {
  content: "";
  position: absolute; inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='4'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23g)' opacity='0.5'/%3E%3C/svg%3E");
  opacity: 0.2; mix-blend-mode: overlay;
}
.upcoming__list {
  list-style: none; margin: var(--sp-6) 0 0; padding: 0;
  display: grid; gap: var(--sp-3);
}
.upcoming__list li {
  display: flex; gap: var(--sp-4); align-items: baseline;
  color: var(--ink-soft);
  padding-block: var(--sp-2);
  border-top: 1px solid var(--border);
}
.upcoming__list li:first-child { border-top: 0; }
.upcoming__list b {
  font-family: var(--font-display); font-weight: 400; font-size: var(--step-1);
  color: var(--gold); min-width: 2.4ch;
}

/* --- Contact ------------------------------------------------------------ */
/* The contact details sit on a painted card, cream on the deep base of Wallpariy's
   field, which clears 9.9:1 against the lightest paint anywhere in that crop. */
.contact-list {
  list-style: none;
  margin: var(--sp-7) 0 0;
  padding: clamp(var(--sp-4), 3vw, var(--sp-6)) clamp(var(--sp-5), 4vw, var(--sp-7));
  display: grid;
  gap: 0;
  border-radius: 2px;
  color: var(--paper);
  background-size: cover;
  background-position: center;
  background-size: cover;
  background-position: center;
  box-shadow: var(--shadow-md);
}
.contact-list li { border-top: 1px solid rgba(250, 246, 239, 0.2); }
.contact-list li:first-child { border-top: 0; }
.contact-list li:last-child { border-bottom: 0; }
.contact-list a, .contact-list > li > span {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-2);
  padding-block: var(--sp-5);
  min-height: 76px;
  text-decoration: none;
  transition: color var(--dur) var(--ease);
}
.contact-list a > * { transition: transform var(--dur) var(--ease); }
.contact-list a:hover { color: var(--gold-light); }
.contact-list a:hover > * { transform: translateX(var(--sp-4)); }
.value--ig { display: inline-flex; align-items: center; gap: var(--sp-3); }
.value--ig .ig { width: 1em; height: 1em; flex: none; opacity: 0.9; }

.contact-list .label {
  font-size: var(--step--1); letter-spacing: 0.2em; text-transform: uppercase;
  color: rgba(250, 246, 239, 0.72);
}
.contact-list .value {
  font-family: var(--font-display); font-size: var(--step-2); line-height: 1.2;
  overflow-wrap: anywhere;
}
.placeholder-note {
  margin-top: var(--sp-6);
  padding: var(--sp-5);
  background: var(--paper-2);
  border-left: 2px solid var(--gold);
  color: var(--muted);
  /* Read by Ariana in the demo, so kept at a comfortable size on phones too */
  font-size: max(var(--step--1), 0.875rem);
  line-height: 1.7;
  max-width: 62ch;
}
.is-placeholder { color: var(--muted); font-style: italic; }
.contact-list .is-placeholder { color: rgba(250, 246, 239, 0.66); }

/* Anything linked to by name sits clear of the fixed header when it lands */
:target { scroll-margin-top: clamp(80px, 12vh, 110px); }
/* The homepage quote and the full statement are the same words, so on the way
   between them the quote travels into place instead of the pages crossfading. */
.statement--feature, .statement--reading { view-transition-name: statement-band; }
.statement--feature .section-title,
.statement--reading .section-title { view-transition-name: statement-title; }
.statement--feature .statement-quote,
.statement--reading .statement-quote { view-transition-name: statement; }
::view-transition-group(statement-title) { animation-duration: 520ms; animation-timing-function: var(--ease); }
::view-transition-group(statement-band) { animation-duration: 560ms; animation-timing-function: var(--ease); }
::view-transition-old(statement-band),
::view-transition-new(statement-band) { animation-duration: 280ms; animation-timing-function: var(--ease); }
::view-transition-group(statement) { animation-duration: 520ms; animation-timing-function: var(--ease); }
::view-transition-old(statement),
::view-transition-new(statement) { animation-duration: 320ms; animation-timing-function: var(--ease); }

/* Arriving straight at a section: it settles in rather than simply being there */
.is-landing > .shell > *:not(.statement-quote) { animation: arrive 700ms var(--ease) both; }
.is-landing > .shell > :nth-child(1) { animation-delay: 60ms; }
.is-landing > .shell > :nth-child(2) { animation-delay: 160ms; }
.is-landing > .shell > :nth-child(3) { animation-delay: 240ms; }
@media (prefers-reduced-motion: reduce) {
  .is-landing > .shell > * { animation-name: arrive-fade; }
}

/* Her signature, cut from the scan she sent, closing the biography */
.signature {
  display: block;
  width: clamp(190px, 26%, 260px);
  height: auto;
  margin-top: var(--sp-6);
  opacity: 0.85;
}
/* Closing the statement: sits at the end of the reading column, as on a canvas */
.statement--reading .signature--light {
  width: clamp(200px, 30%, 280px);
  max-width: 60ch;
  margin: var(--sp-4) auto 0;
  margin-right: max(0px, calc((100% - 60ch) / 2));
  opacity: 0.92;
}

/* --- Lightbox ----------------------------------------------------------- */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  display: none;
  /* Solid rather than blurred: backdrop-filter would make this the containing block
     for the fixed close and arrow buttons, so they would scroll away, and any
     see-through let the page's large titles ghost behind the statement. */
  background: #14100D;
  padding: clamp(var(--sp-5), 4vw, var(--sp-8));
  overflow-y: auto;
  /* Always rendered so it can fade. Hidden = invisible and inert; the exit is
     quicker than the entry so closing feels instant. */
  display: block;
  visibility: hidden;
  opacity: 0;
  transition: opacity 160ms var(--ease), visibility 0s linear 160ms;
}
.lightbox.is-open {
  visibility: visible;
  opacity: 1;
  transition: opacity 240ms var(--ease), visibility 0s;
}
.lightbox__inner {
  min-height: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) minmax(0, 0.85fr);
  gap: clamp(var(--sp-6), 4vw, var(--sp-8));
  align-items: center;
  max-width: 1400px;
  margin-inline: auto;
  opacity: 0;
  transform: scale(0.98) translateY(8px);
  transition: opacity 160ms var(--ease), transform 160ms var(--ease);
}
.lightbox.is-open .lightbox__inner {
  opacity: 1; transform: none;
  transition: opacity 320ms var(--ease), transform 320ms var(--ease);
}
.lightbox img { width: 100%; height: auto; max-height: 84vh; object-fit: contain; margin-inline: auto; }
.lightbox__meta { color: #EDE4D6; }
.lightbox__title {
  font-family: var(--font-display); font-weight: 400;
  font-size: var(--step-3); line-height: 1.1; color: #FAF6EF;
  margin-top: var(--sp-3);
}
.lightbox__sub { font-size: var(--step--1); letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold-light); }
.lightbox__text { margin-top: var(--sp-5); color: #D9CFC2; max-width: 38ch; line-height: 1.75; }
.lightbox__close {
  position: fixed; top: var(--sp-5); right: var(--sp-5);
  width: 52px; height: 52px;
  display: grid; place-items: center;
  background: rgba(250, 246, 239, 0.08);
  border: 1px solid rgba(250, 246, 239, 0.28);
  border-radius: 50%;
  color: #FAF6EF; cursor: pointer;
  transition: background var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.lightbox__close:hover { background: rgba(250, 246, 239, 0.2); }
.lightbox__close svg { width: 18px; height: 18px; }
.lightbox__nav {
  position: fixed; bottom: var(--sp-5); left: 50%; transform: translateX(-50%);
  display: flex; gap: var(--sp-3);
}
.lightbox__nav button {
  width: 52px; height: 52px;
  display: grid; place-items: center;
  background: rgba(250, 246, 239, 0.08);
  border: 1px solid rgba(250, 246, 239, 0.28);
  border-radius: 50%;
  color: #FAF6EF; cursor: pointer;
  transition: background var(--dur) var(--ease), transform var(--dur-press) var(--ease);
}
.lightbox__nav button:hover { background: rgba(250, 246, 239, 0.2); }
.lightbox__nav svg { width: 18px; height: 18px; }
@media (max-width: 900px) {
  .lightbox__inner { grid-template-columns: 1fr; align-items: start; padding-top: var(--sp-8); padding-bottom: 120px; }
  .lightbox img { max-height: 56vh; }
  /* The statement is only readable here, so the arrows sit on a bar that fades
     to the backdrop: text slides under it instead of colliding with the buttons,
     and the last line scrolls clear of it. */
  .lightbox__nav {
    left: 0; right: 0; bottom: 0; transform: none;
    justify-content: center;
    padding: var(--sp-7) var(--sp-5) calc(var(--sp-5) + env(safe-area-inset-bottom));
    background: linear-gradient(to top, rgba(20, 16, 13, 1) 45%, rgba(20, 16, 13, 0));
    pointer-events: none;
  }
  .lightbox__nav button { pointer-events: auto; background-color: rgba(38, 32, 28, 0.9); }
}

/* --- Footer ------------------------------------------------------------- 
   A slim painted strip carrying only the copyright line: the header is always
   on screen, so the footer does not repeat the name or the navigation. */
.site-footer {
  position: relative; z-index: 2;
  background-color: var(--red-deep);
  background-size: cover;
  background-position: center;
  color: #E9DFD0;
  padding-block: var(--sp-6);
}
.site-footer__bottom {
  display: flex; flex-wrap: wrap; gap: var(--sp-4);
  justify-content: space-between; align-items: center;
  font-size: var(--step--1);
  color: #C6BAAA;
  letter-spacing: 0.06em;
}



/* --- Reveal animation --------------------------------------------------- */
.reveal { opacity: 0; transform: translateY(14px); }
.reveal.is-visible {
  opacity: 1; transform: none;
  transition: opacity 560ms var(--ease), transform 560ms var(--ease);
  transition-delay: var(--reveal-delay, 0ms);
}

/* --- Arriving on a page -------------------------------------------------
   The first screen of every page settles in: a short rise and fade, staggered
   about 60ms apart. On the homepage it only plays when you arrive from inside
   the site, because on a first visit the curtain is the entrance. */
@keyframes arrive { from { opacity: 0; transform: translateY(12px); } }
@keyframes arrive-fade { from { opacity: 0; } }
.page-hero .shell > *,
.opening__text > *,
.opening__art {
  animation: arrive 620ms var(--ease) both;
}
.page-hero .shell > :nth-child(1), .opening__text > :nth-child(1) { animation-delay: 40ms; }
.page-hero .shell > :nth-child(2), .opening__text > :nth-child(2) { animation-delay: 100ms; }
.page-hero .shell > :nth-child(3), .opening__text > :nth-child(3) { animation-delay: 160ms; }
.page-hero .shell > :nth-child(4), .opening__text > :nth-child(4) { animation-delay: 220ms; }
.opening__art { animation-delay: 160ms; animation-duration: 760ms; }
/* First visit: the curtain covers the page, so there is nothing to animate */
.curtain-intro .opening__text > *,
.curtain-intro .opening__art { animation: none; }

/* Moving between pages: a short crossfade, the header held still */
@view-transition { navigation: auto; }
.site-header { view-transition-name: site-header; }
::view-transition-old(root) { animation: 140ms var(--ease) both vt-out; }
::view-transition-new(root) { animation: 220ms var(--ease) both vt-in; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; } }

/* Press feedback: every button gives a little under the finger */
.btn:active, .curtain__enter:active, .wall__controls button:active,
.lightbox__close:active, .lightbox__nav button:active, .nav-toggle:active {
  transform: scale(0.97);
  transition-duration: var(--dur-press);
}

/* Phone menu: the links follow the panel in, one after another */
@media (max-width: 800px) {
  .nav a { opacity: 0; transform: translateY(10px); transition: opacity 240ms var(--ease), transform 240ms var(--ease), color var(--dur) var(--ease); }
  body.nav-open .nav a { opacity: 1; transform: none; }
  body.nav-open .nav a:nth-child(1) { transition-delay: 120ms; }
  body.nav-open .nav a:nth-child(2) { transition-delay: 170ms; }
  body.nav-open .nav a:nth-child(3) { transition-delay: 220ms; }
  .nav { transition: transform 420ms var(--ease-drawer); }
}

/* Touch screens trigger :hover on tap, so hover movement stays on real pointers */
@media (hover: none) {
  .btn:hover svg, .link-arrow:hover svg, .breadcrumb:hover svg, .curtain__enter:hover svg,
  .wordmark:hover .wordmark__a, .work-card:hover .work-card__media img,
  .contact-list a:hover > * { transform: none; }
}

/* Reduced motion: fewer and gentler, not none. Fades stay; movement goes. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { transform: none; }
  .reveal.is-visible { transition: opacity 400ms ease; }
  .page-hero .shell > *, .opening__text > *, .opening__art { animation-name: arrive-fade; }
  .lightbox__inner { transform: none; }
  .frame img, .work-card__media img { transition: none; }
  .work-card:hover .work-card__media img, .contact-list a:hover > *,
  .btn:hover svg, .link-arrow:hover svg, .breadcrumb:hover svg { transform: none; }
  .btn:active, .curtain__enter:active, .wall__controls button:active,
  .lightbox__close:active, .lightbox__nav button:active { transform: none; }
  .nav a { transform: none; }
  @view-transition { navigation: none; }
}

/* --- Gallery wall --------------------------------------------------------
   Works laid flat in a row and moved sideways. No perspective anywhere: a
   rotated plane on a curve is what made the previous ring slice through
   itself, and foreshortening a painting is wrong regardless.
   ------------------------------------------------------------------------ */
/* One hanging height for every work. Capped so the widest piece (Iskay Apu, 1.45
   wide) still fits the screen, and so caption and controls stay in view. */
.wall { margin-top: var(--sp-6); --wall-h: min(520px, calc(100svh - 330px), calc(84vw / 1.45)); }

.wall__viewport {
  position: relative;
  /* Clip sideways only, so the centre work's shadow can fall below the row
     instead of ending in a hard edge */
  overflow: hidden;
  overflow-x: clip;
  overflow-y: visible;
  margin-inline: calc(50% - 50vw);     /* run the wall edge to edge */
  padding-block: var(--sp-5);
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}
.wall__viewport.is-dragging { cursor: grabbing; }

.wall__track {
  display: flex;
  align-items: center;
  gap: clamp(var(--sp-5), 3vw, var(--sp-8));
  will-change: transform;
}

.wall__item {
  flex: 0 0 auto;
  /* Works of different shapes hang at a common height, the way a gallery hangs
     a wall: each work keeps its own proportions and takes the width it needs.
     The height leaves room for the caption and controls on one screen. */
  width: auto;
  margin: 0;
  /* --p is set every frame by js/wall.js: 1 at the centre, 0 a work away.
     Size, shadow and scrim all follow it, so they move with the row. */
  --p: 0;
  transform: scale(calc(0.88 + 0.12 * var(--p)));
  will-change: transform;
}

.wall__open {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  border-radius: 2px;
  box-shadow: 0 18px 40px rgba(61, 21, 15, 0.20);
}
/* The deeper shadow of the centre work, faded in on its own layer rather than
   by animating box-shadow, which repaints every frame */
.wall__item { position: relative; }
.wall__item::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 2px;
  box-shadow: 0 30px 66px rgba(61, 21, 15, 0.30), 0 2px 6px rgba(61, 21, 15, 0.12);
  opacity: var(--p);
  pointer-events: none;
}
.wall__open img {
  display: block;
  height: var(--wall-h);
  width: auto;                 /* the width follows each work's own proportions */
}

/* Works off centre step back behind a scrim. They never go transparent:
   a see-through painting reads as a ghost rather than a canvas. */
.wall__open::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(28, 24, 21, 0.42);
  pointer-events: none;
  opacity: calc(1 - var(--p));
}

/* The last card on the wall: the way through to every collection */
.wall__item--more { width: clamp(200px, 22vw, 300px); }
.wall__more {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--sp-3);
  height: var(--wall-h);
  padding: var(--sp-6);
  border: 1px solid var(--border-strong);
  border-radius: 2px;
  background: var(--paper-2);
  text-decoration: none;
  color: var(--red-deep);
}
.wall__more__label {
  font-family: var(--font-display);
  font-size: var(--step-2);
  line-height: 1.1;
}
.wall__more__cue {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  font-size: var(--step--1); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--blue);
}
.wall__more__cue svg { width: 15px; height: 15px; transition: transform var(--dur) var(--ease); }
.wall__more:hover .wall__more__cue svg { transform: translateX(5px); }

.wall__cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-7) var(--sp-4) var(--sp-4);
  color: #FAF6EF;
  font-family: var(--font-display);
  font-size: var(--step-1);
  letter-spacing: 0.02em;
  text-align: left;
  background: linear-gradient(to top, rgba(28, 24, 21, 0.78), rgba(28, 24, 21, 0));
}
.wall__cap .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--gold-light); flex: none; }
.wall__cap.is-slot { font-style: italic; color: rgba(250, 246, 239, 0.74); }
.wall__cap.is-slot .dot { background: rgba(250, 246, 239, 0.45); }
.wall__item[data-placeholder] .wall__open { cursor: default; }
.wall__item[data-placeholder][data-active="false"] .wall__open { cursor: pointer; }

.wall__status {
  margin-top: var(--sp-5);
  text-align: center;
  font-size: var(--step--1);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

.wall__controls {
  margin-top: var(--sp-4);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
}
.wall__controls button {
  width: 48px; height: 48px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--card);
  color: var(--ink);
  cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease), transform var(--dur-press) var(--ease);
}
.wall__controls button:hover {
  border-color: transparent;
  color: var(--paper);
  background-color: var(--blue);
  background-size: 380% auto;
  background-position: 42% 55%;
}
.wall__controls button svg { width: 17px; height: 17px; }
/* Touch screens: swiping is the control, so the arrows and pause step aside.
   The first swipe also stops the auto-rotation (js/wall.js). */
@media (hover: none) and (pointer: coarse) {
  .wall__controls { display: none; }
}
.wall__controls .is-primary {
  width: 58px; height: 58px;
  border-color: transparent;
  color: var(--paper);
  background-color: var(--blue);
  background-size: 340% auto;
  background-position: 38% 52%;
  transition: background-position 450ms var(--ease), transform var(--dur-press) var(--ease);
}
.wall__controls .is-primary:hover { background-position: 62% 40%; }
.wall__controls button[disabled] { opacity: 0.35; cursor: default; }
.wall__controls button[disabled]:hover {
  background-image: none;
  background-color: var(--card);
  border-color: var(--border-strong);
  color: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  .wall__track, .wall__item, .wall__open, .wall__open::after { transition: none; }  /* motion is JS-driven; reduced motion jumps straight to place */
}

/* --- Paint fragments ------------------------------------------------------
   Every painted surface wears its own piece of Wallpariy's shadowed field, cut from
   a separate spot of the original scan (assets/red/), so no texture ever repeats.
   Pieces are graded to one tone so they read as the same paint. */
[data-frag="footer-1"] { background-image: url("../assets/red/footer-1.jpg"); background-image: image-set(url("../assets/red/footer-1.webp") type("image/webp"), url("../assets/red/footer-1.jpg") type("image/jpeg")); }
[data-frag="footer-2"] { background-image: url("../assets/red/footer-2.jpg"); background-image: image-set(url("../assets/red/footer-2.webp") type("image/webp"), url("../assets/red/footer-2.jpg") type("image/jpeg")); }
[data-frag="footer-3"] { background-image: url("../assets/red/footer-3.jpg"); background-image: image-set(url("../assets/red/footer-3.webp") type("image/webp"), url("../assets/red/footer-3.jpg") type("image/jpeg")); }
[data-frag="footer-4"] { background-image: url("../assets/red/footer-4.jpg"); background-image: image-set(url("../assets/red/footer-4.webp") type("image/webp"), url("../assets/red/footer-4.jpg") type("image/jpeg")); }
[data-frag="footer-5"] { background-image: url("../assets/red/footer-5.jpg"); background-image: image-set(url("../assets/red/footer-5.webp") type("image/webp"), url("../assets/red/footer-5.jpg") type("image/jpeg")); }
[data-frag="footer-6"] { background-image: url("../assets/red/footer-6.jpg"); background-image: image-set(url("../assets/red/footer-6.webp") type("image/webp"), url("../assets/red/footer-6.jpg") type("image/jpeg")); }
[data-frag="footer-7"] { background-image: url("../assets/red/footer-7.jpg"); background-image: image-set(url("../assets/red/footer-7.webp") type("image/webp"), url("../assets/red/footer-7.jpg") type("image/jpeg")); }
[data-frag="footer-8"] { background-image: url("../assets/red/footer-8.jpg"); background-image: image-set(url("../assets/red/footer-8.webp") type("image/webp"), url("../assets/red/footer-8.jpg") type("image/jpeg")); }
[data-frag="card"] { background-image: url("../assets/red/card.jpg"); background-image: image-set(url("../assets/red/card.webp") type("image/webp"), url("../assets/red/card.jpg") type("image/jpeg")); }
[data-frag="btn-1"] { background-image: url("../assets/red/btn-1.jpg"); background-image: image-set(url("../assets/red/btn-1.webp") type("image/webp"), url("../assets/red/btn-1.jpg") type("image/jpeg")); }
[data-frag="btn-2"] { background-image: url("../assets/red/btn-2.jpg"); background-image: image-set(url("../assets/red/btn-2.webp") type("image/webp"), url("../assets/red/btn-2.jpg") type("image/jpeg")); }
[data-frag="btn-3"] { background-image: url("../assets/red/btn-3.jpg"); background-image: image-set(url("../assets/red/btn-3.webp") type("image/webp"), url("../assets/red/btn-3.jpg") type("image/jpeg")); }
[data-frag="btn-4"] { background-image: url("../assets/red/btn-4.jpg"); background-image: image-set(url("../assets/red/btn-4.webp") type("image/webp"), url("../assets/red/btn-4.jpg") type("image/jpeg")); }
[data-frag="btn-5"] { background-image: url("../assets/red/btn-5.jpg"); background-image: image-set(url("../assets/red/btn-5.webp") type("image/webp"), url("../assets/red/btn-5.jpg") type("image/jpeg")); }
.wall__controls .is-primary { background-image: url("../assets/red/ctl-1.jpg"); background-image: image-set(url("../assets/red/ctl-1.webp") type("image/webp"), url("../assets/red/ctl-1.jpg") type("image/jpeg")); background-size: cover; background-position: 50% 45%; }
.wall__controls .is-primary:hover { background-position: 50% 15%; }
.wall__controls [data-wall-prev]:hover { background-image: url("../assets/red/ctl-2.jpg"); background-image: image-set(url("../assets/red/ctl-2.webp") type("image/webp"), url("../assets/red/ctl-2.jpg") type("image/jpeg")); background-size: cover; background-position: center; }
.wall__controls [data-wall-next]:hover { background-image: url("../assets/red/ctl-3.jpg"); background-image: image-set(url("../assets/red/ctl-3.webp") type("image/webp"), url("../assets/red/ctl-3.jpg") type("image/jpeg")); background-size: cover; background-position: center; }
/* Blue, green, ochre and the Paintings banner red: same idea, pieces in assets/frag/ */
[data-frag="statement-home"] { background-image: url("../assets/frag/statement-home.jpg"); background-image: image-set(url("../assets/frag/statement-home.webp") type("image/webp"), url("../assets/frag/statement-home.jpg") type("image/jpeg")); }
[data-frag="statement-about"] { background-image: url("../assets/frag/statement-about.jpg"); background-image: image-set(url("../assets/frag/statement-about.webp") type("image/webp"), url("../assets/frag/statement-about.jpg") type("image/jpeg")); }
[data-frag="env-home"] { background-image: url("../assets/frag/env-home.jpg"); background-image: image-set(url("../assets/frag/env-home.webp") type("image/webp"), url("../assets/frag/env-home.jpg") type("image/jpeg")); }
[data-frag="env-work"] { background-image: url("../assets/frag/env-work.jpg"); background-image: image-set(url("../assets/frag/env-work.webp") type("image/webp"), url("../assets/frag/env-work.jpg") type("image/jpeg")); }
[data-frag="env-swatch"] { background-image: url("../assets/frag/env-swatch.jpg"); background-image: image-set(url("../assets/frag/env-swatch.webp") type("image/webp"), url("../assets/frag/env-swatch.jpg") type("image/jpeg")); }
[data-frag="mixed-home"] { background-image: url("../assets/frag/mixed-home.jpg"); background-image: image-set(url("../assets/frag/mixed-home.webp") type("image/webp"), url("../assets/frag/mixed-home.jpg") type("image/jpeg")); }
[data-frag="mixed-work"] { background-image: url("../assets/frag/mixed-work.jpg"); background-image: image-set(url("../assets/frag/mixed-work.webp") type("image/webp"), url("../assets/frag/mixed-work.jpg") type("image/jpeg")); }
[data-frag="mixed-swatch"] { background-image: url("../assets/frag/mixed-swatch.jpg"); background-image: image-set(url("../assets/frag/mixed-swatch.webp") type("image/webp"), url("../assets/frag/mixed-swatch.jpg") type("image/jpeg")); }
[data-frag="char-home"] { background-image: url("../assets/frag/char-home.jpg"); background-image: image-set(url("../assets/frag/char-home.webp") type("image/webp"), url("../assets/frag/char-home.jpg") type("image/jpeg")); }
[data-frag="char-work"] { background-image: url("../assets/frag/char-work.jpg"); background-image: image-set(url("../assets/frag/char-work.webp") type("image/webp"), url("../assets/frag/char-work.jpg") type("image/jpeg")); }
[data-frag="char-swatch"] { background-image: url("../assets/frag/char-swatch.jpg"); background-image: image-set(url("../assets/frag/char-swatch.webp") type("image/webp"), url("../assets/frag/char-swatch.jpg") type("image/jpeg")); }
[data-frag="paint-home"] { background-image: url("../assets/frag/paint-home.jpg"); background-image: image-set(url("../assets/frag/paint-home.webp") type("image/webp"), url("../assets/frag/paint-home.jpg") type("image/jpeg")); }
[data-frag="paint-work"] { background-image: url("../assets/frag/paint-work.jpg"); background-image: image-set(url("../assets/frag/paint-work.webp") type("image/webp"), url("../assets/frag/paint-work.jpg") type("image/jpeg")); }
