/* =========================================================================
   Noa — Photography Portfolio
   Design system + all styles. Light, minimal, photo-first. No build step.
   ========================================================================= */

/* ---- Design tokens ---------------------------------------------------- */
:root {
  /* Brand colors (overridable at runtime from manifest.site.theme).
     primary  drives links, highlights, primary buttons
     secondary drives hover/secondary accents
     accent   drives small details (dots, focus) */
  --brand-primary:   #a8734f;
  --brand-secondary: #7c6a58;
  --brand-accent:    #c89b6a;
  --accent: var(--brand-primary);

  /* Palette (near-white canvas, ink text) */
  --bg:          #faf9f7;
  --bg-2:        #f2f0ec;   /* elevated surface / hover wash */
  --ink:         #14130f;   /* primary text */
  --ink-2:       #4a463f;   /* secondary text */
  --ink-3:       #716c63;   /* muted text / meta — ≥4.5:1 on every surface (WCAG AA) */
  --line:        #e3e0d9;   /* hairline borders */
  --white:       #ffffff;
  --surface:     #ffffff;   /* inputs, menus, raised panels */
  --photo-bg:    #ebe8e2;   /* behind a photo while it loads */
  --danger:      #b23a2e;
  --ok:          #2f7d4f;
  --shadow:      0 18px 60px -24px rgba(20, 19, 15, 0.28);

  /* Type — `--serif` is the heading/display font (overridable via manifest.site.fonts). */
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --serif: "Jost", "Inter", sans-serif;

  /* Layout (photo-forward: tight side padding, minimal image gaps) */
  --container: 1720px;
  --gutter: clamp(12px, 2.4vw, 32px);
  --gap: 4px;         /* hairline space between images */
  --space-section: clamp(56px, 9vh, 120px);
  --radius: 0px;      /* photo-forward: square corners, edge to edge */
  --nav-h: 60px;      /* the nav bar's exact height; content below it offsets by this */

  /* Motion */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur: 0.7s;
  --dur-drawer: 0.32s; /* drawers + side panels slide (with --ease), never snap */
}

/* ---- Themes -------------------------------------------------------------
   Light ("Studio") is the default everywhere. A page opts into following the
   device's light/dark setting with `class="theme-auto"` on <html> (the admin
   does; the public site stays light, the photos' backdrop). `data-theme=
   "light" | "dark"` on <html> forces one. Dark = "Darkroom": neutral greys so
   photos carry the color. Components use the tokens, so they follow along. */
@media (prefers-color-scheme: dark) {
  :root.theme-auto:not([data-theme="light"]) {
    --bg: #151515; --bg-2: #262626; --surface: #1c1c1c; --photo-bg: #232323;
    --ink: #ececea; --ink-2: #bdbdba; --ink-3: #94948f; --line: #2e2e2c;
    --danger: #e0786c; --ok: #6cc28f;
    --shadow: 0 18px 50px -20px rgba(0, 0, 0, 0.7);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #151515; --bg-2: #262626; --surface: #1c1c1c; --photo-bg: #232323;
  --ink: #ececea; --ink-2: #bdbdba; --ink-3: #94948f; --line: #2e2e2c;
  --danger: #e0786c; --ok: #6cc28f;
  --shadow: 0 18px 50px -20px rgba(0, 0, 0, 0.7);
  color-scheme: dark;
}

/* ---- Reset / base ----------------------------------------------------- */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}
img { max-width: 100%; display: block; }
[hidden] { display: none !important; }
/* Keyboard focus ring for everything interactive (mouse clicks don't show it).
   Form fields use their own ring (see "Form fields"). */
:is(a, button, [role="button"], [tabindex]):focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.photo:focus-visible { outline-offset: -3px; } /* inside the edge-to-edge tile */
.lightbox :is(button, a):focus-visible { outline-color: #fff; } /* `hidden` always wins over component display rules */
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; }
h1, h2, h3, h4 { margin: 0; font-weight: 400; line-height: 1.08; }
p { margin: 0; }

