/* ============================================================
   Chris Lianopoulos — site styles
   Built on the NOSTO design system tokens (_ds/.../styles.css).
   Plain CSS, no runtime. Light + dark themes.
   ============================================================ */

/* ---- Theme palettes ---- */
:root {
  --page: #ffffff;
  --ink: #000000;
  --muted: #6B6B6B;
  --muted-2: #9A9A9A;
  --line-strong: rgba(0, 0, 0, 0.40);
  --line-soft: rgba(0, 0, 0, 0.16);
  --media-bg: #E6E6E6;
}
/* Explicit dark choice */
:root[data-theme="dark"] {
  --page: #000000;
  --ink: #ffffff;
  --muted: #B4B4B4;
  --muted-2: #8A8A8A;
  --line-strong: rgba(255, 255, 255, 0.40);
  --line-soft: rgba(255, 255, 255, 0.16);
  --media-bg: #141414;
}
/* System dark, when the visitor hasn't explicitly chosen light */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --page: #000000;
    --ink: #ffffff;
    --muted: #B4B4B4;
    --muted-2: #8A8A8A;
    --line-strong: rgba(255, 255, 255, 0.40);
    --line-soft: rgba(255, 255, 255, 0.16);
    --media-bg: #141414;
  }
}

html, body { margin: 0; min-height: 100%; }
body {
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-sans, system-ui, sans-serif);
  transition: background 240ms var(--ease-out-soft, ease), color 240ms var(--ease-out-soft, ease);
}

/* ---- Page shell ---- */
.screen {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  padding: 0 56px;
  box-sizing: border-box;
}

