/* ── RESET ───────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
img, video { max-width: 100%; display: block; }

/* ── TOKENS ──────────────────────────────────────────────────────── */
:root {
    --ink:      #141311;
    --ink-2:    #1D1B18;
    --paper:    #F4F1EA;
    --white:    #FAF8F3;
    --gold:        #8A9A72;
    --gold-dim:    rgba(138,154,114,0.45);
    --gold-dark:   #56643F;
    --gold-bright: #8A9A72;
    --ash:      #9D9686;
    --ash-dark: #56503F;
    --line:     rgba(250,248,243,0.12);
    --line-dark: rgba(20,19,17,0.12);
    --sans: 'PP Neue Montreal', system-ui, -apple-system, sans-serif;
    --serif: 'Fraunces', Georgia, 'Iowan Old Style', serif;
    --mono: 'SF Mono', 'JetBrains Mono', ui-monospace, Menlo, monospace;
    --ease: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-2: cubic-bezier(0.65, 0, 0.35, 1);
}

@font-face { font-family: 'PP Neue Montreal'; src: url('fonts/PPNeueMontreal-Light.woff2') format('woff2'); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: 'PP Neue Montreal'; src: url('fonts/PPNeueMontreal-Book.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'PP Neue Montreal'; src: url('fonts/PPNeueMontreal-Medium.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'PP Neue Montreal'; src: url('fonts/PPNeueMontreal-Bold.woff2') format('woff2'); font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: 'PP Neue Montreal'; src: url('fonts/PPNeueMontreal-Italic.woff2') format('woff2'); font-weight: 400; font-style: italic; font-display: swap; }

/* ── Fraunces — the bespoke editorial serif paired with PP Neue Montreal.
   Self-hosted (not a system-font fallback) so the "second voice" in the
   type system is as deliberate a choice as the sans. ────────────────── */
@font-face { font-family: 'Fraunces'; src: url('fonts/Fraunces-Regular.woff2') format('woff2'); font-weight: 300 600; font-style: normal; font-display: swap; }
@font-face { font-family: 'Fraunces'; src: url('fonts/Fraunces-Italic.woff2') format('woff2'); font-weight: 300 600; font-style: italic; font-display: swap; }

body {
    background: var(--ink);
    color: var(--white);
    font-family: var(--sans);
    font-weight: 300;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }
.wrap { max-width: 1320px; margin: 0 auto; padding: 0 clamp(24px, 5vw, 64px); }

/* ── FOCUS — every interactive element gets a real, visible ring.
   Removing this for visual cleanliness is the most common way a
   "premium minimal" site quietly fails keyboard/screen-reader users. ── */
:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

/* ── MASTHEAD — the running head used across every page (home, case
   studies via dateline, and here). Was defined inline on index.html only;
   moved here so a second page (photography.html) doesn't have to duplicate
   it — same mistake .page-head already made once this project. ────────── */
.masthead { position: relative; z-index: 3; margin-top: 86px; padding: 0.7rem clamp(24px, 5vw, 64px); display: flex; align-items: center; gap: 1.1em; flex-wrap: wrap; font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ash); background: var(--ink); border-bottom: 1px solid var(--line); }
.masthead .sep { color: rgba(250,248,243,0.3); }
.masthead .hl { color: var(--gold); }

/* ── NAV — solid paper bar at all times: crisp, high-contrast, no
   transparent-over-video ambiguity. ─────────────────────────────── */
nav {
    position: fixed; top: 0; left: 0; right: 0; z-index: 260;
    display: flex; align-items: center; justify-content: space-between;
    padding: 1.5rem clamp(24px, 5vw, 64px);
    background: var(--paper);
    border-bottom: 1px solid var(--line-dark);
    transition: box-shadow 0.4s var(--ease);
    --gold: var(--gold-dark);
}
nav.scrolled { box-shadow: 0 10px 30px rgba(20,19,17,0.10); }
.nav-mark { position: relative; display: inline-block; font-family: var(--sans); font-weight: 700; font-size: 0.82rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink); }
.nav-mark span { color: var(--gold); }