/* Utility: a wide content column */
.wrap {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Full-bleed band for image grids — spans the whole viewport, edge to edge. */
.bleed { width: 100%; padding-inline: 0; }

/* Serif display + elegant italic accent */
.display {
  font-family: var(--serif);
  font-optical-sizing: auto;
  letter-spacing: -0.01em;
}
.display em { font-style: italic; color: var(--accent); } /* kept: accent-color italic for headings (theme color hook) */

/* Eyebrow label — spaced small caps. Not used on a page yet; kept as a
   design-system utility for future templates/tenants. */
.eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 500;
}

/* ---- Site nav --------------------------------------------------------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* One fixed height in every state, so the cover and page headers can offset
     by exactly --nav-h (and nothing shifts when the bar turns solid). */
  height: var(--nav-h);
  padding: 0 var(--gutter);
  mix-blend-mode: difference;   /* stays legible over any photo */
  color: #fff;
  transition: transform 0.45s var(--ease), background 0.4s var(--ease);
  will-change: transform;
}
.nav a { color: inherit; }
/* Hidden off the top of the screen (scroll-down / resting state mid-page). */
.nav.hidden { transform: translateY(-102%); }
/* Revealed mid-page on a deliberate upward scroll: solid, legible chrome. */
.nav.solid {
  mix-blend-mode: normal;
  color: var(--ink);
  background: rgba(250, 249, 247, 0.9);
  backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: 1px solid var(--line);
}
.wordmark {
  font-family: var(--serif);
  font-size: 1.15rem;
  letter-spacing: 0.02em;
  font-weight: 500;
}
.wordmark-img { height: 26px; width: auto; display: block; }

/* When a logo image is used, drop the difference blend and keep everything
   legible with shadows over photos (removed once the solid bar takes over). */
.nav.has-logo { mix-blend-mode: normal; }
.nav.has-logo:not(.solid) { color: #fff; }
.nav.has-logo:not(.solid) .nav-links a { text-shadow: 0 1px 8px rgba(0,0,0,0.55); }
.nav.has-logo:not(.solid) .nav-toggle span { box-shadow: 0 1px 6px rgba(0,0,0,0.4); }

/* Logo variant switching: light logo over the transparent hero, default on the
   solid bar. With only one logo, the default shows everywhere (with a shadow). */
.wordmark-img--light { display: none; }
.nav.has-logo.has-logo-light:not(.solid) .wordmark-img--default { display: none; }
.nav.has-logo.has-logo-light:not(.solid) .wordmark-img--light { display: block; }
/* Single-logo fallback: shadow it over photos for legibility. */
.nav.has-logo:not(.has-logo-light):not(.solid) .wordmark-img--default {
  filter: drop-shadow(0 1px 8px rgba(0,0,0,0.55));
}
.nav-links {
  display: flex;
  gap: clamp(18px, 3vw, 40px);
  align-items: center;
  font-size: 0.82rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.nav-links a { position: relative; padding-block: 4px; }
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--ease);
}
.nav-links a:hover::after,
.nav-links a[aria-current="page"]::after { transform: scaleX(1); }

/* Hamburger toggle — hidden on desktop, shown on mobile */
.nav-toggle {
  display: none;
  width: 26px; height: 18px; position: relative;
  background: none; border: none; padding: 0; cursor: pointer;
}
.nav-toggle span {
  position: absolute; left: 0; width: 100%; height: 2px;
  background: currentColor; transition: transform 0.35s var(--ease), opacity 0.2s var(--ease);
}
.nav-toggle span:nth-child(1) { top: 0; }
.nav-toggle span:nth-child(2) { top: 8px; }
.nav-toggle span:nth-child(3) { top: 16px; }
body.menu-open .nav-toggle span:nth-child(1) { transform: translateY(8px) rotate(45deg); }
body.menu-open .nav-toggle span:nth-child(2) { opacity: 0; }
body.menu-open .nav-toggle span:nth-child(3) { transform: translateY(-8px) rotate(-45deg); }

/* Full-screen mobile menu */
.mobile-menu {
  position: fixed; inset: 0; z-index: 45;
  background: var(--bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px;
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease), visibility 0.35s;
}
.mobile-menu.open { opacity: 1; visibility: visible; transform: none; }
.mobile-menu a {
  font-family: var(--serif); font-size: 2rem; font-weight: 300; color: var(--ink);
  padding: 10px 0;
}

