/* ==========================================================================
   Bahía Photographer — full-bleed
   Photographs are the ONLY background on this site. There is no cream, no
   white card, no tinted band: every screenful is a photograph and the text
   sits on top of it. (User decision 2026-09-10 — "photos sell the products".)

   ⛔ INVERTED 2026-09-12 (Adam: the site read too dark): the ground is WHITE,
   the type is BLACK, and every scrim is a WHITE wash/gradient instead of a black
   one. Legibility is still the whole engineering problem, only mirrored: black
   type over a DARK photograph (this wedding is mostly night work) needs a strong
   white gradient — .80 at the foot after Adam asked for it softened (was .92) — which washes the bottom of the frame more
   than the old black one did. Every text panel still carries a TWO-PART scrim:
   a full-panel wash plus a stronger gradient under the text. Do not "clean up"
   by removing one of them, and re-measure contrast after any change.
   ========================================================================== */

/* Titles stay Instrument Serif. Body is now EB Garamond — a text-optimised serif of
   the same old-style family, so running copy reads as the same voice as the headings
   instead of switching to a sans (Adam's request 2026-09-10).
   ⛔ Garamond runs small and light. The size and weight below are raised deliberately:
   a 300-weight serif at default size is not legible over a photograph. */
/* Self-hosted fonts (2026-09-14, Adam) — no request to Google, so the privacy notice needs no Google Fonts line.
   Subsets kept: latin, latin-ext, cyrillic, cyrillic-ext (Instrument Serif has no Cyrillic; Russian headings fall back to Georgia, as before). */
