:root {
  /* Half of a crossfade: the visible image fades out, the new src swaps in
     (already warmed by prefetchImg, so effectively instant), then it fades
     back in. app.js reads this value so JS timing can't drift from the CSS.
     Keep the unit in ms — app.js parses the number directly. */
  --fade: 180ms;
}

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

.hp {
  position: absolute;
  left: -9999px;
}

.skip-nav {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 999;
  padding: 0.5rem 1rem;
  background: #222;
  color: #fff;
  font-size: 0.85rem;
  text-decoration: none;
}

.skip-nav:focus { left: 0; }

:focus-visible {
  outline: 2px solid #222;
  outline-offset: 2px;
}

/* The series view is a tabindex="-1" landing spot, not a control: app.js moves
   focus here when a series opens so a keyboard or screen-reader user lands in
   the new content instead of back at the top of the document. Nothing can Tab
   to it, so the ring marks no reachable target — it just draws a 1400px box
   around the whole view, which is what a visitor sees on any deep link, since
   Chrome treats programmatic focus with no prior interaction as keyboard
   focus. Controls inside it keep their rings. */
.series-view:focus,
.series-view:focus-visible { outline: none; }

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

body {
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
  background: #fff;
  color: #222;
  -webkit-font-smoothing: antialiased;
  line-height: 1.5;
}

header {
  padding: 3rem 2rem 2rem;
  text-align: center;
}

header h1 {
  font-size: 1.2rem;
  font-weight: 400;
  letter-spacing: 0.2em;
}

/* Wraps rather than overflows: the links plus their letter-spacing exceed a
   320px viewport, which is what body { overflow-x: hidden } used to mask.
   Wrapping keeps it contained regardless of how many links or how long a
   name the header carries. */
nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem 2rem;
  padding: 0 2rem 2.5rem;
  font-size: 0.88rem;
  letter-spacing: 0.12em;
  position: relative;
}

nav a {
  color: #666;
  text-decoration: none;
  transition: color 0.2s;
}