@media (max-width: 700px) {
  .nav-links { display: none; }
  .nav-toggle { display: block; }
}

/* ---- UI components (design system) ------------------------------------
   Shared by the admin and available to any page. Token-based, so they work in
   both themes. Icons come from icons.js (`icon("name")` → <svg class="i">). */
svg.i {
  width: 18px; height: 18px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round;
}

/* Buttons: .btn (outlined), .primary (filled, one per view), .quiet (no
   border), .danger (destructive, quiet). Icons go first: <svg class="i">. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 34px; padding: 0 14px; border: 1px solid var(--line); border-radius: 4px;
  background: var(--surface); color: var(--ink); font: inherit; font-size: 0.875rem;
  white-space: nowrap; cursor: pointer; transition: border-color 0.15s, background 0.15s, opacity 0.15s;
}
.btn:hover:not(:disabled) { border-color: var(--ink-3); }
.btn .i { width: 16px; height: 16px; }
.btn.primary { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.btn.primary:hover:not(:disabled) { opacity: 0.86; }
.btn.quiet { background: none; border-color: transparent; color: var(--ink-2); }
.btn.quiet:hover:not(:disabled) { background: var(--bg-2); color: var(--ink); }
.btn.danger { background: none; border-color: transparent; color: var(--danger); }
.btn.danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 10%, transparent); }
.btn.small { height: 28px; padding: 0 10px; font-size: 0.8125rem; }
.btn:disabled { opacity: 0.4; cursor: default; }
/* "There's something to publish": a soft accent ring on the primary button. */
.btn.ready { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent); }

/* Call to action on public pages (e.g. "Back to home"): an outlined link in
   spaced capitals, icon after the words; fills with ink on hover. */
.cta {
  display: inline-flex; align-items: center; gap: 1.75em;
  padding: 1.15em 2.5em; border: 1px solid var(--ink); color: var(--ink);
  font-size: 0.8125rem; font-weight: 500; letter-spacing: 0.24em; text-transform: uppercase; line-height: 1;
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}
.cta:hover { background-color: var(--ink); color: var(--bg); }
.cta .i { width: 20px; height: 20px; stroke-width: 1.5; }

/* Icon-only button (always give it a title / aria-label). */
.icon-btn {
  display: inline-grid; place-items: center; width: 30px; height: 30px; padding: 0;
  border: 0; border-radius: 4px; background: none; color: var(--ink-2); cursor: pointer;
}
.icon-btn:hover:not(:disabled) { background: var(--bg-2); color: var(--ink); }
.icon-btn:disabled { opacity: 0.3; cursor: default; }
.icon-btn .i { width: 17px; height: 17px; }

/* Switch: a real checkbox, styled. <input type="checkbox" class="switch" role="switch"> */
input[type="checkbox"].switch {
  -webkit-appearance: none; appearance: none; position: relative; flex: none;
  width: 34px; height: 20px; margin: 0; border-radius: 99px; background: var(--line);
  cursor: pointer; transition: background 0.15s;
}
input[type="checkbox"].switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); transition: transform 0.15s;
}
input[type="checkbox"].switch:checked { background: var(--accent); }
input[type="checkbox"].switch:checked::after { transform: translateX(14px); }
input[type="checkbox"].switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Segmented control: real radios, one visible choice.
   <div class="seg" role="radiogroup"><label><input type="radio" name="x" value="a"><span>A</span></label>…</div> */