@font-face {
  font-family: 'EB Garamond';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/EBGaramond-400i-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/EBGaramond-400i-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/EBGaramond-400i-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/EBGaramond-400i-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/EBGaramond-400-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/EBGaramond-400-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/EBGaramond-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/EBGaramond-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/EBGaramond-500-cyrillic-ext.woff2) format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/EBGaramond-500-cyrillic.woff2) format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/EBGaramond-500-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(fonts/EBGaramond-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/InstrumentSerif-400i-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/InstrumentSerif-400i-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/InstrumentSerif-400-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Instrument Serif';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(fonts/InstrumentSerif-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root{
  --ink:#12110f; --paper:#faf8f5; --muted:rgba(18,17,15,.72);   /* --paper IS the ground: body, mosaic and every vignette fade to it */
  --line:rgba(18,17,15,.18); --accent:#8a6a12;   /* gold darkened: #c9a227 on white is 2.4:1 */
  --nav-h:74px; --max:1360px;
  --pan:100svh;                 /* svh, not vh — vh jumps on mobile URL-bar show/hide */
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  font-family:'EB Garamond',Georgia,'Times New Roman',serif;
  font-weight:400; font-size:1.09rem; line-height:1.6; color:var(--ink);
  background:var(--paper);      /* warm paper: the gallery frame and the ground behind a loading photograph */
  overflow-x:hidden;
}
img{display:block;max-width:100%}
a{color:inherit}
h1,h2,h3,h4{font-family:'Instrument Serif',Georgia,serif;font-weight:400;line-height:1.14;letter-spacing:-.01em}
h1{font-size:clamp(2.6rem,6.4vw,5.4rem)}
h2{font-size:clamp(2rem,4.4vw,3.5rem)}
h3{font-size:clamp(1.25rem,2vw,1.6rem)}
em{font-style:italic}
::selection{background:var(--accent);color:#fff}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px}

/* ── NAV ─────────────────────────────────────────────────────────────────
   Always white: it only ever sits over a photograph now. The old
   `body.has-hero` switch is gone because there is no non-photo state left. */
nav{
  position:fixed;top:0;left:0;right:0;height:var(--nav-h);z-index:90;
  display:flex;align-items:center;justify-content:space-between;
  padding:0 clamp(18px,3.4vw,42px);
  color:var(--ink);transition:background .3s ease,backdrop-filter .3s ease;
}
nav::before{ /* keeps the wordmark readable over a bright sky */
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:linear-gradient(rgba(250,248,245,.66),rgba(250,248,245,0));
}
nav.scrolled{background:rgba(250,248,245,.88);backdrop-filter:blur(14px)}
nav.scrolled::before{opacity:0}
.nav-logo{display:flex;align-items:center;text-decoration:none}
/* ⛔ The mark is an <img>, so the halo that kept the old TEXT wordmark readable over a
   bright sky has to come from drop-shadow — text-shadow does nothing to an image. */
.nav-logo img{display:block;height:46px;width:auto;filter:drop-shadow(0 1px 10px rgba(250,248,245,.9))}
.nav-links{display:flex;align-items:center;gap:clamp(16px,2.2vw,30px);list-style:none}
.nav-links a{
  text-decoration:none;font-size:.84rem;letter-spacing:.16em;text-transform:uppercase;
  opacity:.9;text-shadow:0 1px 10px rgba(250,248,245,.80);transition:opacity .2s;
}
.nav-links a:hover,.nav-links a[aria-current="page"]{opacity:1;color:var(--accent)}
.nav-right{display:flex;align-items:center;gap:18px}
.lang-toggle{display:flex;align-items:center;gap:6px}
.lang-btn{
  background:none;border:0;cursor:pointer;color:var(--ink);opacity:.6;
  font:inherit;font-size:.8rem;letter-spacing:.12em;padding:2px;
  text-shadow:0 1px 10px rgba(250,248,245,.80);
}
.lang-btn.active{opacity:1;color:var(--accent)}
/* The language items are links since 2026-09-17 (one URL per language), not buttons. */
a.lang-btn{text-decoration:none;color:var(--ink)}
a.lang-btn.active{color:var(--accent)}
.lang-sep{opacity:.45;font-size:.7rem}
/* Compact language switcher (2026-09-13, five languages): the current code, a dropdown with the names. */
.lang-toggle{position:relative}
.lang-current{background:none;border:0;cursor:pointer;color:var(--accent);font:inherit;font-size:.8rem;letter-spacing:.12em;
  padding:6px 2px;display:inline-flex;align-items:center;gap:6px;text-shadow:0 1px 10px rgba(250,248,245,.80)}
.lang-current::after{content:"";width:6px;height:6px;border-right:1.5px solid currentColor;border-bottom:1.5px solid currentColor;
  transform:translateY(-2px) rotate(45deg);transition:transform .2s}
.lang-toggle.open .lang-current::after{transform:translateY(1px) rotate(225deg)}
.lang-menu{display:none;position:absolute;right:0;top:calc(100% + 10px);list-style:none;min-width:150px;padding:6px 0;
  background:rgba(250,248,245,.97);backdrop-filter:blur(14px);box-shadow:0 8px 28px rgba(18,17,15,.14);border:1px solid var(--line);z-index:95}
.lang-toggle.open .lang-menu{display:block}
.lang-menu .lang-btn{display:block;width:100%;text-align:left;padding:9px 18px;font-size:.9rem;letter-spacing:.04em;opacity:.75;text-shadow:none}
.lang-menu .lang-btn:hover,.lang-menu .lang-btn.active{opacity:1}
/* When the links would wrap (measured in lang.js), the header uses the phone menu at any width. Mirrors the 980px block. */
nav.collapsed .nav-links{position:fixed;top:var(--nav-h);left:0;right:0;flex-direction:column;gap:0;background:rgba(250,248,245,.96);
  backdrop-filter:blur(16px);max-height:0;overflow:hidden;transition:max-height .3s ease}
nav.collapsed .nav-links.open{max-height:70vh}
nav.collapsed .nav-links li{width:100%;border-bottom:1px solid rgba(18,17,15,.10)}
nav.collapsed .nav-links a{display:block;padding:17px 26px;font-size:.85rem}
nav.collapsed .nav-burger{display:block}
.nav-burger{display:none;background:none;border:0;cursor:pointer;width:28px;height:20px;position:relative}
.nav-burger span{position:absolute;left:0;width:100%;height:1.5px;background:var(--ink);transition:.25s}
.nav-burger span:nth-child(1){top:2px}
.nav-burger span:nth-child(2){top:9px}
.nav-burger span:nth-child(3){top:16px}
/* ⛔ 8 menu items (Albums added 2026-09-13) wrapped onto two lines between 981px and ~1200px — and 7 already
   wrapped below ~1100px. ⛔ It must sit AFTER the base .nav-links a / .nav-logo rules — placed before them
   (same specificity) the font-size and letter-spacing were silently overridden and it still wrapped at 1024px. Tighter spacing there keeps one line down to the 980px burger breakpoint
   (measured: 1 line at 990/1024/1100/1150/1200/1240). */
@media (min-width:981px) and (max-width:1240px){.nav-links{gap:14px}.nav-links a{letter-spacing:.1em;font-size:.78rem}.nav-logo img{height:42px}}

/* ── PANEL: one photograph, one screen ───────────────────────────────── */
.pan{position:relative;min-height:var(--pan);display:grid;overflow:hidden;isolation:isolate}
.pan-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:var(--pos,center 38%);z-index:-2}
.pan > picture{display:contents}   /* the <img> stays positioned against its panel */
/* ⛔ Portrait screens show the panel's PORTRAIT companion (<source>), with its own focal point. */
@media (orientation:portrait){.pan-img{object-position:var(--pos-tall,var(--pos,center 38%))}}
/* ⛔ Panel film removed too (Adam, 2026-09-16) — same reason as the frames. The scrim element
   stays (it also carries the paper vignette on framed panels); only its flat .14 wash is gone.
   Text legibility is NOT affected: that comes from .pan-body::before, the gradient under the
   text block, which is untouched. */