/* ── SCRATCH REVEAL — hovering "Raphael Loquellano" uncovers navy only
   where the cursor has actually passed, like scratching foil off a card,
   rather than a flat hover-color swap. Two identical text layers stacked
   exactly on top of each other (nm-base underneath in normal colors,
   nm-reveal above in navy); an offscreen canvas accumulates a paint trail
   as the mouse moves and that canvas becomes nm-reveal's CSS mask, so only
   the painted pixels of the navy layer show through. Getting the whole
   name blue genuinely requires dragging the cursor across all of it, not
   just entering the link once. Resets on mouse-leave — each hover is its
   own scratch, not a permanent one-time reveal. See setupScratchReveal()
   in main.js. Static (no mask-image yet) until JS runs, so it degrades to
   the plain base layer if JS fails — never a broken/invisible nav-mark. ── */
/* nm-base is itself a <span>, so the existing ".nav-mark span" rule above
   (meant only for the nested "LOQUELLANO" span) matches this wrapper too
   and would tint the whole base layer gold — explicit color restores it. */
.nav-mark .nm-base { position: relative; z-index: 1; color: var(--ink); }
/* .nav-mark .nm-reveal / .nav-mark .nm-reveal span — not the flatter
   .nm-reveal / .nm-reveal span — because .nav-mark span (existing, higher
   specificity than a single class) already matches this same element and
   was silently winning the color battle regardless of source order. */
.nav-mark .nm-reveal { position: absolute; inset: 0; z-index: 2; color: #1F2E4D; pointer-events: none; }
.nav-mark .nm-reveal span { color: #1F2E4D; }
.nav-links { list-style: none; display: flex; gap: clamp(1.1rem, 2.4vw, 2.4rem); }
.nav-links a { font-size: 0.72rem; font-weight: 500; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ash-dark); transition: color 0.3s var(--ease); }
.nav-links a:hover, .nav-links a.active { color: var(--ink); }
.nav-cta { font-size: 0.72rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; padding: 0.7rem 1.4rem; border: 1px solid var(--line-dark); border-radius: 2px; color: var(--ink); transition: border-color 0.3s var(--ease), background 0.3s var(--ease); }
.nav-cta:hover { border-color: var(--gold); background: rgba(86,100,63,0.08); }
.nav-toggle {
    display: none; align-items: center; justify-content: center;
    width: 40px; height: 40px; flex: none;
    appearance: none; -webkit-appearance: none;
    background: none; border: none; color: var(--ink); cursor: pointer; padding: 0; margin: 0;
    z-index: 300;
}
.nav-toggle svg { width: 22px; height: 22px; display: block; pointer-events: none; }

.mobile-menu {
    position: fixed; inset: 0; z-index: 250; background: var(--paper);
    display: flex; flex-direction: column; justify-content: center; gap: 0.4rem;
    padding: 0 clamp(24px, 8vw, 64px);
    transform: translateY(-100%); transition: transform 0.5s var(--ease);
    --gold: var(--gold-dark);
}
.mobile-menu.open { transform: translateY(0); }
.mobile-menu a { font-family: var(--sans); font-size: clamp(1.8rem, 8vw, 2.4rem); font-weight: 500; color: var(--ink); padding: 0.55rem 0; border-bottom: 1px solid var(--line-dark); }
.mobile-menu a.mm-cta { color: var(--gold); }
body.menu-open { overflow: hidden; }

@media (max-width: 760px) {
    .nav-links, .nav-cta { display: none; }
    .nav-toggle { display: flex; align-items: center; justify-content: center; }
}
@media (min-width: 761px) { .mobile-menu { display: none; } }

/* ── SECTION CHROME ─────────────────────────────────────────────── */
.eyebrow { font-family: var(--mono); font-size: 0.7rem; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold); margin-bottom: 1rem; display: flex; align-items: center; gap: 0.7em; }
.eyebrow::before {
    content: ''; width: 13px; height: 13px; flex: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.2' fill='none' stroke='%238A9A72' stroke-width='1.3'/%3E%3Ccircle cx='8' cy='8' r='1.7' fill='%238A9A72'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-size: contain;
    animation: lens-spin 18s linear infinite;
}
.section.on-paper .eyebrow::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.2' fill='none' stroke='%2356643F' stroke-width='1.3'/%3E%3Ccircle cx='8' cy='8' r='1.7' fill='%2356643F'/%3E%3C/svg%3E");
}
@keyframes lens-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .eyebrow::before { animation: none; } }