/* Film grain — a whisper of texture for a cinematic, editorial feel */
body::after {
  content: "";
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  z-index: 2000;
  pointer-events: none;
  opacity: 0.04;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---- Header / masthead ---- */
.masthead {
  height: 64px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
}
.wordmark {
  font-family: var(--font-sans, system-ui, sans-serif);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
}
.nav { display: flex; align-items: center; gap: 32px; }
.nav a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 3px;
  transition: border-bottom-color var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.nav a:hover,
.nav a:focus-visible,
.nav a[aria-current="page"] { border-bottom-color: var(--ink); }

/* Theme toggle — flat monochrome sliding switch, NOSTO style
   (hairline pill, solid square-capped icons, no shadow) */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  color: var(--ink);
  flex: none;
  line-height: 0;
  vertical-align: middle;
  /* match the nav links' underline space (3px padding + 1px border)
     so the switch centres on the same optical axis as PROFILE / CONTACT */
  padding-bottom: 4px;
}
.theme-toggle svg {
  width: 13px;
  height: 13px;
  display: block;
  stroke: currentColor;
  fill: none;
  stroke-width: 1.75;
  stroke-linecap: square;
  stroke-linejoin: miter;
  opacity: 0.3;
  transition: opacity var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.theme-toggle .tsw-track {
  position: relative;
  width: 34px;
  height: 18px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  box-sizing: border-box;
  flex: none;
}
.theme-toggle .tsw-knob {
  position: absolute;
  top: 50%;
  left: 2px;
  width: 12px;
  height: 12px;
  transform: translateY(-50%);
  border-radius: 50%;
  background: var(--ink);
  transition: left var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
:root[data-theme="dark"] .theme-toggle .tsw-knob { left: calc(100% - 14px); }
:root[data-theme="dark"] .theme-toggle .tsw-moon { opacity: 1; }
:root:not([data-theme="dark"]) .theme-toggle .tsw-sun { opacity: 1; }
.theme-toggle:focus-visible { outline: 1px solid var(--ink); outline-offset: 3px; }

/* ---- Category sub-bar (title + back) ---- */
.subbar {
  height: 64px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
}
.subbar .back {
  color: #D0021B; /* source-specified accent on the Back link */
  border-bottom: 1px solid transparent;
  padding-bottom: 3px;
  transition: border-bottom-color var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.subbar .back:hover,
.subbar .back:focus-visible { border-bottom-color: #D0021B; }

/* ---- Media grids ---- */
.grid {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  padding-bottom: 8px;
}
.grid--center { align-items: center; gap: 22px; }   /* landing: centred single row, with air */
.grid--top { align-content: start; }     /* category pages: rows from top */

/* Category listings fill the viewport like the landing hero. The listings carry no
   subbar, so the media band starts right under the masthead — same skeleton as the
   landing (masthead + band + footer), for a seamless page-to-page transition.
   Band height = 100vh − masthead(64) − footer(72) − grid pad(8) = 144.
   Desktop only; ≤900px keeps the natural, scrolling grid in the responsive block. */
@media (min-width: 901px) {
  /* COVER RATIO RULE: every category cover (Architecture, Commercial, Hospitality)
     is one full-height band tile — identical crop/ratio on every listing, and it
     fills the viewport like the landing hero. Do not special-case per page.
     A listing with more projects than fit on screen simply scrolls; a scroll hint
     (see .scroll-cue) tells the viewer there is more, so covers never get shortened. */
  .grid--top:not(.gallery) { grid-auto-rows: calc(100vh - 144px); }
  .grid--top:not(.gallery) .tile {
    aspect-ratio: auto;
    height: 100%;
  }
}

/* ---- Hero landing — one large image, crossfading across an editorial index ---- */
.hero {
  flex: 1;
  /* Height floor: on browsers that don't resolve `flex:1` height for a child
     with absolutely-positioned contents (some smart-TV engines), this keeps the
     hero — and its images — from collapsing. Modern browsers fill more via flex,
     so this is only a fallback and changes nothing there. */
  min-height: calc(100vh - 150px);
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  overflow: hidden;
  background: var(--media-bg);
  margin-bottom: 8px;
}
.hero-stage { position: absolute; top: 0; right: 0; bottom: 0; left: 0; }
.hero-img {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  opacity: 0;
  transition: opacity 900ms var(--ease-out-soft, ease);
}
.hero-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-img.is-on { opacity: 1; }
.hero::before {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(to top, rgba(0,0,0,0.62) 0%, rgba(0,0,0,0.12) 34%, rgba(0,0,0,0) 62%);
}
.hero-index {
  position: relative;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 40px;
  padding: 32px;
}
.hero-index a {
  color: #fff;
  display: inline-flex;
  align-items: baseline;
  gap: 11px;
  cursor: pointer;
  font-family: var(--font-sans, system-ui, sans-serif);
  font-weight: 700;
  font-size: clamp(1.3rem, 2.2vw, 2rem);
  letter-spacing: -0.01em;
  opacity: 0.82;
  transition: opacity var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.hero-index a .idx {
  font-family: var(--font-mono, monospace);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.16em;
  opacity: 0.85;
}
/* Make it read as a button: the label carries a persistent underline, and an arrow
   sits after it. Both strengthen on hover/focus so it's clearly interactive. */
.hero-index a .lbl {
  border-bottom: 1.5px solid rgba(255, 255, 255, 0.5);
  padding-bottom: 6px;
  transition: border-bottom-color var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.hero-index a .lbl::after {
  content: "\2192";                 /* → arrow */
  display: inline-block;
  margin-left: 12px;
  font-weight: 400;
  transition: transform var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.hero-index a:focus-visible,
.hero-index a.active { opacity: 1; }
.hero-index a:focus-visible .lbl { border-bottom-color: #fff; }
.hero-index a:focus-visible .lbl::after { transform: translateX(5px); }
/* Hover effects only on devices that truly hover — otherwise a phone's first
   tap triggers :hover and the visitor has to tap twice to open the category. */
@media (hover: hover) {
  .hero-index a:hover { opacity: 1; }
  .hero-index a:hover .lbl { border-bottom-color: #fff; }
  .hero-index a:hover .lbl::after { transform: translateX(5px); }
}
/* On touch, links read as fully tappable (no dim, arrow already visible). */
@media (hover: none) {
  .hero-index a { opacity: 1; }
}
@media (max-width: 560px) {
  .hero-index { gap: 22px; padding: 22px; }
}

/* ---- Tile (image or placeholder + hover label) ---- */
.tile {
  position: relative;
  display: block;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--media-bg);
  text-decoration: none;
}
.grid--center .tile { max-height: 72vh; }
.tile img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Explicit base transform + GPU layer up front: without these the very first
     hover has to promote the image to its own compositor layer mid-transition,
     which snaps ("jumps in") instead of animating. A reload only masks it by
     warming the cache. Priming the layer here makes the first hover smooth too. */
  transform: translateZ(0);
  backface-visibility: hidden;
  will-change: transform;
  transition: transform var(--dur-cinematic, 1200ms) var(--ease-out-soft, ease);
}
.tile:hover img,
.tile:focus-visible img { transform: scale(1.035) translateZ(0); }

/* Empty placeholder well (category pages have no photos yet) */
.tile .ph {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  text-align: center;
  color: var(--muted-2);
  background: var(--media-bg);
}

/* Hover scrim + label (dark scrim over photography, both themes) */
.tile .overlay {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  display: flex;
  align-items: flex-end;
  padding: 24px;
  opacity: 0;
  background: rgba(0, 0, 0, 0);
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  transition:
    opacity var(--dur-fast, 240ms) var(--ease-out-soft, ease),
    background var(--dur-fast, 240ms) var(--ease-out-soft, ease),
    backdrop-filter var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.tile:hover .overlay,
.tile:focus-visible .overlay {
  opacity: 1;
  background: rgba(0, 0, 0, 0.32);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.tile .overlay h2 { margin: 0; color: #fff; font-size: clamp(1.6rem, 2.4vw, 2.1rem); letter-spacing: -0.01em; }

/* ---- Two-column content (Profile / Contact) ---- */
.two-col {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.8fr);
  gap: 56px;
  padding: 28px 0 36px;
}
.two-col--center { align-items: center; }
.two-col--start { align-content: center; align-items: start; }

.prose { display: flex; flex-direction: column; gap: 18px; max-width: 82ch; }
.prose .muted { color: var(--muted); }
/* editorial lede — the opening statement reads larger and more confident */
.prose .nosto-h3 { font-size: clamp(1.6rem, 2.7vw, 2.15rem); line-height: 1.22; letter-spacing: -0.012em; }

/* Portrait (Profile) */
.portrait-wrap { width: 100%; display: flex; justify-content: flex-end; align-items: center; }
.portrait {
  width: 100%;
  max-width: 440px;
  aspect-ratio: 4 / 5;
  /* Never let the portrait push the page past one screen: cap it to the real
     available height (viewport minus header/footer/padding ≈ 200px, + margin).
     On tall screens it stays 440px wide; on shorter ones it shrinks to fit so
     the footer is always visible without scrolling. */
  max-height: calc(100vh - 230px);
  background: var(--media-bg);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted-2);
  text-align: center;
  padding: 16px;
  box-sizing: border-box;
  overflow: hidden;
}
.portrait:has(img) { padding: 0; }
.portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ---- Contact ---- */
.contact-left { max-width: 52ch; }
.contact-links { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; margin-top: 8px; }
.contact-links a {
  color: var(--ink);
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 6px;
  transition: color var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.contact-links a.muted { color: var(--muted); }
/* Social icon links (Instagram, WhatsApp) — no underline, muted glyph that inks up on hover */
.contact-links a.contact-ig,
.contact-links a.contact-wa {
  border-bottom: 0;
  padding-bottom: 0;
  line-height: 0;
  color: var(--muted);
  margin-top: 2px;
}
.contact-links a.contact-ig:hover,
.contact-links a.contact-ig:focus-visible,
.contact-links a.contact-wa:hover,
.contact-links a.contact-wa:focus-visible { color: var(--ink); }

.contact-form { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 480px; justify-self: end; }
.field-group { border: 1px solid var(--line-strong); }
.field-group input,
.field-group textarea {
  font-family: var(--font-sans, system-ui, sans-serif);
  font-weight: 300;
  font-size: 17px;
  color: var(--ink);
  background: transparent;
  border: 0;
  outline: none;
  width: 100%;
  box-sizing: border-box;
  padding: 18px 16px;
  resize: vertical;
}
.field-group input::placeholder,
.field-group textarea::placeholder { color: var(--muted-2); }
.field-group input:focus,
.field-group textarea:focus { outline: 1px solid var(--ink); outline-offset: -1px; }
.field-rule { height: 1px; background: var(--line-soft); }

/* Category select — matches the text inputs, with a custom caret */
.field-select {
  font-family: var(--font-sans, system-ui, sans-serif);
  font-weight: 300;
  font-size: 17px;
  color: var(--ink);
  background: transparent;
  border: 0;
  outline: none;
  width: 100%;
  box-sizing: border-box;
  padding: 18px 44px 18px 16px;
  cursor: pointer;
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23888' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
}
.field-select:focus { outline: 1px solid var(--ink); outline-offset: -1px; }
/* muted "placeholder" look while the empty option is selected */
.field-select:has(option[value=""]:checked) { color: var(--muted-2); }
.field-select option { color: var(--ink); background: var(--page); }

/* Solid button (NOSTO Button, solid variant) — inverts with theme */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 50px;
  padding: 0 24px;
  background: var(--ink);
  color: var(--page);
  font-family: var(--font-mono, monospace);
  font-weight: 400;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 1px solid var(--ink);
  cursor: pointer;
  align-self: flex-start;
  transition: opacity var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.btn:hover { opacity: 0.82; }
.btn:active { opacity: 0.7; }

/* ---- Contact nudge (CRO) — a quiet line above the footer on project pages ---- */
.cta {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 14px;
  padding: 30px 0 8px;
  margin-top: 8px;
  border-top: 1px solid var(--line-soft);
  text-align: center;
}
.cta span { color: var(--muted); }
.cta a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--ink);
  padding-bottom: 4px;
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transition: opacity var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.cta a .arr { display: inline-block; margin-left: 8px; transition: transform var(--dur-fast, 240ms) var(--ease-out-soft, ease); }
.cta a:hover, .cta a:focus-visible { opacity: 0.72; }
.cta a:hover .arr, .cta a:focus-visible .arr { transform: translateX(4px); }

/* ---- Prev / next navigation between projects in a category ---- */
.project-nav {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 20px 0;
  margin-top: 8px;
  border-top: 1px solid var(--line-soft);
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
/* When the nav sits directly above the CTA, drop the CTA's top gap so the nav
   text stays centered between its own top hairline and the CTA's hairline. */
.project-nav + .cta { margin-top: 0; }
.project-nav a {
  color: var(--ink);
  text-decoration: none;
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  transition: opacity var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.project-nav a:hover, .project-nav a:focus-visible { opacity: 0.72; }
.project-nav .pn-all { color: var(--muted); }
.project-nav .pn-next { text-align: right; }
.project-nav .pn-arr { display: inline-block; }
.project-nav .pn-prev:hover .pn-arr, .project-nav .pn-prev:focus-visible .pn-arr { transform: translateX(-3px); }
.project-nav .pn-next:hover .pn-arr, .project-nav .pn-next:focus-visible .pn-arr { transform: translateX(3px); }
.project-nav .pn-arr { transition: transform var(--dur-fast, 240ms) var(--ease-out-soft, ease); }
@media (max-width: 560px) {
  .project-nav .pn-name { display: none; }
}

/* ---- Selected clients — auto-scrolling logo marquee (Profile, left column) ---- */
.clients { margin-top: 16px; }
.clients-label {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin: 0 0 14px;
}
.logo-marquee {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
}
.logo-track {
  display: flex;
  align-items: center;
  width: max-content;
  animation: logo-marquee 32s linear infinite;
}
.logo-track img {
  width: auto;
  margin: 0 22px;
  opacity: 0.8;
  -webkit-user-drag: none;
  user-select: none;
}
@keyframes logo-marquee { to { transform: translateX(-50%); } }
/* Mobile: the strip spans the full width and reads slow, so run it noticeably faster. */
@media (max-width: 900px) { .logo-track { animation-duration: 10s; } }
@media (prefers-reduced-motion: reduce) {
  .logo-marquee { -webkit-mask-image: none; mask-image: none; }
  .logo-track { animation: none; flex-wrap: wrap; width: auto; row-gap: 16px; }
  .logo-track img.dup { display: none; }
}

/* ---- Footer ---- */
.footer {
  flex: none;
  height: 72px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.footer span { color: var(--muted-2); }

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 900px) {
  .screen { padding: 0 24px; }

  .grid {
    flex: none;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
    padding: 8px 0 24px;
  }
  .grid--center .tile { max-height: none; }
  /* Where hover isn't available, show the label */
  .tile .overlay {
    opacity: 1;
    background: rgba(0, 0, 0, 0.28);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  .two-col {
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
    padding: 16px 0 32px;
  }
  .portrait-wrap { justify-content: flex-start; }
  .portrait { max-width: 100%; height: auto; aspect-ratio: 4 / 5; }
  .contact-form { justify-self: stretch; max-width: 100%; }
}

@media (max-width: 560px) {
  .screen { padding: 0 20px; }

  /* header fits on one row without overflow */
  .masthead { gap: 12px; }
  .wordmark { font-size: 13px; }
  .nav { gap: 16px; }
  .nav a { font-size: 11px; letter-spacing: 0.08em; }
  .theme-toggle { gap: 6px; }
  .theme-toggle .tsw-track { width: 30px; height: 16px; }
  .theme-toggle .tsw-knob { width: 12px; height: 12px; }
  :root[data-theme="dark"] .theme-toggle .tsw-knob { left: calc(100% - 14px); }
  .theme-toggle svg { width: 12px; height: 12px; }

  /* landing / category: 1-up hero tiles; project galleries: 2-up for browsing */
  .grid { grid-template-columns: minmax(0, 1fr); }
  .grid.gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }

  .subbar { gap: 12px; }
  .subbar span, .subbar .back { font-size: 11px; letter-spacing: 0.08em; }
}

@media (max-width: 380px) {
  .screen { padding: 0 16px; }
  .wordmark { font-size: 12px; }
  .nav { gap: 13px; }
}

@media (prefers-reduced-motion: reduce) {
  .tile img, .tile .overlay, .nav a, .btn, .subbar .back, .contact-links a, body {
    transition-duration: 1ms;
  }
  .tile:hover img, .tile:focus-visible img { transform: none; }
}

/* ============================================================
   Lightbox (image popup) — dark veil, both themes
   ============================================================ */
.lightbox[hidden] { display: none !important; }
.lightbox {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.92);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 64px 24px;
  box-sizing: border-box;
}
.lightbox .lb-img {
  max-width: min(92vw, 1600px);
  max-height: 85vh;
  object-fit: contain;
  display: block;
}
.lightbox button {
  position: absolute;
  background: none;
  border: 0;
  color: #fff;
  cursor: pointer;
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 10px;
  transition: color 120ms var(--ease-out-soft, ease);
}
.lightbox button:hover { color: #B4B4B4; }
.lightbox button:focus-visible { color: #fff; outline: 2px solid #fff; outline-offset: 2px; }
.lightbox .lb-close { top: 22px; right: 30px; }
.lightbox .lb-prev  { left: 20px;  top: 50%; transform: translateY(-50%); }
.lightbox .lb-next  { right: 20px; top: 50%; transform: translateY(-50%); }
.lightbox .lb-count {
  position: absolute;
  bottom: 24px;
  left: 0;
  right: 0;
  text-align: center;
  color: #8A8A8A;
}
@media (max-width: 560px) {
  .lightbox { padding: 56px 12px; }
  .lightbox .lb-prev { left: 8px; }
  .lightbox .lb-next { right: 8px; }
}

/* ============================================================
   Project galleries — respect each photo's real aspect ratio.
   Landscape frames span two columns; nothing is cropped
   (the tile's aspect-ratio is set per-photo, inline).
   ============================================================ */
.grid.gallery {
  gap: 0;                 /* flush, same as the listing grids — no space between frames */
  grid-auto-flow: row dense;
  align-items: start;
}
.grid.gallery .tile { aspect-ratio: auto; max-height: none; }
.grid.gallery .tile--wide { grid-column: span 2; }
.tile picture, .portrait picture { display: block; width: 100%; height: 100%; }
.tile video, .tile .cover-video { display: block; width: 100%; height: 100%; object-fit: cover; background: #000; }
.tile--video { background: #000; }

/* Editorial frame numbers on gallery photos (01, 02, …) */
.grid.gallery { counter-reset: frame; }
.grid.gallery .tile { counter-increment: frame; }
.grid.gallery .tile::after {
  content: counter(frame, decimal-leading-zero);
  position: absolute;
  left: 12px;
  bottom: 10px;
  z-index: 2;
  font-family: var(--font-mono, monospace);
  font-size: 11px;
  letter-spacing: 0.16em;
  color: #fff;
  opacity: 0.5;
  text-shadow: 0 1px 10px rgba(0, 0, 0, 0.55);
  transition: opacity var(--dur-fast, 240ms) var(--ease-out-soft, ease);
  pointer-events: none;
}
.grid.gallery .tile:hover::after,
.grid.gallery .tile:focus-visible::after { opacity: 0.92; }

/* Editorial numbering on category listings (01, 02, …) — matches the landing index.
   Labels are shown persistently with a mono number, over a light bottom gradient. */
.grid--top:not(.gallery) { counter-reset: item; }
.grid--top:not(.gallery) .tile { counter-increment: item; }
.grid--top:not(.gallery) .tile .overlay {
  opacity: 1;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.58) 0%, rgba(0, 0, 0, 0) 46%);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.grid--top:not(.gallery) .tile .overlay h2 {
  display: flex;
  gap: 11px;
  align-items: baseline;        /* fallback: single-line titles */
  align-items: last baseline;   /* multi-line: sit on the LAST line, like the landing */
}
.grid--top:not(.gallery) .tile .overlay h2::before {
  content: counter(item, decimal-leading-zero);
  font-family: var(--font-mono, monospace);
  font-weight: 400;
  font-size: 11px;
  letter-spacing: 0.16em;
  opacity: 0.85;
}
/* Project titles read as buttons — same underline + arrow as the landing index. */
.grid--top:not(.gallery) .tile .overlay h2 .lbl {
  border-bottom: 1.5px solid rgba(255, 255, 255, 0.5);
  padding-bottom: 6px;
  transition: border-bottom-color var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.grid--top:not(.gallery) .tile .overlay h2 .lbl::after {
  content: "\2192";                 /* → arrow */
  display: inline-block;
  margin-left: 12px;
  font-weight: 400;
  transition: transform var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
.grid--top:not(.gallery) .tile:hover .overlay h2 .lbl,
.grid--top:not(.gallery) .tile:focus-visible .overlay h2 .lbl { border-bottom-color: #fff; }
.grid--top:not(.gallery) .tile:hover .overlay h2 .lbl::after,
.grid--top:not(.gallery) .tile:focus-visible .overlay h2 .lbl::after { transform: translateX(5px); }

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

.form-status { color: var(--muted); margin: 0; }

/* Skip-to-content link — off-screen until a keyboard user tabs to it. */
.skip-link {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 2000;
  background: #111;
  color: #fff;
  padding: 10px 16px;
  font-family: var(--font-mono, monospace);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-decoration: none;
  transform: translateY(-200%);
  transition: transform 0.18s ease;
}
.skip-link:focus { transform: translateY(0); outline: 2px solid #fff; outline-offset: 2px; }

/* Focus-ring safety net: every link/button gets a clear focus outline unless a
   more specific rule already provides its own indicator. */
a:focus-visible,
button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* Floating WhatsApp button — mobile only; sits beneath the lightbox (z 1000).
   The icon is a filled disc; a dashed outline ring orbits it with a small gap,
   its "cutouts" reading as strokes (matches the mono/hairline design system). */
.fab-wa {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 900;
  display: none;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--page);
  border: 1px solid var(--ink);
  transition: opacity var(--dur-fast, 240ms) var(--ease-out-soft, ease),
              transform var(--dur-fast, 240ms) var(--ease-out-soft, ease);
}
/* Orbiting dashed ring, set off from the icon by a small gap. */
.fab-wa::before {
  content: "";
  position: absolute;
  top: -7px; right: -7px; bottom: -7px; left: -7px;
  border-radius: 50%;
  border: 1.5px dashed var(--ink);
  opacity: 0.55;
  animation: fab-wa-spin 9s linear infinite;
  will-change: transform;
  pointer-events: none;
}
@keyframes fab-wa-spin { to { transform: rotate(360deg); } }
.fab-wa:hover, .fab-wa:focus-visible { opacity: 0.85; transform: scale(1.04); }
.fab-wa:hover::before, .fab-wa:focus-visible::before { opacity: 0.85; }
.fab-wa svg { width: 26px; height: 26px; display: block; }
@media (max-width: 900px) { .fab-wa { display: flex; } }
@media (prefers-reduced-motion: reduce) {
  .fab-wa::before { animation: none; }
  .fab-wa:hover, .fab-wa:focus-visible { transform: none; }
}

/* Comfortable tap targets on touch screens */
@media (max-width: 900px) {
  .nav a { padding-top: 8px; padding-bottom: 8px; }
  .theme-toggle { padding-top: 8px; padding-bottom: 8px; }
  .contact-links a { padding: 6px 0; }
  .project-nav a, .cta a { padding-top: 8px; padding-bottom: 8px; }
}

/* Screen-reader / SEO only — visually hidden but present for crawlers and a11y. */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ============================================================
   Reveal on scroll — the NOSTO word-/row fade-up.
   Hidden only when JS is active (html.js), so no-JS stays visible.
   ============================================================ */
html.js .grid > *,
html.js .prose > *,
html.js .portrait-wrap,
html.js .contact-form {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.8s var(--ease-out-soft, cubic-bezier(.16,1,.3,1)),
              transform 0.8s var(--ease-out-soft, cubic-bezier(.16,1,.3,1));
}
html.js .grid > *.is-in,
html.js .prose > *.is-in,
html.js .portrait-wrap.is-in,
html.js .contact-form.is-in {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  html.js .grid > *,
  html.js .prose > *,
  html.js .portrait-wrap,
  html.js .contact-form { opacity: 1; transform: none; transition: none; }
}
/* Each gallery photo fades in as it finishes decoding, so images never pop in
   (only applied by JS to images that are not yet loaded; cached/no-JS stay visible). */
html.js .grid img.img-loading { opacity: 0; }
html.js .grid img.img-loaded { opacity: 1; transition: opacity 0.6s var(--ease-out-soft, ease); }

/* ============================================================
   404
   ============================================================ */
.notfound {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 18px;
  padding: 24px 0;
}
.notfound .code {
  font-family: var(--font-display, sans-serif);
  font-weight: 800;
  font-size: clamp(4rem, 16vw, 12rem);
  line-height: 0.82;
  letter-spacing: -0.03em;
}
.notfound a.home {
  color: var(--ink);
  border-bottom: 1px solid var(--ink);
  padding-bottom: 3px;
}