.seg { display: inline-flex; flex-wrap: wrap; padding: 2px; gap: 2px; border-radius: 5px; background: var(--bg-2); }
.seg label { position: relative; margin: 0; cursor: pointer; }
.seg input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.seg span {
  display: block; padding: 5px 12px; border-radius: 4px; color: var(--ink-3);
  font-size: 0.8125rem; line-height: 1.5; white-space: nowrap; transition: background 0.15s, color 0.15s;
}
.seg label:hover span { color: var(--ink); }
.seg input:checked + span { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14); }
.seg input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Menu (dropdown panel): .menu > a | button, .menu-label, hr */
.menu {
  min-width: 200px; padding: 6px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 6px; box-shadow: var(--shadow);
}
.menu a {
  display: flex; align-items: center; width: 100%; gap: 10px; padding: 8px 10px; border: 0; border-radius: 4px;
  background: none; color: var(--ink); font: inherit; font-size: 0.875rem; text-align: left; text-decoration: none; cursor: pointer;
}
.menu a:hover, .menu a:focus-visible { background: var(--bg-2); outline: none; }
.menu .menu-label { padding: 8px 10px 4px; color: var(--ink-3); font-size: 0.75rem; }
.menu .menu-row { padding: 2px 6px 6px; }
.menu hr { border: 0; border-top: 1px solid var(--line); margin: 6px 0; }

/* ---- Form fields (design-system component) ----------------------------
   Shared interaction states for every text-like field (input, textarea,
   select). Pages own size/padding/border; these own hover, focus, disabled.
   The :is()/:not() selector is deliberately specific enough to win over
   page-level `input[type=text]` rules. */
:is(input:not([type=checkbox],[type=radio],[type=color],[type=file],[type=range],[type=submit],[type=button],[type=hidden]), textarea, select) {
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}
:is(input:not([type=checkbox],[type=radio],[type=color],[type=file],[type=range],[type=submit],[type=button],[type=hidden]), textarea, select):hover:not(:focus):not(:disabled) {
  border-color: var(--ink-3);
}
:is(input:not([type=checkbox],[type=radio],[type=color],[type=file],[type=range],[type=submit],[type=button],[type=hidden]), textarea, select):focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
:is(input, textarea, select):disabled { opacity: 0.5; cursor: default; }
/* Touch screens: fields at 16px at least. iOS zooms the page in on any field
   with smaller text when it's focused, and keeps that zoom after leaving it. */
@media (pointer: coarse) {
  :is(input:not([type=checkbox],[type=radio],[type=color],[type=file],[type=range],[type=submit],[type=button],[type=hidden]), textarea, select) { font-size: max(16px, 1em) !important; } /* beats any page rule */
}
input[type=checkbox], input[type=radio] { accent-color: var(--ink); }

/* ---- Select -----------------------------------------------------------
   Native <select> with the OS arrow replaced by an inset chevron, so the caret
   never touches the border. Pages set padding/border; this owns the arrow. */
select {
  -webkit-appearance: none; appearance: none;
  padding-right: 42px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23716c63' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 16px 16px;
  cursor: pointer;
  text-overflow: ellipsis;
}
select::-ms-expand { display: none; }

/* ---- Buttons / links -------------------------------------------------- */
.link-underline {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding-bottom: 3px;
  border-bottom: 1px solid currentColor;
  transition: gap 0.4s var(--ease), opacity 0.3s var(--ease);
}
.link-underline:hover { gap: 1.1em; }
.link-underline .arrow { transition: transform 0.4s var(--ease); }
.link-underline:hover .arrow { transform: translateX(4px); }

/* =========================================================================
   HOME + INDEX PAGES (masthead, page headers, tile grids)
   ========================================================================= */
/* Inner-page header — mirrors the home masthead rhythm, plus clearance for the
   fixed nav bar (--nav-h). */
.page-header {
  padding-top: calc(var(--nav-h) + clamp(20px, 4vh, 48px));
  padding-bottom: clamp(24px, 4vh, 44px);
}
.page-header h1 {
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(2rem, 5vw, 3.5rem); letter-spacing: -0.02em; line-height: 1.05;
}
.page-header p { margin-top: 18px; color: var(--ink-2); max-width: 48ch; }

/* Home header — masthead above the grid (home page has no nav bar). */
.home-intro {
  padding-top: clamp(48px, 8vh, 96px);
  padding-bottom: clamp(28px, 5vh, 56px);
}
.home-title {
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(2.4rem, 7vw, 5rem); letter-spacing: -0.02em; line-height: 1;
}
.home-sub {
  margin-top: 16px; color: var(--ink-2);
  font-size: clamp(1rem, 1.4vw, 1.15rem); max-width: 56ch;
}

.collection-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--gap);
  padding-bottom: var(--space-section);
}
/* Same side gutter as the photo galleries (half the page gutter) on larger
   screens; phones stay edge to edge. */