/* ── GRAIN — ambient film-grain texture over the whole page, the kind of
   quiet tactile detail that separates "shot" from "stock." One overlay,
   everywhere, instead of a decorative device repeated per element. ───── */
body::after {
    content: ''; position: fixed; inset: 0; z-index: 998; pointer-events: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    opacity: 0.05; mix-blend-mode: overlay;
}
h1, h2, h3 { font-family: var(--sans); font-weight: 500; letter-spacing: -0.022em; line-height: 1.0; text-wrap: balance; }
h1 { font-size: clamp(2.8rem, 7.2vw, 6.4rem); font-weight: 500; }
/* Genuine typeface contrast on accent words, not just an italicized copy of
   the sans — the same editorial serif already used in quote blocks and case-
   study copy, so the "voice shift" reads as a real second typeface. */
h1 em, h2 em { font-family: var(--serif); font-style: italic; font-weight: 400; color: var(--gold); }
h2 { font-size: clamp(2.1rem, 4.6vw, 3.7rem); }
h3 { font-size: clamp(1.3rem, 2vw, 1.7rem); font-weight: 500; }
p.lede { font-size: clamp(1.05rem, 1.6vw, 1.3rem); font-weight: 300; line-height: 1.58; color: var(--ash); max-width: 62ch; }

/* ── VIEWFINDER — reserved for the one true hero moment per page (home hero,
   project hero). Deliberately NOT applied to repeated grid media — a
   signature detail repeated on every thumbnail stops being a detail. ──── */
.viewfinder { position: relative; }
.viewfinder::after {
    content: ''; position: absolute; inset: 18px; pointer-events: none; z-index: 3;
    background-image:
        linear-gradient(var(--white), var(--white)), linear-gradient(var(--white), var(--white)),
        linear-gradient(var(--white), var(--white)), linear-gradient(var(--white), var(--white)),
        linear-gradient(var(--white), var(--white)), linear-gradient(var(--white), var(--white)),
        linear-gradient(var(--white), var(--white)), linear-gradient(var(--white), var(--white));
    background-repeat: no-repeat;
    background-size: 1px 16px, 16px 1px, 1px 16px, 16px 1px, 1px 16px, 16px 1px, 1px 16px, 16px 1px;
    background-position:
        top left, top left,
        top right, top right,
        bottom left, bottom left,
        bottom right, bottom right;
    opacity: 0.5;
    transition: inset 0.5s var(--ease), opacity 0.5s var(--ease);
}
.viewfinder:hover::after { inset: 12px; opacity: 0.8; }
/* Corner brackets read as a considered detail on a full-size hero; on a
   phone-width hero they're disproportionately large relative to the box
   and just add clutter around already-tight overlaid text — drop them
   below the same 700px breakpoint used for other mobile hero adjustments. */
@media (max-width: 700px) { .viewfinder::after { content: none; } }

/* ── EDITORIAL LIST — replaces generic equal-width card grids wherever a
   set of items is really a sequence to read down, not tiles to scan. ── */
