/* The gallery, course by course (gallery-page.js): the site's paper and cream, with one dark, golden evening inside the
   scroll story. Every motion has an opacity-only version for visitors who ask for less motion; those beat the blanket
   rule in styles.css with !important and a more specific selector (see the end of this file). */
.gv-page{--night:#0f0b0a;--ivory:#f6ead6;--display:clamp(44px,5.2vw,74px);overflow-x:clip;background:var(--paper)}
html.is-arriving .pg-page>.gv-page{animation:none!important}   /* the page has its own arrival, below */
.gv-page svg{flex:none}

/* ---------- The story: one stage pinned under the sticky site header (--pw-top, from trackHeader), five chapters ----------
   The stage fills the large viewport (100lvh), so no band of night shows under it once a phone's browser bars slide
   away; the words and the gold line are lifted by the bars' height (100lvh - 100svh) so the bars never cover them. */
.gv-story{--units:4.9;--bars:0px;position:relative;height:calc(var(--units) * (100vh - var(--pw-top)));height:calc(var(--units) * (100lvh - var(--pw-top)));background:var(--night)}
@supports (height:100lvh){.gv-story{--bars:calc(100lvh - 100svh)}}
.gv-stage{position:sticky;top:var(--pw-top);height:calc(100vh - var(--pw-top));height:calc(100lvh - var(--pw-top));overflow:hidden;background:var(--paper)}
.gv-layer{position:absolute;inset:0;z-index:1;overflow:hidden}
.gv-layer img{width:100%;height:100%;object-fit:cover;transform-origin:50% 60%}
.gv-layer[data-layer="0"]{clip-path:inset(36% var(--padx) var(--padx));animation:gv-develop 900ms var(--ease-out) 200ms backwards}
.gv-layer[data-layer="1"],.gv-layer[data-layer="2"],.gv-layer[data-layer="3"]{clip-path:inset(100% 0 0)}
/* --eve (set by measure()): 62% of the stage, or less on a short screen, so the evening words and credit end above the gold line */
.gv-layer[data-layer="4"]{bottom:auto;height:var(--eve,62%);clip-path:inset(0 0 100%)}
/* The film (owner, 19 Oct 2026): it sits on the opening photo, which is its own first frame, and shows once it can
   draw; the scroll plays it (gallery-page.js). The stage is held longer with it, so each clip gets about a screen. */
.gv-page.gv-filmed .gv-story{--units:6.5}
.gv-film{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;transform-origin:50% 60%;opacity:0;pointer-events:none;transition:opacity 240ms var(--ease-out)}
.gv-film.is-ready{opacity:1}
.gv-filmed .gv-night,.gv-filmed .gv-horizon{display:none}
.gv-filmed .gv-chapter.gv-eve{top:auto;bottom:calc(64px + var(--bars) + env(safe-area-inset-bottom))}
.gv-mat{display:none;position:absolute;z-index:1;box-shadow:0 0 0 100vmax var(--paper);pointer-events:none}   /* reduced motion only: the paper round the framed photo */
.gv-night{position:absolute;inset:0;z-index:1;background:var(--night);opacity:0}
.gv-horizon{position:absolute;left:0;right:0;top:var(--eve,62%);z-index:2;height:1px;background:var(--gold);transform:scaleX(0);transform-origin:left}
.gv-scrim{position:absolute;inset:0;z-index:2;pointer-events:none;opacity:0;background:linear-gradient(to top,rgba(15,11,10,.86) var(--bars),rgba(15,11,10,.58) calc(26% + var(--bars)),rgba(15,11,10,0) calc(56% + var(--bars)))}

.gv-opening{position:absolute;z-index:3;top:clamp(28px,4.5vh,64px);left:var(--padx);right:var(--padx)}
.gv-page .gv-opening h1{font-size:var(--display);line-height:1}
.gv-page .gv-lede{margin-top:16px;max-width:36ch;color:var(--ink-soft);font-size:clamp(16px,1.2vw,18px);line-height:1.6}
.gv-skip{margin-top:2px}

.gv-chapter{position:absolute;z-index:4;left:var(--padx);right:var(--padx);bottom:calc(64px + var(--bars) + env(safe-area-inset-bottom));max-width:640px;color:var(--ivory)}
.gv-page .gv-chapter h2{margin:0;color:var(--ivory);font-size:var(--display);line-height:1}
.gv-page .gv-chapter p{margin-top:12px;max-width:32ch;color:rgba(246,234,214,.9);text-wrap:pretty}
.gv-chapter.gv-eve{top:calc(var(--eve,62%) + 28px);bottom:auto}   /* the evening's words sit just under the gold horizon */
.gv-page .gv-chapter .gv-credit{max-width:none;margin-top:8px;font-size:13px;line-height:1.5;color:rgba(246,234,214,.72)}

.gv-progress{position:absolute;z-index:4;left:var(--padx);right:var(--padx);bottom:calc(32px + var(--bars) + env(safe-area-inset-bottom));display:grid;grid-template-columns:repeat(5,1fr);gap:4px;max-width:640px;opacity:0}
/* Up to 1000px wide the floating "Request a quote" button sits bottom right (support.css), so the words and the gold line go above it. */
@media (max-width:1000px){
  .gv-progress{bottom:calc(84px + var(--bars) + env(safe-area-inset-bottom))}
  .gv-chapter,.gv-filmed .gv-chapter.gv-eve{bottom:calc(116px + var(--bars) + env(safe-area-inset-bottom))}
}
.gv-progress span{height:2px;overflow:hidden;background:rgba(246,234,214,.3)}
.gv-progress b{display:block;height:100%;background:var(--gold);transform:scaleX(0);transform-origin:left}

/* Masked line reveal: each line rises 28px into its own mask, 280ms on the strong ease-out, 45ms after the line above */
.gv-ln{display:block;overflow:hidden;overflow:clip;padding-bottom:.16em;margin-bottom:-.16em}
.gv-ln>span{display:block}
.gv-opening .gv-ln>span{animation:gv-rise 280ms var(--ease-out) calc(160ms + var(--d,0ms)) backwards}
.gv-page .gv-lede{animation:gv-across 280ms var(--ease-out) 340ms backwards}
.gv-skip{animation:gv-fade 280ms var(--ease-out) 420ms backwards}
.gv-chapter .gv-ln>span,.gv-page .gv-chapter p{opacity:0;transform:translateY(28px);transition:opacity 160ms var(--ease-out),transform 160ms var(--ease-out)}
.gv-page .gv-chapter p{transform:translateX(-28px);clip-path:inset(-20% 100% -20% 0);transition:opacity 160ms var(--ease-out),transform 160ms var(--ease-out),clip-path 160ms var(--ease-out)}
.gv-chapter.is-on .gv-ln>span,.gv-page .gv-chapter.is-on p{opacity:1;transform:none;clip-path:inset(-20% -10% -20% 0);transition-duration:280ms;transition-delay:var(--d,0ms)}
.gv-page .gv-chapter.is-on p{--d:135ms}
@keyframes gv-rise{from{opacity:0;transform:translateY(28px)}}
@keyframes gv-across{from{opacity:0;transform:translateX(-28px);clip-path:inset(-20% 100% -20% 0)}to{clip-path:inset(-20% -10% -20% 0)}}
@keyframes gv-develop{from{opacity:0;filter:saturate(.35) brightness(1.12) contrast(.86)}}
@keyframes gv-fade{from{opacity:0}}

/* ---------- The opening shuffle (gallery-shuffle.js, owner 14 Oct 2026) ----------
   Four prints land 140ms apart; the others slide away while the top print, the page's own first photo, settles into
   its frame. The paper behind stays until the print sits exactly on the photo, so removing it changes nothing. */
.gv-shuffle{position:absolute;inset:0;z-index:5;background:var(--paper);pointer-events:none}
.gv-print{position:absolute;overflow:hidden;background:#fffdf8;border-radius:calc(2px / var(--s));
  box-shadow:0 0 0 var(--edge) #fffdf8,0 calc(12px / var(--s)) calc(30px / var(--s)) rgba(62,22,34,.24);
  transform:translate(var(--x),var(--y)) rotate(var(--r)) scale(var(--s))}
.gv-print img{position:absolute;max-width:none;object-fit:cover}
.gv-shuffle.is-playing .gv-print{animation:gv-land 420ms var(--ease-out) var(--d) backwards,gv-away 380ms var(--ease-in-out) calc(1000ms + var(--d) / 4) forwards}
.gv-shuffle.is-playing .gv-print.is-top{animation:gv-land 420ms var(--ease-out) var(--d) backwards,gv-settle 600ms var(--ease-in-out) 1000ms forwards}
@keyframes gv-land{from{opacity:0;transform:translate(var(--x),calc(var(--y) - 70px)) rotate(calc(var(--r) * 2.4)) scale(calc(var(--s) * 1.12))}}
@keyframes gv-away{to{opacity:0;transform:translate(calc(var(--x) + var(--out)),var(--y)) rotate(calc(var(--r) * 3)) scale(var(--s))}}
@keyframes gv-settle{to{transform:none;border-radius:var(--radius);box-shadow:0 0 0 0 #fffdf8,0 0 0 0 transparent}}
/* The heading waits under the paper, then drops into place from above. */
.gv-page.gv-shuffling .gv-opening{visibility:hidden}
.gv-page.gv-dropping .gv-opening .gv-ln>span{animation:gv-drop 460ms var(--ease-out) var(--d,0ms) backwards}
.gv-page.gv-dropping .gv-lede{animation:gv-drop 460ms var(--ease-out) 140ms backwards}
.gv-page.gv-dropping .gv-skip{animation:gv-drop 460ms var(--ease-out) 200ms backwards}
@keyframes gv-drop{from{opacity:0;transform:translateY(-30px)}}
/* The scroll cue: a small arrow that bobs gently and says "Scroll". Transform and opacity only; it fades out once the page moves, and while the opening shuffle plays. */
.gv-scroll-cue{position:fixed;left:50%;bottom:22px;z-index:6;transform:translateX(-50%);display:grid;justify-items:center;gap:6px;padding:10px 16px 8px;border-radius:999px;background:rgba(252,250,245,.94);box-shadow:0 2px 10px rgba(48,45,40,.16);color:var(--wine);text-decoration:none;font:600 13px/1 var(--sans);letter-spacing:.16em;text-transform:uppercase;transition:opacity 300ms var(--ease-out),visibility 300ms}
.gv-scroll-cue svg{width:22px;height:22px;animation:gv-cue 1.6s var(--ease-in-out) infinite}
.gv-scroll-cue.is-gone,.gv-page.gv-shuffling .gv-scroll-cue{opacity:0;visibility:hidden;pointer-events:none}
/* On a phone the floating quote button sits bottom right, so the cue moves to the left there. */
@media (max-width:600px){.gv-scroll-cue{left:20px;transform:none}}
@keyframes gv-cue{0%,100%{transform:translateY(0);opacity:.5}50%{transform:translateY(7px);opacity:1}}
@keyframes gv-out{to{opacity:0}}

/* ---------- After dark: the evening carries on once the story lets go ---------- */
.gv-after{padding:0 0 clamp(56px,7vw,104px);background:var(--night);color:var(--ivory)}
.gv-pair{display:grid;gap:4px}
.gv-pair img{width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;background:#221b18}
.gv-page .gv-after .gv-credit{margin:12px var(--padx) 0;font-size:13px;line-height:1.5;color:rgba(246,234,214,.72)}   /* a figcaption under the canapés only */
.gv-page .gv-after .pg-link{margin:20px var(--padx) 0;color:var(--ivory)}
.gv-page .gv-after :focus-visible{outline-color:var(--ivory)}

/* ---------- The collection ---------- */
.gv-collection{padding-top:clamp(56px,7vw,104px)}
.gv-head{padding:0 var(--padx)}
.gv-page .gv-head h2{margin:0}
.gv-head h2:focus{outline:none}
.gv-page .gv-head p{margin-top:12px;max-width:44ch;color:var(--ink-soft)}
/* Each button reaches 13px past its words on both sides (the negative margin keeps the layout), so even "All" is 44 × 44.
   The chosen course is bold as well as wine, so it reads without colour; ::before (bold, hidden, no height) keeps each
   button as wide as its bold words, so nothing shifts when the choice changes. */
.gv-filters{display:flex;flex-wrap:wrap;gap:0 26px;margin:16px var(--padx) 0}
.gv-filters button{position:relative;min-width:44px;min-height:44px;margin:0 -13px;padding:0 13px;border:0;background:none;color:var(--ink-soft);font:500 15px/1.3 var(--sans);text-align:left;cursor:pointer;touch-action:manipulation}
.gv-filters button::before{content:attr(data-label);content:attr(data-label)/"";display:block;height:0;overflow:hidden;visibility:hidden;font-weight:700}
.gv-filters button::after{content:"";position:absolute;left:13px;right:13px;bottom:4px;height:2px;background:var(--gold);transform:scaleX(0);transform-origin:left;transition:transform 300ms var(--ease-out)}
.gv-filters button[aria-pressed="true"]{color:var(--wine);font-weight:700}
.gv-filters button[aria-pressed="true"]::after{transform:scaleX(1)}
.gv-page .gv-filters button:focus-visible{outline-offset:-2px}
@media (hover:hover) and (pointer:fine){.gv-filters button:hover{color:var(--wine)}}

.gv-grid{list-style:none;margin:20px 0 0;padding:0;display:grid;gap:4px}
.gv-tile{grid-row:span 2;height:100%;min-width:0;min-height:0;max-height:100%;box-sizing:border-box;overflow:hidden;background:var(--cream)}
.gv-shot{display:block;width:100%;height:100%;min-width:0;min-height:0;padding:0;border:0;background:none;cursor:zoom-in}
.gv-page .gv-shot:focus-visible{outline:3px solid var(--wine);outline-offset:-3px}
.gv-page .gv-shot.gv-pointer-return:focus-visible{outline:none}
.gv-shot img{display:block;width:100%;height:100%;min-width:0;min-height:0;max-height:100%;object-fit:cover}
.gv-empty{margin:20px var(--padx) 0}
.gv-occasion{margin-top:clamp(56px,7vw,104px)}
@media (max-width:899px){
  /* phone mosaic, repeating every five: one wide, two tall, two square */
  .gv-grid{grid-template-columns:repeat(2,1fr);grid-auto-rows:calc((100vw - 4px) / 4)}
  .gv-tile:nth-child(5n+1){grid-column:span 2;grid-row:span 3}
  .gv-tile:nth-child(5n+2),.gv-tile:nth-child(5n+3){grid-row:span 3}
  .gv-tile:last-child:nth-child(5n+2),.gv-tile:last-child:nth-child(5n+4){grid-column:span 2}   /* no lone tile at the end */
}
@media (min-width:900px){
  /* wider screens, repeating every seven: one wide and two tall, then four square */
  .gv-grid{margin-inline:var(--padx);grid-template-columns:repeat(4,1fr);grid-auto-rows:calc((min(100vw - 2 * var(--padx),1320px) - 12px) / 8)}
  .gv-tile:nth-child(7n+1){grid-column:span 2;grid-row:span 3}
  .gv-tile:nth-child(7n+2),.gv-tile:nth-child(7n+3){grid-row:span 3}
  /* no hole at the end, however many photos the course has */
  .gv-tile:last-child:nth-child(7n+2),.gv-tile:nth-last-child(2):nth-child(7n+4),.gv-tile:last-child:nth-child(7n+5),.gv-tile:last-child:nth-child(7n+6){grid-column:span 2}
  .gv-tile:last-child:nth-child(7n+1),.gv-tile:last-child:nth-child(7n+4){grid-column:span 4}
  .gv-pair{grid-template-columns:1fr 1fr;margin-inline:var(--padx)}
  .gv-page .gv-after .gv-credit{margin-inline:0}
}
/* Photos develop as they arrive: from clear, slightly large and washed out to full colour */
.gv-page.is-live .gv-tile img{opacity:0;transform:scale(1.05);filter:saturate(.35) brightness(1.12) contrast(.86);transition:opacity 900ms var(--ease-out) var(--wait,0ms),transform 900ms var(--ease-out) var(--wait,0ms),filter 900ms var(--ease-out) var(--wait,0ms)}
.gv-page.is-live .gv-tile.is-in img{opacity:1;transform:none;filter:none}
@media (hover:hover) and (pointer:fine){.gv-page.is-live .gv-tile.is-in .gv-shot:hover img{transform:scale(1.035);transition:transform 200ms var(--ease-out)}}

/* ---------- Invitation ---------- */
.gv-invite{margin-top:clamp(56px,7vw,104px);padding:clamp(56px,7vw,104px) var(--padx);background:var(--cream)}
.gv-page .gv-invite h2{max-width:18ch;margin:0}
.gv-page .gv-invite p{margin-top:12px;max-width:44ch;color:var(--ink-soft)}
.gv-invite .pg-actions{margin-top:24px}

/* ---------- Viewer: a solid night ground, so nothing on the page shows through ---------- */
.gv-viewer{position:fixed;inset:0;width:100%;max-width:none;height:100vh;height:100dvh;max-height:none;margin:0;padding:0;border:0;overflow:hidden;background:var(--night);color:var(--ivory)}
.gv-viewer[open]{display:grid;grid-template-rows:auto minmax(0,1fr) auto;animation:gv-fade 200ms var(--ease-out)}
.gv-viewer::backdrop{background:var(--night)}
/* The ground fades in with the window and out before it closes (gallery-page.js adds .is-leaving), never a cut to black. */
.gv-viewer[open]::backdrop{animation:gv-fade 200ms ease-out}
.gv-viewer.is-leaving::backdrop{animation:gv-out 200ms ease-out forwards}
.gv-v-top{display:flex;justify-content:flex-end;padding:max(12px,env(safe-area-inset-top)) 12px 0}
.gv-viewer .gv-v-btn{position:static;width:48px;height:48px;display:grid;place-items:center;padding:0;border:0;border-radius:50%;background:rgba(246,234,214,.14);color:var(--ivory);cursor:pointer;touch-action:manipulation;transition:transform 160ms var(--ease-out)}
.gv-viewer .gv-v-btn:active{transform:scale(.97)}
.gv-viewer :focus-visible{outline:2px solid var(--ivory);outline-offset:3px}
.gv-viewer .gv-v-figure{display:grid;min-height:0;margin:0;padding:8px 0;touch-action:pan-y}
/* The full-screen orbit: framed prints placed by gallery-page.js (size, depth, turn); the front one is at full size. */
.gv-viewer .gv-v-orbit{position:relative;min-height:0;overflow:hidden;touch-action:pan-y;user-select:none;-webkit-user-select:none}
.gv-viewer .gv-v-card{position:absolute;left:50%;top:50%;box-sizing:border-box;border-radius:18px;background:#fffcf6;border:1px solid #dfd5c8;box-shadow:0 26px 50px rgba(0,0,0,.42),0 6px 14px rgba(0,0,0,.25);backface-visibility:hidden;will-change:transform,opacity}
.gv-viewer .gv-v-card img{display:block;width:100%;height:100%;max-width:none;object-fit:cover;border-radius:10px;pointer-events:none}
@media(max-width:600px){.gv-viewer .gv-v-card{border-radius:12px}.gv-viewer .gv-v-card img{border-radius:7px}}
.gv-v-cap{padding:12px var(--padx) 0;text-align:center;font-size:14px;line-height:1.45}
.gv-v-cap small{display:block;margin-top:4px;font-size:13px;color:rgba(246,234,214,.72)}
.gv-v-cap small:empty{display:none}
.gv-v-nav{display:flex;justify-content:center;align-items:center;gap:16px;padding:16px 0 max(20px,env(safe-area-inset-bottom))}
.gv-v-pos{min-width:64px;text-align:center;font-size:13px;color:rgba(246,234,214,.72);font-variant-numeric:tabular-nums}

/* ---------- Reduced motion (the owner's phone): opacity only, nothing moves ---------- */
@media (prefers-reduced-motion:reduce){
  .gv-page .gv-scroll-cue svg{animation:none!important}
  .gv-page .gv-tile{transform:none!important;will-change:auto}
  .gv-page .gv-opening .gv-ln>span,.gv-page .gv-lede,.gv-page .gv-skip,.gv-page .gv-layer[data-layer="0"],.gv-page .gv-viewer[open]{animation:gv-fade 280ms var(--ease-out) backwards!important}
  .gv-page .gv-viewer[open]::backdrop{animation:gv-fade 280ms ease-out backwards!important}
  .gv-page .gv-viewer.is-leaving::backdrop{animation:gv-out 200ms ease-out forwards!important}
  .gv-page .gv-chapter .gv-ln>span,.gv-page .gv-chapter p{transform:none!important;clip-path:none!important;transition:opacity 280ms var(--ease-out)!important}
  .gv-page.is-live .gv-tile img{transform:none!important;filter:none!important;transition:opacity 400ms var(--ease-out)!important}
  .gv-page .gv-filters button::after{transform:scaleX(1)!important;opacity:0;transition:opacity 200ms var(--ease-out)!important}
  .gv-page .gv-filters button[aria-pressed="true"]::after{opacity:1}
  .gv-page .gv-mat{display:block}
  .gv-page .gv-shuffle.is-playing .gv-print{animation:gv-fade 260ms ease var(--d) backwards!important}
  .gv-page .gv-shuffle.is-playing{animation:gv-out 350ms ease 750ms forwards}
  .gv-page.gv-dropping .gv-opening .gv-ln>span,.gv-page.gv-dropping .gv-lede,.gv-page.gv-dropping .gv-skip{animation:gv-fade 300ms ease backwards!important}
  .gv-page .pg-btn:active,.gv-viewer .gv-v-btn:active{transform:none}
}
@media (prefers-reduced-data:reduce){.gv-page .gv-tile{transform:none!important;will-change:auto}}
@media print{.gv-page .gv-tile{transform:none!important;will-change:auto}}

/* Staff dashboard: Gallery Photos (public/gallery-admin.js). */
.gallery-admin-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px;margin:0 0 26px}
.gallery-admin-item{margin:0;display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid var(--line);border-radius:14px;background:#fff}
.gallery-admin-item img{width:100%;aspect-ratio:1;object-fit:cover;border-radius:10px;display:block}
.gallery-admin-item figcaption{font-size:13px;overflow-wrap:anywhere}
.gallery-admin-item.is-hidden img{opacity:.35;filter:grayscale(1)}
.gallery-admin-item .field-label{font-size:13px;display:flex;flex-direction:column;gap:4px}
.gallery-admin-item .sbtn{min-height:44px;width:100%}
.gallery-admin-uploads{grid-template-columns:repeat(auto-fill,minmax(min(100%,280px),1fr))}
.gallery-admin-uploads .gallery-admin-item img{height:160px;aspect-ratio:auto}

/* A visible cue that the photos swipe: arrows and one line above the collection. */
.gv-swipe{display:inline-flex;align-items:center;gap:12px;margin:16px var(--padx) 4px;max-width:calc(100% - 2 * var(--padx));box-sizing:border-box;padding:9px 16px 9px 12px;border-radius:999px;background:rgba(112,43,61,.08);font:500 15px/1.4 var(--sans);color:var(--ink)}
.gv-swipe-arrows{display:inline-flex;flex:none;gap:2px;color:var(--wine)}
.gv-swipe-arrows svg{width:22px;height:22px}