@media (min-width: 701px) { .collection-grid { margin-inline: calc(var(--gutter) / 2); } }
@media (max-width: 900px) { .collection-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) {
  .collection-grid { grid-template-columns: 1fr; gap: 0; }
}
.collection-card { display: block; }
.collection-card .card-media {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  aspect-ratio: 4 / 5;   /* fixed ratio keeps every tile the same height */
  background: var(--bg-2);
}
.collection-card .card-media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 1.1s var(--ease), filter 0.6s var(--ease);
}
.collection-card:hover .card-media img { transform: scale(1.05); }
.card-foot {
  display: flex; align-items: baseline; justify-content: space-between;
  /* Closer to its own photo than to the next row's, so it reads as that photo's title. */
  gap: 16px; margin-top: 12px; padding-bottom: clamp(28px, 3.2vw, 44px);
}
.card-foot h3 { font-family: var(--serif); font-weight: 400; font-size: 1.35rem; }
/* Phones: tiles run edge to edge, so the title keeps the page's side margin. */
@media (max-width: 700px) { .card-foot { padding-inline: var(--gutter); } }

/* =========================================================================
   COLLECTION PAGE
   ========================================================================= */
/* Starts below the fixed nav bar so the top of the cover isn't hidden under it. */
.collection-hero { position: relative; height: 78svh; min-height: 480px; overflow: hidden; margin-top: var(--nav-h); }
.collection-hero img { width: 100%; height: 100%; object-fit: cover; }
.collection-hero::after {  /* light scrims: nav at the top, breadcrumb at the bottom */
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0.1), rgba(0,0,0,0) 30%, rgba(0,0,0,0) 75%, rgba(0,0,0,0.3));
}
.collection-hero .caption {
  position: absolute; left: 0; bottom: 0; z-index: 1;
  width: 100%; color: #fff; padding: var(--gutter);
}
.collection-hero .caption .crumb,
.page-header .crumb {
  display: inline-block; margin-bottom: 12px;
  font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(255,255,255,0.85); opacity: 0.9;
}
.collection-hero .caption .crumb:hover { opacity: 1; }
.collection-hero .caption .crumb { margin-bottom: 0; } /* the only thing on the banner */
.page-header .crumb { color: var(--ink-3); }
.page-header .meta {
  margin-top: 10px; font-size: 0.8rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-3);
}
/* No banner: the intro lines up under the left-aligned header, not centered. */
.page-header + .collection-intro { padding-top: 0; max-width: var(--container); }
.page-header + .collection-intro p { max-width: 60ch; }
.page-header .crumb:hover { color: var(--ink); }

.collection-intro { padding-block: clamp(40px, 8vh, 96px); max-width: 60ch; }
/* No description: keep the banner off the photos by exactly one gallery gap. */
/* Title + date sit under the banner: tight, no nav clearance needed there. */
.collection-hero + .page-header { padding-block: 15px; }
.collection-intro p { font-size: 1.1rem; color: var(--ink-2); }

/* Galleries (gallery.js) — photos are NEVER cropped. Each tile gets its
   photo's own aspect-ratio inline; JS arranges tiles into justified rows
   (.g-row, "grid") or balanced columns (.g-col, "masonry"). The gap is the
   same on every screen size (--g), which the layout math reads back. */
.gallery {
  --g: var(--gap);
  /* Side gutter (half the page gutter) so photos don't touch the window edge.
     Margin, not padding: gallery.js lays out against clientWidth. */
  margin-inline: calc(var(--gutter) / 2);
  display: flex; flex-direction: column; gap: var(--g);
  padding-bottom: var(--space-section);
}
.gallery[data-layout="masonry"] { flex-direction: row; align-items: flex-start; }
.g-row { display: flex; gap: var(--g); }
.g-row--capped { justify-content: center; }  /* a lone tall photo isn't stretched */
.g-col { display: flex; flex-direction: column; gap: var(--g); flex: none; }
.photo {
  margin: 0;                 /* reset default <figure> margin */
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--bg-2);
  cursor: pointer;
  min-width: 0;
}
.photo img {
  width: 100%; height: 100%; object-fit: cover; display: block; /* box == photo ratio → no crop */
  transition: opacity 0.4s var(--ease);
}
.photo .dl {
  position: absolute; right: 12px; bottom: 12px;
  width: 40px; height: 40px; display: grid; place-items: center;
  background: rgba(255,255,255,0.92); color: var(--ink);
  border-radius: 50%; opacity: 0; transform: translateY(6px);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
  backdrop-filter: blur(6px);
}
.photo .dl svg { width: 18px; height: 18px; }
.photo .dl { z-index: 2; cursor: pointer; }
/* Hover reveals only on devices with a real pointer. On touch screens a tap
   that reveals content counts as a "hover" first (iOS then needs a second tap),
   so there the tile's download button is hidden and one tap opens the
   lightbox, which has its own download button. */