.ed-row { display: grid; grid-template-columns: 0.32fr 1fr 1fr; gap: clamp(20px, 3vw, 40px); padding: clamp(28px, 3.6vw, 44px) 0; border-top: 1px solid var(--line-dark); align-items: start; }
.ed-row:last-child { border-bottom: 1px solid var(--line-dark); }
@media (max-width: 860px) { .ed-row { grid-template-columns: 1fr; gap: 0.9rem; } }
.ed-num { font-family: var(--mono); font-size: 0.85rem; color: var(--gold); letter-spacing: 0.06em; padding-top: 0.2em; }
.ed-body p { color: var(--ash); margin-top: 0.8rem; line-height: 1.6; }
.ed-list { list-style: none; display: flex; flex-direction: column; gap: 0.65rem; }
.ed-list li { font-size: 0.95rem; color: var(--ash); padding-left: 1.2rem; position: relative; }
.ed-list li::before {
    content: ''; position: absolute; left: 0; top: 0.3em; width: 9px; height: 9px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.2' fill='none' stroke='%238A9A72' stroke-width='1.6'/%3E%3Ccircle cx='8' cy='8' r='1.9' fill='%238A9A72'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-size: contain;
}
.section.on-paper .ed-list li::before {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='6.2' fill='none' stroke='%2356643F' stroke-width='1.6'/%3E%3Ccircle cx='8' cy='8' r='1.9' fill='%2356643F'/%3E%3C/svg%3E");
}
.section { padding: clamp(72px, 11vw, 140px) 0; }
/* ── PAGE HEAD — was copy-pasted identically into about.html, services.html,
   and contact.html's own inline <style>, with work.html carrying a near-
   duplicate. One definition, one place to change it. ────────────────── */
.page-head { padding-top: clamp(140px, 18vw, 210px); padding-bottom: 1rem; }
.page-head.tight { padding-bottom: 3rem; }
.featured-head { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 2.6rem; flex-wrap: wrap; gap: 1rem; }
.section.on-paper { background: var(--paper); color: var(--ink); --gold: var(--gold-dark); }
.section.on-paper .ash { color: var(--ash-dark); }
.section.on-paper p.lede { color: var(--ash-dark); }
.section.on-paper .ed-body p, .section.on-paper .ed-list li { color: var(--ash-dark); }

/* ── ED-PROOF — a small linked project still for each capability row, so
   "Brand & Social Video" etc. point at actual finished work instead of
   standing as an unsupported claim. Opt-in per row (not every ed-row needs
   one); reuses the site's aspect-ratio-plus-explicit-width pattern to
   avoid the aspect-ratio/height-cap conflict already hit elsewhere. ──── */
.ed-proof { display: block; }
.ed-proof-media { width: 100%; aspect-ratio: 4 / 5; overflow: hidden; border-radius: 3px; background: var(--ink-2); }
.ed-proof-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s var(--ease); display: block; }
.ed-proof:hover .ed-proof-media img { transform: scale(1.045); }
.ed-proof-cap { display: flex; align-items: baseline; gap: 0.5em; margin-top: 0.7rem; font-family: var(--mono); font-size: 0.68rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ash); transition: color 0.3s var(--ease); }
.ed-proof-cap .arrow { transition: transform 0.3s var(--ease); }
.ed-proof:hover .ed-proof-cap { color: var(--gold); }
.ed-proof:hover .ed-proof-cap .arrow { transform: translateX(3px); }
.section.on-paper .ed-proof-cap { color: var(--ash-dark); }
.hairline { border: none; border-top: 1px solid var(--line); }
.section.on-paper .hairline { border-top: 1px solid var(--line-dark); }

.btn { display: inline-flex; align-items: center; gap: 0.6em; font-size: 0.78rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; padding: 0.95rem 1.8rem; border-radius: 2px; transition: all 0.3s var(--ease); }
.btn-primary { background: var(--white); color: var(--ink); }
.btn-primary:hover { background: var(--gold); }
.btn-ghost { border: 1px solid var(--line); color: var(--white); }
.section.on-paper .btn-ghost { border: 1px solid var(--line-dark); color: var(--ink); }
.btn-ghost:hover { border-color: var(--gold); }
.btn-arrow { transition: transform 0.3s var(--ease); }
.btn:hover .btn-arrow { transform: translateX(4px); }