.pan-scrim{position:absolute;inset:0;z-index:-1;pointer-events:none}
.pan-body{
  position:relative;align-self:end;
  width:100%;max-width:var(--max);margin:0 auto;
  padding:0 clamp(20px,4vw,54px) clamp(64px,10vh,130px);
}
.pan-body::before{ /* the second half of the scrim — sits only under the text */
  content:"";position:absolute;left:0;right:0;bottom:0;top:-14vh;z-index:-1;pointer-events:none;
  background:linear-gradient(rgba(250,248,245,0),rgba(250,248,245,.50) 38%,rgba(250,248,245,.80));
}
.pan-center .pan-body{align-self:center;text-align:center;padding-bottom:clamp(40px,6vh,80px)}
/* ⛔ The centred-title wash MUST fade to zero at its own edges. It used to stop at .16, which left a
   hard-edged lighter band — and once .pan-body::before was stretched to full width for the framed
   panels, that band ran across the whole screen (Adam spotted it on galleries.html, 2026-09-13).
   `closest-side` makes the ellipse touch the box edges, so no edge of the box is ever visible. */
.pan-center .pan-body::before{top:-22vh;bottom:-10vh;background:radial-gradient(ellipse closest-side at center,rgba(250,248,245,.66),rgba(250,248,245,.34) 55%,rgba(250,248,245,0) 100%)}
.pan-tight{min-height:auto}
/* ── SOLO: ONE photograph behind a whole page ─────────────────────────────
   About is a single photograph of Adam with all the text over it, rather than a
   sequence of panels drawn from the portfolio (his call 2026-09-10 — the About page
   should be him, not client work).
   ⛔ The image is position:FIXED, not absolute. His photographs are 1100-1280px, and
   stretching one over a 3000px-tall section would upscale it ~3x and show every
   artefact. Fixed means it only ever covers ONE viewport, so the upscale stays at
   roughly 2x and the photograph holds together.
   ⛔ position:fixed on an <img>, NOT background-attachment:fixed — the latter is
   broken on iOS Safari; a fixed positioned element is not. */
.pan-solo{min-height:100svh}
.pan-solo .pan-img{position:fixed}
.pan-solo .pan-scrim{position:fixed}
.pan-solo .pan-body{align-self:start;padding-top:clamp(96px,16vh,170px)}
/* ⛔ Lighter than the panel scrim ON PURPOSE, and the number is measured not guessed.
   The panel gradient (.30/.62/.86) put this page at contrast 9.2 against a floor of 3.0
   — three times more scrim than legibility needs, which flattened the portrait to a
   silhouette. At .12/.34/.52 the worst pixel under the text still measures 4.04 and the
   photograph is roughly twice as bright.
   ⛔ RE-MEASURE IF ABOUT_BG CHANGES. The figure is for that one photograph; a brighter
   background (open sky, sand) will score lower and may need the heavier gradient back. */
.pan-solo .pan-scrim{background:rgba(250,248,245,.08)}
.pan-solo .pan-body::before{top:-10vh;background:linear-gradient(rgba(250,248,245,.12),rgba(250,248,245,.38) 30%,rgba(250,248,245,.56))}
/* ⛔ The wash had hard LEFT, RIGHT and BOTTOM edges — a pale rectangle the width of the text column
   floating over Adam's photograph (seen 2026-09-13/14, same class of bug as the Galleries band).
   It now spans the whole viewport and is MASKED to zero on every side: fully opaque under the text
   column, fading out to the right before his face, and fading at top and bottom inside the body's own
   padding so no text line sits in a fade. Two masks intersected (mask-composite), because opacity
   along both axes cannot be one gradient. Contrast under the text is unchanged: the mask is 100%
   wherever there is text. */