nav a:hover, nav a.active { color: #008555; }

nav a.ig { display: inline-flex; align-items: center; }
nav a.ig svg { width: 16px; height: 16px; fill: #666; transition: fill 0.2s; }
nav a.ig:hover svg { fill: #00A86B; }

/* ---- Series Grid (landing for Work) ---- */
.series-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;
  padding: 0 3rem;
  max-width: 1400px;
  margin: 0 auto;
}

/* Series tiles and thumbnails are real <button>s so Enter/Space, focus order,
   and AT role come from the platform instead of a tabindex/keydown shim.
   These strip the UA button chrome without touching the semantics. */
.series-tile,
.thumb-rail .thumb {
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: inherit;
  display: block;
  cursor: pointer;
}

.series-tile {
  opacity: 0;
  transition: opacity 0.35s ease;
  width: 100%;
}

.series-tile.loaded { opacity: 1; }

/* <span> so it's valid inside the <button> tile; block for the aspect box. */
.series-tile .tile-img {
  display: block;
  overflow: hidden;
  aspect-ratio: 1;
  background: #f5f5f5;
}

.series-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.5s ease;
  border: 2px solid #e0e0e0;
}

/* View fade-in */
.fade-view {
  opacity: 0;
  transition: opacity 0.35s ease;
}

.fade-view.in { opacity: 1; }

.series-tile:hover img { transform: scale(1.04); }

/* Cover crops that read better off-centre are a property of the artwork, not
   of the stylesheet — set per-work via `focalPoint` in data/artworks.json. */

.series-label {
  display: block;
  padding: 0.8rem 0 0;
  text-align: center;
}

.series-label span {
  color: #333;
  font-size: 0.82rem;
  font-weight: 300;
  letter-spacing: 0.12em;
}

/* ---- Home slideshow ---- */
.home {
  position: relative;
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 3rem;
}

.home-slide {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.home-slide .img-wrap {
  width: 100%;
  height: 72vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.home-slide img {
  max-width: 100%;
  max-height: 72vh;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
  border: 2px solid #e0e0e0;
}

/* The hero is a single static work, so it is visible from first paint — no
   JavaScript involved. These are the crossfade states the slideshow drove; they
   pair with the commented-out markup in index.html and slideshow in js/app.js.
.home-slide img { opacity: 0; transition: opacity var(--fade) ease; }
.home-slide img.visible { opacity: 1; }
.home-slide img.fading { opacity: 0; } */

.home-info {
  text-align: center;
  padding-top: 1rem;
}

.home-info .title {
  font-size: 0.95rem;
  font-weight: 300;
  letter-spacing: 0.04em;
  color: #333;
}

.home-info .meta {
  font-size: 0.8rem;
  color: #767676;
  margin-top: 0.25rem;
  letter-spacing: 0.03em;
}

.home-nav {
  position: absolute;
  top: 40%;
  background: none;
  border: none;
  color: #767676;
  font-size: 2rem;
  cursor: pointer;
  padding: 0.5rem;
  transition: color 0.2s;
  user-select: none;
}

.home-nav:hover { color: #008555; }
.home-prev { left: 0.5rem; }
.home-next { right: 0.5rem; }

/* ---- Series Detail View ---- */
.series-view {
  display: none;
  height: calc(100vh - 160px);
  max-width: 1400px;
  margin: 0 auto;
  padding: 0 2rem;
  opacity: 0;
  transition: opacity 0.35s ease;
}

.series-view.visible { display: flex; gap: 1.5rem; }
.series-view.in { opacity: 1; }


nav .back-link {
  display: none;
  position: absolute;
  left: max(3rem, calc((100% - 1400px) / 2 + 3rem));
  color: #767676;
  text-decoration: none;
  font-size: 0.72rem;
  transition: color 0.2s;
}

nav .back-link:hover { color: #008555; }
nav .back-link.visible { display: inline; }

/* Main image area */
.main-image {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: calc(100vh - 280px);
}

.main-image img {
  max-width: 100%;
  max-height: calc(100vh - 280px);
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
  border: 2px solid #e0e0e0;
  transition: opacity var(--fade) ease;
}

.main-image img.swapping { opacity: 0; }

/* Mobile carousel (built in JS, shown only at <=600px; see media query) */
.carousel { display: none; }
/* Home thumbnail footer is mobile-only (desktop home stays a clean hero) */
#home-thumb-rail { display: none; }

.main-image-info {
  text-align: center;
  padding-top: 1rem;
}

.main-image-info .title {
  font-size: 0.95rem;
  font-weight: 300;
  letter-spacing: 0.04em;
  color: #333;
}

.main-image-info .meta {
  font-size: 0.8rem;
  color: #767676;
  margin-top: 0.25rem;
  letter-spacing: 0.03em;
}

.inquire-link {
  display: inline-block;
  margin-top: 0.5rem;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  color: #767676;
  text-decoration: none;
  transition: color 0.2s;
}

.inquire-link:hover { color: #008555; }

/* Thumbnail rail (right side, vertical) */
.thumb-rail {
  width: 80px;
  flex-shrink: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 4px 0;
  scrollbar-width: none;
}

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

.thumb-rail .thumb {
  width: 100%;
  aspect-ratio: 1;
  cursor: pointer;
  overflow: hidden;
  opacity: 0.4;
  transition: opacity 0.25s;
  flex-shrink: 0;
}

.thumb-rail .thumb:hover { opacity: 0.75; }
.thumb-rail .thumb.active { opacity: 1; }

.thumb-rail .thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---- About ---- */
.about {
  display: none;
  max-width: 900px;
  margin: 0 auto;
  padding: 2rem 2rem 4rem;
  line-height: 1.8;
  font-size: 0.95rem;
  color: #444;
}

.about.visible { display: block; }
.about p { max-width: 600px; margin-left: auto; margin-right: auto; }
.about p + p { margin-top: 1rem; }

.cv {
  margin-top: 2.5rem;
  padding-left: calc((100% - 600px) / 2);
}

.cv h3 {
  font-size: 0.82rem;
  font-weight: 400;
  letter-spacing: 0.12em;
  color: #333;
  margin-top: 2rem;
  margin-bottom: 0.5rem;
}

.cv ul {
  list-style: none;
  font-size: 0.88rem;
  color: #555;
  line-height: 1.8;
}

.cv .cv-year {
  display: inline-block;
  width: 4.5rem;
  flex-shrink: 0;
  color: #767676;
}

.cv li {
  display: flex;
}

.cv a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid #ddd;
  transition: border-color 0.2s;
}

.cv a:hover { border-color: #008555; }

/* ---- Blog ---- */
.blog {
  display: none;
  max-width: 600px;
  margin: 0 auto;
  padding: 2rem 2rem 4rem;
}

.blog.visible { display: block; }

.blog-posts { list-style: none; }
.blog-posts p { font-size: 0.95rem; color: #444; }

.blog-post {
  padding: 1.5rem 0;
  border-bottom: 1px solid #eee;
}

.blog-post:first-child { padding-top: 0; }

.blog-post .post-title {
  font-size: 0.95rem;
  font-weight: 300;
  letter-spacing: 0.04em;
  color: #333;
}

.blog-post .post-title a {
  color: inherit;
  text-decoration: none;
}

.blog-post .post-title a:hover { color: #008555; }

.blog-post .post-date {
  font-size: 0.8rem;
  color: #999;
  margin-top: 0.25rem;
  letter-spacing: 0.03em;
}

.blog-post .post-excerpt {
  font-size: 0.9rem;
  color: #666;
  margin-top: 0.5rem;
  line-height: 1.6;
}

/* ---- Contact ---- */
.contact {
  display: none;
  max-width: 480px;
  margin: 0 auto;
  padding: 2rem 2rem 4rem;
}

.contact.visible { display: block; }

.contact form { display: flex; flex-direction: column; gap: 1rem; }

.contact input,
.contact textarea,
.ml-row input {
  font-family: inherit;
  font-size: 0.9rem;
  padding: 0.7rem 0.8rem;
  border: 1px solid #ddd;
  background: #fafafa;
  color: #222;
  outline: none;
  transition: border-color 0.2s;
}

.contact input:focus,
.contact textarea:focus,
.ml-row input:focus { border-color: #999; }

.contact textarea { resize: vertical; min-height: 120px; }

.contact button,
.ml-row button {
  font-family: inherit;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  padding: 0.75rem 1.3rem;
  border: 1px solid #222;
  background: #222;
  color: #fff;
  cursor: pointer;
  transition: background 0.2s, color 0.2s;
}

.contact button { align-self: flex-start; }

.contact button:hover,
.ml-row button:hover { background: #fff; color: #222; }

.contact .or-email {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid #eee;
  font-size: 0.85rem;
  color: #767676;
}

.contact .email-link {
  display: inline-block;
  margin-top: 0.5rem;
  color: #222;
  text-decoration: none;
  border-bottom: 1px solid #ccc;
  padding-bottom: 1px;
  transition: border-color 0.2s;
}

.contact .email-link:hover { border-color: #222; }

.contact .form-sent {
  font-size: 0.9rem;
  color: #444;
  line-height: 1.6;
}

.mailing-list {
  margin-top: 2rem;
  padding-top: 2rem;
  border-top: 1px solid #eee;
}

.mailing-list p {
  font-size: 0.85rem;
  color: #767676;
  margin-bottom: 0.75rem;
}

.ml-row {
  display: flex;
  gap: 0.5rem;
}

.ml-row input { flex: 1; }

.ml-msg {
  font-size: 0.82rem;
  margin-top: 0.5rem;
  color: #767676;
}

.ml-msg.error { color: #c44; }
.ml-msg.success { color: #008555; }

/* ---- Footer ---- */
footer {
  text-align: center;
  padding: 3rem 2rem;
  font-size: 0.7rem;
  color: #767676;
  letter-spacing: 0.05em;
}

/* ---- Responsive ---- */
@media (max-width: 900px) {
  .series-grid { grid-template-columns: repeat(2, 1fr); }
  .thumb-rail { width: 60px; }
}

@media (max-width: 600px) {
  header { padding: 2rem 1rem 1.5rem; }
  header h1 { font-size: 1.1rem; }
  nav { gap: 0.6rem 1.2rem; font-size: 0.7rem; padding: 0 1rem 1.5rem; }
  .series-grid { grid-template-columns: 1fr; gap: 1rem; padding: 0 1.5rem; }
  .series-label span { font-size: 0.82rem; }

  /* The absolutely-positioned back link overlaps the centered nav on narrow
     screens; hide it on mobile (the "Work" nav link returns to the grid). */
  nav .back-link,
  nav .back-link.visible { display: none; }

  /* Home shows the same single work on mobile as on desktop, so the hero stays
     visible here — only the page padding tightens. */
  .home { padding: 0 1rem; }
  .home-slide .img-wrap { height: auto; }

  /* Home as a swipe carousel with a thumbnail footer — see the commented-out
     markup in index.html and slideshow in js/app.js. Uncomment all three to
     bring it back for an artist with more work to show.
  .home-nav { display: none; }
  .home-slide .img-wrap { display: none; }
  #home-thumb-rail { display: flex; margin-top: 1.5rem; } */

  .series-view.visible {
    flex-direction: column;
    height: auto;
    gap: 1rem;
    padding: 0 1rem;
  }
  /* Remove the desktop min-height so the image isn't framed by empty space. */
  .main-image { min-height: auto; }

  /* Swap the single crossfading image for a native scroll-snap carousel. */
  #main-img { display: none; }
  .carousel {
    display: flex;
    width: 100%;
    /* Fixed height, deliberately. Sizing the box to each artwork's own ratio
       does remove the empty space under a landscape, but it moves the layout
       mid-gesture: the height animates while the scroll is still settling, the
       snap loses its footing, and paging through a series stops feeling like
       one surface. A steady box that each painting slides into is worth more
       than a tight one that shifts under your thumb. */
    height: 60vh;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .carousel::-webkit-scrollbar { display: none; }
  .carousel .slide {
    flex: 0 0 100%;
    height: 100%;
    box-sizing: border-box;
    padding: 0 1rem;
    scroll-snap-align: center;
    scroll-snap-stop: always;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .carousel .slide img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border: 2px solid #e0e0e0;
    opacity: 1; /* override .home-slide img { opacity: 0 } from the crossfade */
  }
  .thumb-rail {
    width: 100%;
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
    gap: 3px;
    padding: 0;
  }
  .thumb-rail .thumb {
    width: 52px;
    aspect-ratio: 1;
  }
}

/* ---- Form states ---- */
button:disabled { opacity: 0.5; cursor: default; }

.form-error {
  font-size: 0.85rem;
  color: #c44;
  margin-top: 0.75rem;
  line-height: 1.6;
}

/* ---- Degraded state (app.js failed) ---- */
/* Every view starts hidden and only JS reveals it, so a broken app.js would
   otherwise render a blank page. boot.js sets .js-failed on <html> when app.js
   fails to load, parse, or finish its synchronous init; these rules fall back
   to the same reveal-everything state as css/noscript.css. The home hero has a
   real src in the HTML, and About/Contact are full content, so the degraded
   page is still usable. */
.js-failed .fade-view,
.js-failed .about,
.js-failed .contact {
  opacity: 1 !important;
  display: block !important;
}

.js-failed .home { display: block !important; }
/* The hero is a single static work now, so it paints without help. This stays
   as a belt-and-braces guard for whoever uncomments the crossfade slideshow. */
.js-failed .home-slide img { opacity: 1 !important; }
/* The empty JS-built grid has nothing to act on */
.js-failed .series-grid { display: none !important; }

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