/* ── FOOTER ──────────────────────────────────────────────────────── */
footer { padding: 56px 0 40px; border-top: 1px solid var(--line); }
footer .wrap { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; }
footer .foot-mark { font-weight: 700; font-size: 0.8rem; letter-spacing: 0.12em; text-transform: uppercase; }
footer .foot-links { display: flex; gap: 1.8rem; list-style: none; font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ash); }
footer .foot-links a:hover { color: var(--white); }

/* ── REVEAL ──────────────────────────────────────────────────────── */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.reveal.visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; } }

/* ── WORK GRID (shared by home preview + work.html) ───────────────
   3 columns, not 2 — at 2-up, 9 real projects leaves the 9th stranded
   alone in its own row with a huge empty gap beside it (an odd-count
   problem that recurs every time an even number of projects gets added).
   3-up resolves the current count to exactly 3 clean rows and produces
   more moderately-sized cards better suited to a full project inventory
   than 2 oversized columns. ────────────────────────────────────────── */
.work-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(16px, 2.4vw, 32px); }
@media (max-width: 980px) { .work-grid { grid-template-columns: repeat(2, 1fr); } }
.work-card { position: relative; overflow: hidden; background: var(--ink-2); aspect-ratio: 4/5; cursor: none; transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease); --gold: var(--gold-bright); }
.work-card:hover { transform: translateY(-6px); box-shadow: 0 30px 60px -14px rgba(20,19,17,0.55), 0 12px 26px -8px rgba(20,19,17,0.4); }

/* Compact variant — home page's curated 3-up. The old "bento" treatment
   (first card spanning 2 rows, oversized next to the other two) read as
   too heavy for a homepage that already has a full-bleed hero and two
   more full-bleed break sections above it — three equal, modestly-sized
   cards is the quieter, cleaner choice here. */
.work-grid.compact { grid-template-columns: repeat(3, 1fr); }
.work-grid.compact .work-card { aspect-ratio: 4/5; }
@media (max-width: 860px) { .work-grid.compact { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .work-grid.compact { grid-template-columns: 1fr; } }

/* Carousel variant — same .work-card markup, swapped to a horizontal
   scroll-snap track instead of a grid. Toggle lives in the section header;
   JS just swaps this class on the same #featured-grid element rather than
   re-rendering, so hover-play video state etc. is untouched by the switch. */
.work-grid.carousel-mode { display: flex; grid-template-columns: none; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; padding-bottom: 2px; }
.work-grid.carousel-mode::-webkit-scrollbar { display: none; }
.work-grid.carousel-mode .work-card { flex: none; width: min(72vw, 400px); aspect-ratio: 4/5; scroll-snap-align: center; }
.view-toggle { display: inline-flex; gap: 0.4rem; }
.view-toggle button { font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase; padding: 0.5rem 0.9rem; border: 1px solid var(--line-dark); border-radius: 999px; color: var(--ash); background: transparent; transition: all 0.25s var(--ease); }
.view-toggle button:hover { color: var(--ink); }
.view-toggle button.active { background: var(--ink); border-color: var(--ink); color: var(--white); }
.section.on-paper .view-toggle button.active { background: var(--ink); color: var(--white); }
.work-card img, .work-card video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform 0.9s var(--ease), filter 0.5s var(--ease); filter: saturate(0.94); }
.work-card:hover img, .work-card:hover video { transform: scale(1.04); filter: saturate(1.05); }
.work-card .overlay { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 62%, rgba(10,10,11,0.82) 100%); z-index: 2; }
/* Pinned to white regardless of the section wrapping the card — this text
   sits on the video/image thumbnail's own dark gradient, not on the page's
   ambient background, so it must never inherit an on-paper section's dark
   text color (was rendering near-invisible: near-black on near-black). */