@media (hover: hover) {
  .photo:hover img { opacity: 0.9; }
  .photo .dl:hover { background: #fff; }
  .photo:hover .dl,
  .photo:focus-within .dl { opacity: 1; transform: translateY(0); } /* keyboard users can reach it */
  .photo:hover .photo-label { opacity: 1; transform: none; }
}
@media (hover: none) {
  .photo .dl { display: none; }
}
/* Hover title (collection name on the starred home grid). */
.photo-label {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  padding: 40px 16px 14px;
  font-family: var(--serif); font-size: 1rem; letter-spacing: 0.01em; color: #fff;
  background: linear-gradient(180deg, transparent, rgba(0,0,0,0.45));
  opacity: 0; transform: translateY(6px); pointer-events: none;
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
@media (hover: none) { .photo-label { opacity: 1; transform: none; } } /* always shown on touch */
.photo:has(.photo-label) .dl { z-index: 2; }

/* Lightbox — full-bleed viewer with a thumbnail rail on the right. */
.lightbox {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(12, 12, 13, 0.98);
  display: none; opacity: 0; transition: opacity 0.35s var(--ease);
}
.lightbox.open { display: flex; opacity: 1; }

.lb-main {
  position: relative; flex: 1; min-width: 0;
  display: flex; flex-direction: column;
  padding: clamp(24px, 4vw, 56px);
}
.lb-stage { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; position: relative; }
.lb-stage img { transition: opacity 0.25s var(--ease); }
.lb-stage.loading img { opacity: 0.35; }   /* previous photo dims while the next loads */
.lb-spinner {
  position: absolute; left: 50%; top: 50%; width: 30px; height: 30px; margin: -15px 0 0 -15px;
  border: 2px solid rgba(255,255,255,0.25); border-top-color: #fff; border-radius: 50%;
  opacity: 0; transition: opacity 0.2s var(--ease); pointer-events: none;
  animation: lb-spin 0.8s linear infinite;
}
.lb-stage.loading .lb-spinner { opacity: 1; transition-delay: 0.15s; } /* skip it for instant loads */
@keyframes lb-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .lb-spinner { animation: none; } }
.lb-main { touch-action: pan-y pinch-zoom; } /* horizontal swipes are ours */
.lb-stage img {
  max-width: 100%; max-height: 100%; object-fit: contain;
  box-shadow: 0 30px 80px -30px rgba(0,0,0,0.8);
}
.lb-footer {
  display: flex; align-items: center; justify-content: center; gap: 18px;
  padding-top: 16px; min-height: 28px;
}
.lb-caption { color: rgba(255,255,255,0.72); font-size: 0.85rem; letter-spacing: 0.02em; }
.lb-caption .lb-count { color: rgba(255,255,255,0.4); margin-left: 12px; }

.lb-close, .lb-nav {
  position: absolute; z-index: 2; color: #fff; background: none; border: none;
  line-height: 1; opacity: 0.65; transition: opacity 0.25s var(--ease); cursor: pointer;
}
.lb-close:hover, .lb-nav:hover { opacity: 1; }
.lb-close { top: 14px; right: 26px; font-size: 2rem; padding: 8px; }
.lb-nav { top: 50%; transform: translateY(-50%); font-size: 2.4rem; padding: 12px; }
.lb-prev { left: 4px; }
.lb-next { right: 4px; }

.lb-download {
  width: 40px; height: 40px; display: grid; place-items: center; flex: none;
  color: #fff; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.3);
  border-radius: 50%; transition: background 0.25s var(--ease);
}
.lb-download:hover { background: rgba(255,255,255,0.22); }
.lb-download svg { width: 18px; height: 18px; }

/* Right-side thumbnail rail */
.lb-rail {
  flex: none; width: 120px; overflow-y: auto; overscroll-behavior: contain;
  padding: 72px 16px 20px; display: flex; flex-direction: column; gap: 10px;
  background: rgba(0, 0, 0, 0.28); border-left: 1px solid rgba(255,255,255,0.06);
}
.lb-rail::-webkit-scrollbar { width: 6px; }
.lb-rail::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.18); border-radius: 3px; }
.lb-rail button {
  flex: none; padding: 0; border: 2px solid transparent; border-radius: 2px;
  background: none; cursor: pointer; overflow: hidden; aspect-ratio: 3 / 2;
}
.lb-rail button img { width: 100%; height: 100%; object-fit: cover; display: block;
  opacity: 0.5; transition: opacity 0.2s var(--ease); }