.pan-solo .pan-body::before{left:calc(50% - 50vw);right:calc(50% - 50vw);
  --wash-solid:max(52%,calc(50% - var(--max)/2 + 760px));
  -webkit-mask-image:linear-gradient(to right,#000 var(--wash-solid),transparent calc(var(--wash-solid) + 380px)),
    linear-gradient(to bottom,transparent,#000 10vh,#000 calc(100% - clamp(64px,10vh,130px)),transparent);
  -webkit-mask-composite:source-in;
  mask-image:linear-gradient(to right,#000 var(--wash-solid),transparent calc(var(--wash-solid) + 380px)),
    linear-gradient(to bottom,transparent,#000 10vh,#000 calc(100% - clamp(64px,10vh,130px)),transparent);
  mask-composite:intersect}
.pan-solo .copy+.copy{margin-top:clamp(38px,6vh,64px)}
.copy{max-width:62ch}
.pan-center .copy{margin:0 auto}
.eyebrow{
  font-size:.78rem;letter-spacing:.26em;text-transform:uppercase;
  color:var(--accent);margin-bottom:18px;
}
.lede{font-size:clamp(1.02rem,1.5vw,1.22rem);color:var(--muted);margin-top:20px}
.copy p+p{margin-top:16px}
.copy h2+p,.copy h1+p{margin-top:18px}

/* ── BUTTONS ─────────────────────────────────────────────────────────── */
.btn{
  display:inline-block;margin-top:28px;padding:14px 30px;text-decoration:none;
  font-size:.82rem;letter-spacing:.18em;text-transform:uppercase;
  border:1px solid var(--line);transition:.22s;backdrop-filter:blur(3px);
}
.btn-solid{background:var(--ink);color:#fff;border-color:var(--ink)}
.btn-solid:hover{background:var(--accent);border-color:var(--accent);color:#000}
.btn-ghost{background:rgba(250,248,245,.52);color:var(--ink)}
.btn-ghost:hover{background:var(--ink);color:#fff;border-color:var(--ink)}
.btn+.btn{margin-left:12px}

/* ── MOSAIC: several photographs filling one screen ──────────────────── */
/* ⛔ HEAVY OVAL VIGNETTE (Adam, 2026-09-12). The fade is a MASK, not a white overlay:
   the tile becomes transparent at the edge and the page's own ground shows through, so the
   vignette can never disagree with the background. That is why --paper must stay the single
   definition of the ground — a hardcoded #fff here would halo against the warm paper.
   ⛔ Each tile keeps its OWN height (height:auto + align-self:start), so the oval takes the
   PHOTOGRAPH's shape: square -> circle, portrait -> upright oval, landscape -> wide oval.
   Before this, height:100% made every tile fill its grid row, and a row is as tall as its
   tallest member — so a landscape frame sharing a row with portraits was stretched and
   object-fit:cover cropped it to portrait (measured 2026-09-12: 7 of 10 landscapes cropped,
   the other 3 intact only because they happened to share a row). ⛔ `align-content:start` on
   .mosaic is load-bearing: with min-height:var(--pan) the default stretch would re-inflate
   the rows and undo this.
   ⛔ The radii must stay <= 50%, i.e. an ellipse INSCRIBED in the tile. At 66% the gradient
   still had alpha 0.40 at the middle of every edge, so the photograph's straight edges stayed
   visible and it read as a rectangle with soft corners. At 49% the fade reaches zero at every
   point on the box boundary, so no edge survives anywhere (corner alpha was already 0 — the
   corners are never the problem, the EDGE MIDPOINTS are).
   ⛔ The mask is scoped to .mosaic .shot. The lightbox builds its own <img>, so the
   click-through full-size view is unmasked — verified 2026-09-12. */
.mosaic{display:grid;gap:44px;padding:44px;min-height:var(--pan);grid-template-columns:repeat(3,1fr);background:var(--paper);align-content:start}
.mosaic .shot{width:100%;height:auto;align-self:start;object-fit:cover;object-position:var(--pos,center);cursor:zoom-in;min-height:0;
  -webkit-mask-image:radial-gradient(ellipse 49% 49% at 50% 50%,#000 58%,rgba(0,0,0,0) 100%);
          mask-image:radial-gradient(ellipse 49% 49% at 50% 50%,#000 58%,rgba(0,0,0,0) 100%)}
.mosaic-2{grid-template-columns:repeat(2,1fr)}
.mosaic-4{grid-template-columns:repeat(4,1fr)}
/* ── PARTNERS (Adam, 2026-09-13): name, role, area, link — grouped by role ── */
.partners{max-width:none}
.partner-group + .partner-group{margin-top:clamp(26px,4vh,40px)}
.partner-group h2{font-size:clamp(1.5rem,2.6vw,2.1rem);margin-bottom:12px}
.partner-list{list-style:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:10px 34px}
.partner-list li{display:flex;flex-direction:column;gap:2px}
.partner-list a,.partner-name{font-size:1.12rem;text-decoration:none;border-bottom:1px solid var(--line);align-self:start}
.partner-list a:hover{color:var(--accent);border-color:var(--accent)}
.partner-area{font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:var(--muted)}
/* placeholders while a role has nobody listed yet — they name no one */
.partner-soon{list-style:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:18px 34px}
.partner-group + .partner-soon{margin-top:clamp(26px,4vh,40px)}
.partner-soon li{display:flex;flex-direction:column;gap:3px;padding-top:12px;border-top:1px solid var(--line)}
.soon-role{font-family:'Instrument Serif',Georgia,serif;font-size:clamp(1.25rem,2vw,1.6rem);line-height:1.15}
.soon-note{font-size:.76rem;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);font-style:italic}
/* ⛔ No hover film (Adam, 2026-09-16): hovering used to fade the photo to .86 toward the paper,
   i.e. another white wash. The 'Full photo' label is the whole affordance now. */
.shot{transition:opacity .25s}

/* ── FRAMES: every photograph full size, white-washed, in a rounded white vignette ──
   Adam, 2026-09-13 — replaces the oval tiles (which now survive only on the guest album,
   whose markup album.py still writes as .mosaic). Applied to text panels AND gallery photos:
   1. a flat .14 white wash (today's .pan-scrim value);
   2. a RECTANGULAR vignette with rounded corners: an inset box-shadow in var(--paper) that
      follows border-radius; the spread makes every edge end exactly on the ground colour;
   3. the box's own border-radius + overflow:hidden, so the photo's square corners are clipped
      — an inset shadow alone follows the radius but the photo would still poke out past it;
   4. white space (--gap) between one photograph's bottom and the next one's top.
   ⛔ .pan-solo (About) is excluded: its photo and scrim are position:fixed, and a rounded
   fixed scrim would frame the viewport instead of the section.
   ⛔ A gallery frame is sized to the photograph's OWN shape — width min(100%, screen height x
   ratio) — so a portrait is shown whole, never cropped to fill a landscape screen.
   ⛔ Colour of the wash and vignette is the paper token, never a literal white. */
:root{--frame-r:5vmin; --fade:10vmin; --gap:5vh}   /* gap 12vh -> 5vh: Adam asked for less, 2026-09-13 */
.pan:not(.pan-solo){border-radius:var(--frame-r)}
/* ⛔ The text wash (.pan-body::before) spans only the --max text column, so on a screen wider
   than 1360px it ended in two hard vertical lines ~40px inside the panel — invisible before,
   obvious once the panel edges fade to white. Stretch it to the full panel width. */
.pan:not(.pan-solo) .pan-body::before{left:calc(50% - 50vw);right:calc(50% - 50vw)}
.pan:not(.pan-solo) > .pan-scrim{border-radius:inherit;
  box-shadow:inset 0 0 var(--fade) calc(var(--fade) * .25) var(--paper)}
.pan + .pan,.pan + .stack,.stack + .pan,.row + .row{margin-top:var(--gap)}
/* ROWS (Adam, 2026-09-13): one landscape, or two vertical/square photographs side by side.
   On a landscape screen a row is as tall as the screen or as the width allows, whichever is
   smaller: --h = min(screen height, (stack width - gaps) / sum of the photos' ratios).
   ⛔ 100cqw (the stack's own width via container-type) — not 100vw, which counts the scrollbar.
   Portrait screens (phones, upright tablets) keep ONE photograph per row. */
.stack{display:block;container-type:inline-size}
.row{display:flex;flex-direction:column;align-items:center;gap:var(--gap)}
@media (orientation:landscape){
  .row{flex-direction:row;justify-content:center;
    --h:min(var(--pan),calc((100cqw - var(--gap) * (var(--n) - 1)) / var(--sum)))}
  .row .frame{margin:0;flex:none;width:calc(var(--h) * var(--r))}
}
.frame{position:relative;margin:0 auto;width:min(100%,calc(var(--pan) * var(--r)));aspect-ratio:var(--r);
  overflow:hidden;border-radius:var(--frame-r);cursor:zoom-in;
  /* Skip painting frames that are off screen — the blurred inset shadow is the expensive part
     of a 123-photo gallery on a slow phone. The width stays explicit and the height comes from
     aspect-ratio, so the page length does not change when a frame is skipped. */
  content-visibility:auto;contain-intrinsic-size:auto 100svh}
/* ── BACKGROUND PHOTOS ARE CLICKABLE (Adam, 2026-09-13) ─────────────────────────────
   The text layers let clicks fall through to the photograph; only links and form controls
   stay clickable. ⛔ Keep the form controls in that list or the contact form stops working. */
.pan-img{cursor:zoom-in}
.pan-body,.pan-end .end-cta,.pan > footer{pointer-events:none}
.pan a,.pan button,.pan input,.pan select,.pan textarea,.pan label{pointer-events:auto}
/* "Full photo" label on every photograph. ⛔ pointer-events:none — a tap on it opens the photo. */
.view-hint{position:absolute;z-index:2;right:clamp(14px,2.2vw,28px);display:inline-flex;align-items:center;gap:7px;
  padding:7px 13px 7px 10px;border-radius:999px;background:rgba(250,248,245,.88);color:var(--ink);
  font-size:.7rem;line-height:1;letter-spacing:.16em;text-transform:uppercase;pointer-events:none;
  box-shadow:0 1px 10px rgba(18,17,15,.14);transition:background .2s,color .2s}
.view-hint svg{width:13px;height:13px;flex:none}
.frame .view-hint{top:clamp(14px,2.2vw,28px)}
.pan > .view-hint{top:calc(var(--nav-h) + 10px)}
.frame:hover .view-hint,.pan-img:hover ~ .view-hint,.pan > picture:hover ~ .view-hint{background:var(--ink);color:var(--paper)}
/* ⛔ SATURATION LIFT REMOVED (Adam, 2026-09-16). The +15% existed only to give back the colour the
   .14 white film muted (added 2026-09-13, chosen from a +10/+20/+30 comparison). The film was removed
   the same day the lift was, so the compensation had nothing left to compensate for and the page was
   showing colour STRONGER than the delivered files. The page now matches the export exactly, like the
   lightbox always did. `--sat` still works: set it to 1.15 on :root to restore the old look. */
.frame .shot,.pan-img{filter:saturate(var(--sat,1))}
.frame .shot{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:var(--pos,center)}
/* ⛔ NO FILM (Adam, 2026-09-16). The flat rgba(250,248,245,.14) wash over every photograph was
   REMOVED — a photographer friend read it as low contrast. The soft edge stays: the inset paper
   shadow still fades each frame into the ground, so photos are not hard rectangles. Text panels
   (.pan-scrim + .pan-body::before) are UNCHANGED — their scrim is legibility, not decoration.
   The lightbox was already filmless: "Full photo" shows the file as delivered. */
.frame::after{content:"";position:absolute;inset:0;pointer-events:none;border-radius:inherit;
  box-shadow:inset 0 0 var(--fade) calc(var(--fade) * .25) var(--paper)}

/* ── FORM (sits on a photograph) ─────────────────────────────────────── */
.form{display:grid;gap:18px;max-width:620px}
.field{display:grid;gap:7px}
.field .field-label{font-size:.78rem;letter-spacing:.15em;text-transform:uppercase;color:var(--muted)}
.field input,.field select,.field textarea{
  font:inherit;color:var(--ink);padding:13px 15px;
  background:rgba(250,248,245,.72);border:1px solid var(--line);border-radius:0;
  backdrop-filter:blur(6px);
}
.field input::placeholder,.field textarea::placeholder{color:rgba(18,17,15,.42)}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--accent)}
.field select option{background:#faf8f5;color:var(--ink)}
.consent{display:flex;gap:11px;align-items:flex-start;font-size:.85rem;color:var(--muted)}
.hp{position:absolute;left:-9999px}

/* ── CLOSING PANEL: call to action + footer, over ONE photograph ──────
   ⛔ The footer used to carry its own image. At ~300px tall that cropped every
   photograph to a meaningless strip, on every page. It is now transparent and
   sits at the foot of the final full-screen panel. Do not give it a photo back. */
.pan-end{display:grid;grid-template-rows:1fr auto;align-content:stretch}
.pan-end .end-cta{
  align-self:center;text-align:center;max-width:62ch;
  margin:0 auto;padding:clamp(90px,14vh,150px) clamp(20px,4vw,54px) 0;position:relative;
}
.pan-end::after{ /* one gradient covering CTA and footer together */
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:linear-gradient(rgba(250,248,245,.10) 0%,rgba(250,248,245,.42) 45%,rgba(250,248,245,.82) 100%);
}
footer{position:relative;padding:clamp(44px,7vh,80px) clamp(20px,4vw,54px) 30px;background:none}
.foot-grid{display:grid;grid-template-columns:1.5fr 1fr 1fr;gap:38px;max-width:var(--max);margin:0 auto 34px}
.foot-grid-4{grid-template-columns:1.4fr 1fr 1fr 1fr}   /* + "Where I work" (published location pages) */
.foot-logo{font-family:'Instrument Serif',serif;font-size:1.15rem;letter-spacing:.17em;text-transform:uppercase}
/* The primary lockup set as TYPE rather than as an image: same two faces, same gold rule.
   The footer can carry it because it has the room the nav does not — see brand/README.md. */
.foot-logo + .foot-name{font-size:.72rem;letter-spacing:.34em;text-transform:uppercase;color:var(--accent);
  padding-top:9px;margin-top:8px;margin-bottom:12px;border-top:1px solid var(--accent);display:inline-block}
footer h4{font-size:.78rem;letter-spacing:.17em;text-transform:uppercase;color:var(--accent);margin-bottom:14px}
footer ul{list-style:none;display:grid;gap:9px;font-size:.9rem}
footer a{text-decoration:none;color:var(--muted);transition:color .2s}
footer a:hover{color:var(--ink)}
footer p{color:var(--muted);font-size:.9rem}
.foot-bottom{
  max-width:var(--max);margin:0 auto;border-top:1px solid var(--line);padding-top:20px;
  display:flex;justify-content:space-between;gap:18px;flex-wrap:wrap;font-size:.8rem;color:var(--muted);
}

/* ── LIGHTBOX ────────────────────────────────────────────────────────── */
.lb{position:fixed;inset:0;z-index:200;background:rgba(250,248,245,.97);display:none;place-items:center}
.lb.open{display:grid}
.lb img{max-width:94vw;max-height:90vh;object-fit:contain}
.lb-close,.lb-prev,.lb-next{
  position:absolute;background:none;border:0;color:var(--ink);cursor:pointer;
  font-size:2rem;line-height:1;padding:16px;opacity:.7;transition:opacity .2s;
}
.lb-close:hover,.lb-prev:hover,.lb-next:hover{opacity:1}
.lb-close{top:10px;right:16px}
.lb-prev{left:8px;top:50%;transform:translateY(-50%)}
.lb-next{right:8px;top:50%;transform:translateY(-50%)}
.lb-count{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);font-size:.78rem;letter-spacing:.14em;color:var(--muted)}

/* ── RESPONSIVE ──────────────────────────────────────────────────────── */
/* ⛔ The solo background needs MORE scrim on a phone, not less. A landscape viewport
   crops the photograph's height and the subject sits to one side of the text column;
   a portrait viewport crops its WIDTH instead, so the face fills the frame and every
   paragraph lands on top of it. Same photograph, opposite problem. */
@media (max-width:820px){
  .pan-solo .pan-scrim{background:rgba(250,248,245,.20)}
  .pan-solo .pan-body::before{background:linear-gradient(rgba(250,248,245,.36),rgba(250,248,245,.58) 30%,rgba(250,248,245,.72));
    /* phone: the text is full width, so only the top/bottom fades apply */
    -webkit-mask-image:linear-gradient(to bottom,transparent,#000 10vh,#000 calc(100% - 46px),transparent);
    mask-image:linear-gradient(to bottom,transparent,#000 10vh,#000 calc(100% - 46px),transparent)}
}

/* ── PRICE PACKAGES (theme pages) ─────────────────────────────────────── */
.copy.packages{max-width:none}
/* ⛔ .pan is a grid whose single auto column grew to the cards' min-content, so on a 390px phone the
   panel body measured 428px and the cards ran off-screen. min-width:0 lets the body shrink to the screen. */
.pan-body:has(> .packages){min-width:0}
.pkg-list{list-style:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(230px,100%),290px));gap:18px;margin-top:28px}
.pkg{background:rgba(250,248,245,.74);border:1px solid var(--line);padding:24px 26px 22px;backdrop-filter:blur(4px)}
.pkg h3{font-size:clamp(1.45rem,2.2vw,1.8rem)}
.pkg-dur{margin-top:6px;color:var(--muted);font-size:.86rem;letter-spacing:.08em;text-transform:uppercase}
.pkg-price{margin-top:16px;font-family:'Instrument Serif',Georgia,serif;font-size:clamp(1.7rem,2.6vw,2.2rem);line-height:1.1}
.pkg-note{margin-top:20px;color:var(--muted);font-size:.92rem;max-width:62ch}
/* Package detail pages (2026-09-14): card link, the included checklist, wide copy blocks. */
.pkg{display:flex;flex-direction:column}
.pkg-more{margin-top:16px;align-self:start;font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;text-decoration:none;
  color:var(--accent);border-bottom:1px solid currentColor;padding-bottom:2px}
.pkg-more:hover{color:var(--ink)}
.pkg-more::after{content:" →"}
.copy.pkg-wide{max-width:none}
.pan-body:has(> .pkg-wide){min-width:0}
.pkg-wide h2+.pkg-list{margin-top:24px}
.pkg-wide .btn-ghost{margin-top:30px}
.pkg-includes{list-style:none;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(300px,100%),1fr));gap:14px 42px;
  margin-top:26px;max-width:1040px;font-size:1.08rem}
.pkg-includes li{position:relative;padding-left:28px}
.pkg-includes li::before{content:"";position:absolute;left:0;top:.8em;width:14px;height:1px;background:var(--accent)}
/* ── PRE-BOOKING CALENDAR (availability.html, 2026-09-14) ─────────────────
   free = paper, on hold = amber stripes, booked = ink. Selection = gold outline. */
.pan-body:has(> .cal-wrap){min-width:0}
.cal-wrap{max-width:760px;background:rgba(250,248,245,.86);border:1px solid var(--line);padding:clamp(18px,3vw,34px);backdrop-filter:blur(6px);margin-top:26px}
.cal-top{display:grid;gap:8px}
.cal-q{font-size:.78rem;letter-spacing:.15em;text-transform:uppercase;color:var(--muted)}
.cal-top select{font:inherit;font-size:1.02rem;padding:12px 14px;border:1px solid var(--line);background:#fff;color:var(--ink);max-width:100%}
.cal-what{margin-top:14px;font-size:.95rem;color:var(--muted)}
.cal-notice{margin-top:14px;padding:10px 14px;background:#fbeed2;border-left:3px solid #b7791f}
.cal-legend{display:flex;flex-wrap:wrap;gap:16px;margin-top:20px;font-size:.85rem;color:var(--muted)}
.cal-legend .lg::before{content:"";display:inline-block;width:14px;height:14px;margin-right:7px;vertical-align:-2px;border:1px solid var(--line)}
.lg.free::before,.cal-day.free,.cal-slot.free{background:#fff}
.lg.hold::before,.cal-day.hold,.cal-slot.hold{background:repeating-linear-gradient(135deg,#f3dfb3 0 6px,#fbf1dc 6px 12px)}
.lg.booked::before,.cal-day.booked,.cal-slot.booked{background:var(--ink);color:rgba(255,255,255,.6)}
.cal-month{display:flex;align-items:center;justify-content:space-between;margin-top:18px}
.cal-month h3{text-transform:capitalize}
.cal-nav{background:none;border:1px solid var(--line);width:40px;height:40px;font-size:1.4rem;cursor:pointer;color:var(--ink)}
.cal-nav[disabled]{opacity:.3;cursor:default}
.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px;margin-top:12px}
.cal-wd{text-align:center;font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);padding-bottom:4px}
.cal-day{aspect-ratio:1;border:1px solid var(--line);font:inherit;font-size:1rem;cursor:pointer;color:var(--ink);min-width:0;padding:0}
.cal-day.off{background:none;border-color:transparent;color:rgba(18,17,15,.25);cursor:default}
.cal-day[disabled]{cursor:default}
.cal-day.free:not([disabled]):hover,.cal-slot.free:not([disabled]):hover{border-color:var(--accent)}
.cal-day.sel,.cal-slot.sel{outline:2px solid var(--accent);outline-offset:1px;font-weight:500}
.cal-hint{margin-top:12px;font-size:.9rem;color:var(--muted)}
.cal-times{margin-top:22px}
.cal-slots{display:grid;grid-template-columns:repeat(auto-fill,minmax(118px,1fr));gap:8px;margin-top:10px}
.cal-slot{border:1px solid var(--line);font:inherit;font-size:.92rem;padding:9px 6px;cursor:pointer;color:var(--ink)}
.cal-slot[disabled]{cursor:default}
.cal-form{margin-top:26px;border-top:1px solid var(--line);padding-top:22px}
.cal-form strong{color:var(--ink);letter-spacing:0;text-transform:none;font-size:1rem}
.cal-form .btn,.cal-done .btn{font-family:inherit;cursor:pointer}   /* <button> does not inherit the site font */
.cal-done h2{margin-top:6px}
.cal-done p+p{margin-top:12px}
@media (max-width:620px){.cal-grid{gap:3px}.cal-day{font-size:.9rem}}
/* Unconfirmed values in drafts — must never reach the live site (build.py warns). */
.draft{background:#ffe7a3;color:#5c4300;padding:0 .3em;font-style:italic}

/* ── WHATSAPP: floating contact button (Adam, 2026-09-14) ─────────────────
   Opens wa.me with a pre-filled message in the visitor's language (lang.js swaps href).
   ⛔ z-index 150: above panels, nav (90/95) and view hints, BELOW the lightbox (200) so it never
   sits on top of a photograph being viewed. Safe-area insets keep it off the iPhone home bar. */
/* ⛔ Bottom-LEFT (Adam, 2026-09-14): bottom-right collided with the "Full photo" bubble on panels. */
.wa-float{position:fixed;z-index:150;left:max(18px,env(safe-area-inset-left));bottom:max(18px,env(safe-area-inset-bottom));
  width:56px;height:56px;border-radius:50%;background:#25D366;color:#fff;display:grid;place-items:center;
  box-shadow:0 6px 22px rgba(18,17,15,.24);transition:transform .2s ease,box-shadow .2s ease}
.wa-float:hover{transform:scale(1.07);box-shadow:0 8px 28px rgba(18,17,15,.30)}
.wa-float svg{width:30px;height:30px}
/* Footer: the floating button covered "© Bahía Photographer" (Adam, 2026-09-14). A small WhatsApp icon sits
   between the copyright and Privacy/Legal, and the floating button fades out while that row is on screen
   (lang.js adds body.foot-in). */
.foot-bottom{align-items:center}
.wa-inline{width:40px;height:40px;border-radius:50%;background:#25D366;color:#fff !important;display:grid;place-items:center;
  flex:none;box-shadow:0 4px 14px rgba(18,17,15,.18);transition:transform .2s ease}
.wa-inline:hover{transform:scale(1.07)}
.wa-inline svg{width:22px;height:22px}
.wa-float{transition:transform .2s ease,box-shadow .2s ease,opacity .25s ease}
body.foot-in .wa-float{opacity:0;pointer-events:none;transform:scale(.85)}
/* Phones: three items do not fit one row, so stack them centred — copyright, icon, Privacy · Legal. */
@media (max-width:620px){.foot-bottom{flex-direction:column;justify-content:center;text-align:center;gap:12px}}
@media (max-width:620px){.wa-float{width:52px;height:52px}.wa-float svg{width:28px;height:28px}
  /* ⛔ bottom-left on a phone sat on top of every hero's main button ("Check your date", "Send enquiry"),
     which lines up with the left edge at the foot of the first screen. Keep 88px clear below panel text. */
  body .pan .pan-body{padding-bottom:88px}}   /* ⛔ `body .pan` specificity: a plain .pan-body rule lost to the frames block */
@media (max-width:980px){
  .mosaic{gap:22px;padding:22px}
  .nav-links{
    position:fixed;top:var(--nav-h);left:0;right:0;flex-direction:column;gap:0;
    background:rgba(250,248,245,.96);backdrop-filter:blur(16px);
    max-height:0;overflow:hidden;transition:max-height .3s ease;
  }
  .nav-links.open{max-height:70vh}
  .nav-links li{width:100%;border-bottom:1px solid rgba(18,17,15,.10)}
  .nav-links a{display:block;padding:17px 26px;font-size:.85rem}
  .nav-burger{display:block}
  .foot-grid{grid-template-columns:1fr;gap:26px}
  .mosaic,.mosaic-4{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:620px){
  /* ⛔ The wordmark once pushed the burger off a 390px screen, leaving the site
     with no reachable navigation. Re-check at 375px after ANY change here. */
  .nav-logo img{height:40px}
  .nav-right{gap:12px}
  .pan-body{padding-bottom:clamp(46px,8vh,80px)}
}
@media (max-width:380px){
  /* ⛔ 375px is the check that matters: the wordmark once pushed the burger off screen.
     36px tall = 95px wide, which leaves the burger and the language button reachable. */
  .nav-logo img{height:36px}
}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{transition:none!important;animation:none!important}
}