.work-card .meta { position: absolute; left: 0; right: 0; bottom: 0; padding: 1.5rem 1.7rem; z-index: 3; color: var(--white); text-shadow: 0 1px 3px rgba(0,0,0,0.55), 0 6px 20px rgba(0,0,0,0.35); }
.work-card .cat { font-family: var(--mono); font-size: 0.66rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--gold); margin-bottom: 0.35rem; }
.work-card .title { font-size: 1.2rem; font-weight: 500; color: var(--white); }
.work-card .placeholder-tag { position: absolute; top: 1.2rem; right: 1.2rem; z-index: 3; font-family: var(--mono); font-size: 0.6rem; letter-spacing: 0.1em; text-transform: uppercase; background: rgba(20,19,17,0.7); border: 1px solid var(--line); padding: 0.35rem 0.7rem; color: var(--ash); }
.work-card.placeholder { display: flex; align-items: center; justify-content: center; background: var(--ink-2); }
.work-card.placeholder .ph-inner { text-align: center; color: var(--ash-dark); padding: 2rem; }
.work-card.placeholder .ph-inner .cat { color: var(--gold-dim); }

@media (max-width: 760px) { .work-grid { grid-template-columns: 1fr; } .work-card { cursor: pointer; } }

/* ── PHOTOGRAPHY GRID — masonry columns for personal work with mixed
   aspect ratios; not cropped/uniform like client work-cards. Three columns
   on desktop (not two) — at two columns each image ran wide enough that
   mismatched aspect ratios between adjacent items threw the columns out
   of sync and left ragged gaps at the bottom of every row; three smaller
   columns reads as a considered contact sheet instead of a blown-up dump,
   and the unevenness between columns shrinks proportionally too. ──────── */
/* ── PHOTO GRID — real aligned CSS Grid instead of CSS-columns masonry
   (uneven row heights, no horizontal alignment between columns). Uniform
   4:5 crop + hover-reveal caption, same device proven on the wedding page's
   #wedding-grid redesign — every row lines up edge to edge instead of a
   wall of different-height columns. wedding.html overrides this entirely
   via the higher-specificity #wedding-grid ID, so this only touches
   photography.html's categorized grids. */
.photo-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(6px, 0.8vw, 14px); }
.photo-item { position: relative; margin: 0; overflow: hidden; }
.photo-media { position: relative; aspect-ratio: 4 / 5; overflow: hidden; }
.photo-item img { width: 100%; height: 100%; object-fit: cover; display: block; filter: saturate(0.97); transition: transform 0.7s var(--ease); }
.photo-item:hover img { transform: scale(1.045); }
.photo-item figcaption { position: absolute; left: 0; right: 0; bottom: 0; margin: 0; padding: 1.3rem 1.1rem 1rem; background: linear-gradient(180deg, transparent 0%, rgba(10,10,9,0.72) 100%); color: var(--white); font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.05em; opacity: 0; transform: translateY(6px); transition: opacity 0.3s var(--ease), transform 0.3s var(--ease); pointer-events: none; }
.photo-item:hover figcaption { opacity: 1; transform: translateY(0); }
@media (hover: none) {
    .photo-item figcaption { position: static; opacity: 1; transform: none; background: none; color: var(--ash-dark); padding: 0.6rem 0 0; }
}
.section.on-paper .photo-item figcaption { color: var(--white); }
.film-tag {
    position: absolute; top: 12px; left: 12px; z-index: 2;
    display: flex; align-items: center; gap: 0.45em;
    font-family: var(--mono); font-size: 0.62rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--white); background: rgba(20,19,17,0.55); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
    border: 1px solid rgba(250,248,243,0.22); border-radius: 999px; padding: 0.32rem 0.65rem 0.32rem 0.55rem;
    --gold: var(--gold-bright); /* chip background is always dark regardless of section, so pin to the dark-safe accent */
}
.film-tag .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--gold); flex: none; }
@media (max-width: 1000px) { .photo-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 700px) { .photo-grid { grid-template-columns: 1fr; } }