.lb-rail button:hover img { opacity: 0.85; }
.lb-rail button.active { border-color: var(--accent); }
.lb-rail button.active img { opacity: 1; }

@media (max-width: 700px) {
  .lb-rail { display: none; }
  .lb-main { padding: 60px 8px 20px; }
  .lb-close { right: 10px; }
}

/* =========================================================================
   PAGE NOT FOUND (404.html; content from notfound.js)
   Masthead (logo or name), then the words on the left and the site's photo
   on the right, running off the right edge and sitting on the bottom. The
   photo multiplies onto the page, so a white background disappears into it.
   ========================================================================= */
.nf-page { min-height: 100vh; min-height: 100svh; display: flex; flex-direction: column; }
.nf-root { flex: 1; display: flex; flex-direction: column; }
.nf-mast { display: flex; justify-content: center; padding: clamp(28px, 5vh, 52px) var(--gutter) 0; }
.site-mark { font-family: var(--serif); font-size: clamp(1.4rem, 2.4vw, 2rem); font-weight: 400; letter-spacing: 0.06em; line-height: 1; }
.site-mark img { height: clamp(44px, 6vw, 72px); width: auto; }
.nf {
  flex: 1; display: grid; grid-template-columns: minmax(0, 1fr); align-items: center; column-gap: 24px;
  padding-block: clamp(40px, 8vh, 96px);
  padding-inline: clamp(20px, 7.5vw, 144px) var(--gutter);
}
.nf.has-photo { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); padding-right: 0; padding-bottom: 0; }
.nf-code { font-size: clamp(6.5rem, 15vw, 14rem); font-weight: 300; line-height: 0.8; letter-spacing: -0.03em; }
.nf-title { margin-top: 0.3em; font-size: clamp(2.25rem, 4.6vw, 4.25rem); font-weight: 400; line-height: 1.05; letter-spacing: -0.02em; }
.nf-line { margin-top: 1.4em; color: var(--ink-2); font-size: clamp(0.95rem, 1.3vw, 1.25rem); letter-spacing: 0.18em; }
.nf .cta { margin-top: clamp(32px, 5vh, 56px); }
.nf-photo { align-self: end; }
.nf-photo img {
  width: 100%; height: auto; max-height: 78vh; max-height: 78svh;
  object-fit: contain; object-position: right bottom; mix-blend-mode: multiply;
}
@media (max-width: 700px) {
  .nf, .nf.has-photo { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; align-items: start; row-gap: 40px; padding-inline: var(--gutter); }
  .nf.has-photo { padding-right: 0; }
  .nf-text { padding-right: var(--gutter); }
}

/* =========================================================================
   CONTACT
   ========================================================================= */