/* ── SOCIAL CAROUSEL — auto-panning endless loop (not scroll-snap +
   arrow/dot nav) — same duplicated-content marquee technique as the
   homepage brand marks: slides are rendered twice back to back and the
   track animates exactly -50% of its own width, so the loop point is
   invisible. Edge mask fades the first/last slide rather than cutting
   them off hard. Paused on hover so a frame can actually be looked at. ── */
.fc-carousel { position: relative; }
.fc-mask { overflow: hidden; width: 100%; -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%); mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%); }
.fc-track { display: flex; gap: 14px; width: max-content; animation: fc-pan 48s linear infinite; }
.fc-mask:hover .fc-track { animation-play-state: paused; }
.fc-slide { flex: none; width: min(60vw, 400px); aspect-ratio: 1/1; overflow: hidden; background: var(--ink-2); }
.fc-slide img { width: 100%; height: 100%; object-fit: cover; display: block; }
@keyframes fc-pan { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .fc-track { animation: none; } }

/* ── CURSOR-FOLLOW LABEL — replaces the default pointer over work cards
   with a small "View Project" tag that tracks the cursor, only on
   pointer-fine devices. ─────────────────────────────────────────────── */
.cursor-tag {
    position: fixed; top: 0; left: 0; z-index: 500; pointer-events: none;
    transform: translate(-50%, -50%) scale(0.7); opacity: 0;
    background: var(--white); color: var(--ink); font-family: var(--mono);
    font-size: 0.68rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
    padding: 0.55rem 0.95rem; white-space: nowrap; border-radius: 999px;
    transition: opacity 0.25s var(--ease), scale 0.25s var(--ease);
    will-change: transform;
}
.cursor-tag.visible { opacity: 1; transform: translate(-50%, -50%) scale(1); }
@media (hover: none), (pointer: coarse) { .cursor-tag { display: none; } }

.filter-row { display: flex; gap: 0.6rem; margin-bottom: 2.6rem; flex-wrap: wrap; }
.filter-btn { font-size: 0.72rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; padding: 0.6rem 1.1rem; border: 1px solid var(--line); border-radius: 999px; color: var(--ash); transition: all 0.25s var(--ease); }
.filter-btn:hover { color: var(--white); }
.filter-btn.active { background: var(--white); border-color: var(--white); color: var(--ink); }

/* ── FORM ────────────────────────────────────────────────────────── */
.field { margin-bottom: 1.6rem; }
.field label { display: block; font-size: 0.7rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ash); margin-bottom: 0.6rem; }
.field input, .field select, .field textarea {
    width: 100%; background: transparent; border: none; border-bottom: 1px solid var(--line);
    color: var(--white); font-family: var(--sans); font-size: 1.05rem; font-weight: 300;
    padding: 0.7rem 0; transition: border-color 0.3s var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--gold); }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { outline: 2px solid var(--gold); outline-offset: 4px; }
.field textarea { resize: vertical; min-height: 110px; }
.field select { appearance: none; -webkit-appearance: none; cursor: pointer; }
.field select option { background: var(--ink); color: var(--white); }
#form-status { margin-top: 1.2rem; font-size: 0.9rem; }
#form-status.ok { color: #7FBF8F; }
#form-status.err { color: #D97A6C; }

/* ── BRAND MARKS — benchmarked against instrument.com's client-roster
   section: a short row of flat, borderless marks with generous whitespace
   reads more premium than a dense logo wall. Restraint is the point —
   every mark held to the same height (not width) so a tiny icon (PRPL)
   and a wide wordmark (Scale) sit at equal visual weight in the row, the
   way a printed masthead would set a row of credits. No visible names —
   the marks carry it alone; `title` on each still gives screen readers
   and hover a label. Shared (not page-scoped) since it's used on the
   homepage. ─────────────────────────────────────────────────────────── */
/* ── MARQUEE — each row is an endless, autoplaying carousel rather than a
   static wrapped line: row 1 drifts left→right, row 2 the opposite, row 3
   back to left→right, so the three rows read as counter-moving lanes
   instead of one flat wall of logos. The track's content is duplicated
   once by renderBrandMarks() and animated exactly -50% of its own width —
   since the second half is an identical copy of the first, the loop point
   is invisible and the scroll never has to "reset." Paused on hover so a
   name can actually be read, and stopped entirely under reduced-motion. ── */
.marks-row-mask { overflow: hidden; width: 100%; margin-bottom: clamp(28px, 4vw, 44px); -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%); mask-image: linear-gradient(90deg, transparent 0, #000 6%, #000 94%, transparent 100%); opacity: 0; transition: opacity 0.7s var(--ease); }
.marks-row-mask:last-child { margin-bottom: 0; }
.marks-row-mask.visible { opacity: 1; }
.marks-row { display: flex; align-items: center; width: max-content; column-gap: clamp(32px, 5vw, 64px); animation: marquee-ltr 42s linear infinite; }
.marks-row-mask:hover .marks-row { animation-play-state: paused; }
.marks-row.dir-rtl { animation-name: marquee-rtl; }
.marks-row img { height: clamp(22px, 2.6vw, 30px); width: auto; max-width: 130px; object-fit: contain; opacity: 0.62; transition: opacity 0.3s var(--ease); filter: grayscale(1); flex: none; }
.marks-row img:hover { opacity: 1; }
.section.on-paper .marks-row img { opacity: 0.72; }
@keyframes marquee-ltr { from { transform: translateX(-50%); } to { transform: translateX(0%); } }
@keyframes marquee-rtl { from { transform: translateX(0%); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .marks-row { animation: none; } }

/* ── AUTOPLAY FALLBACK BUTTON — shared across every ambient hero/vignette
   video sitewide (index/about/services headers, project heroes, etc). See
   setupAmbientAutoplayFallbackButton() in main.js. Invisible while the
   video is genuinely playing; a paused parent (autoplay failed for any
   reason) reveals a real, tappable play control instead of the video
   silently looking like a static, broken image. */
.autoplay-fallback { position: absolute; inset: 0; margin: auto; width: 64px; height: 64px; border-radius: 50%; background: rgba(20,19,17,0.55); border: 1px solid rgba(250,248,243,0.35); color: var(--white); display: flex; align-items: center; justify-content: center; cursor: pointer; opacity: 0; pointer-events: none; transition: opacity 0.25s var(--ease), background 0.25s var(--ease); z-index: 5; }
.autoplay-fallback svg { width: 24px; height: 24px; margin-left: 3px; }
.autoplay-fallback:hover { background: rgba(20,19,17,0.8); border-color: var(--gold); }
.video-paused .autoplay-fallback { opacity: 1; pointer-events: auto; }

/* ── QUOTE BLOCK — a single italic pull-statement, oversized opening
   quote mark as the only decoration. Shared (used on index.html and
   about.html) rather than page-scoped, since it's a reusable device for
   any page that wants one considered philosophy line instead of another
   full section of prose. ─────────────────────────────────────────────── */
.quote-block { max-width: 66ch; position: relative; padding-left: clamp(0px, 4vw, 48px); }
.quote-block .q { font-family: var(--serif); font-size: clamp(1.6rem, 3.2vw, 2.5rem); font-weight: 400; line-height: 1.32; font-style: italic; text-wrap: balance; }
.quote-block .attr { margin-top: 1.6rem; font-family: var(--mono); font-size: 0.74rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold); }
.quote-block .mark { position: absolute; left: -0.1em; top: -0.5em; font-family: var(--serif); font-size: 5rem; font-style: italic; color: var(--gold-dim); line-height: 1; user-select: none; }
@media (max-width: 700px) { .quote-block { padding-left: 0; } .quote-block .mark { display: none; } }