.contact { padding-block: var(--space-section); }
[data-contact] { white-space: pre-line; } /* editable wording keeps its line breaks */
.contact-lead h2 {
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(1.8rem, 4vw, 3rem); letter-spacing: -0.01em; margin-bottom: 16px;
}
.contact-lead p { color: var(--ink-2); max-width: 40ch; }
.contact-form { display: grid; gap: 16px; }
.field-row { display: grid; gap: 16px; grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }
.field { display: grid; gap: 6px; }
.field span {
  font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);
}
.contact-form input,
.contact-form select,
.contact-form textarea,
.lock-form input {
  width: 100%; padding: 12px 14px; border: 1px solid var(--line); border-radius: 6px;
  font: inherit; background-color: #fff; color: var(--ink);
}
.contact-form select { padding-right: 42px; }
.contact-form textarea { resize: vertical; min-height: 96px; }
.btn-primary {
  justify-self: start; padding: 13px 26px; border: none; border-radius: 6px;
  background: var(--ink); color: #fff; font-size: 0.82rem; letter-spacing: 0.06em;
  display: inline-flex; align-items: center; gap: 0.5em; transition: opacity 0.25s var(--ease);
}
.btn-primary:hover { opacity: 0.85; }
.btn-primary:disabled { opacity: 0.4; }
.form-note { font-size: 0.85rem; }
/* Contact honeypot: off-screen (not display:none, which some bots skip). */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-note.ok { color: #2f7d4f; }
.form-note.err { color: #b23a2e; }

/* =========================================================================
   PASSWORD-PROTECTED GALLERY — lock screen (collection.js showLock)
   ========================================================================= */
.lock { padding-bottom: var(--space-section); }
.lock-form { display: grid; gap: 16px; max-width: 420px; }
.lock-form > p { color: var(--ink-2); }
/* Password field with a Show/Hide button inside it. */
.field .pw-field { /* undo the `.field span` label style */
  position: relative; display: block;
  font-size: inherit; letter-spacing: normal; text-transform: none; color: inherit;
}
.pw-field input { padding-right: 72px; }
.pw-toggle {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  padding: 6px 10px; border: 0; background: none; cursor: pointer;
  font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3);
}
.pw-toggle:hover { color: var(--ink); }

/* =========================================================================
   FOOTER
   ========================================================================= */
.site-foot {
  border-top: 1px solid var(--line);
  padding-block: 22px;
}
.foot-grid {
  display: flex; align-items: center; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
}
.foot-mark { font-family: var(--serif); font-size: 1.05rem; font-weight: 500; letter-spacing: 0.01em; }
.foot-mark .foot-logo { height: 26px; width: auto; display: block; }
.foot-links { display: flex; gap: 22px; font-size: 0.76rem; letter-spacing: 0.06em; text-transform: uppercase; }
.foot-fine { margin-top: 12px; color: var(--ink-3); font-size: 0.72rem; width: 100%; }

/* =========================================================================
   MOTION — reveal on scroll (progressive enhancement)
   ========================================================================= */
[data-reveal] {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  transition-delay: var(--reveal-delay, 0s);
}
[data-reveal].in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition-duration: 0.01ms !important; }
  [data-reveal] { opacity: 1; transform: none; }
  html { scroll-behavior: auto; }
}

/* Preview of unpublished changes (ui.js showPreviewBar). */
.preview-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 90;
  display: flex; align-items: center; justify-content: space-between; gap: 12px 20px; flex-wrap: wrap;
  padding: 12px var(--gutter); background: var(--ink); color: #fff; font-size: 0.85rem;
}
.preview-actions { display: flex; gap: 8px; }
.preview-bar button {
  padding: 8px 14px; border-radius: 6px; border: 1px solid rgba(255, 255, 255, 0.5);
  background: none; color: #fff; font: inherit; cursor: pointer;
}
.preview-bar .preview-publish { background: #fff; color: var(--ink); border-color: #fff; }
.preview-bar button:hover { opacity: 0.85; }
.preview-bar button:disabled { opacity: 0.5; cursor: default; }
body.has-preview-bar { padding-bottom: 72px; }

/* Couldn't load the site data (ui.js showLoadError). Inner pages: clear the nav. */
.load-error .btn-primary { margin-top: 18px; }
main > .load-error { padding-top: calc(var(--nav-h) + 80px); }

/* Empty / loading states */
.state {
  padding: 80px 0; text-align: center; color: var(--ink-3);
  grid-column: 1 / -1;  /* span the full grid when used inside one */
}
