/* ===== tokens ===== */
:root{
  --paper:#FFFFFF; --ink:#000000; --muted:#8C8C8C; --tag:#FFFFFF; --ghost:#EEEEEE;
  --line:rgba(0,0,0,.15); --edge:rgba(0,0,0,.08);
  --sans:"Helvetica Neue",Helvetica,Arial,sans-serif;
  --sai-t:env(safe-area-inset-top,0px); --sai-r:env(safe-area-inset-right,0px);
  --sai-b:env(safe-area-inset-bottom,0px); --sai-l:env(safe-area-inset-left,0px);
  --gutter:clamp(14px,2vw,28px);
  /* motion: two curves, three durations; the same constants live in media.js for WAAPI. Only opacity and blur ever animate */
  --e-out:cubic-bezier(.2,.7,.1,1); --e-io:cubic-bezier(.7,0,.2,1);
  --t-1:180ms; --t-2:360ms; --t-3:600ms;
}
*{box-sizing:border-box}
html,body{margin:0;height:100%;overflow:hidden;overscroll-behavior:none;background:var(--paper);color:var(--ink);color-scheme:light}
body{font:14px/1.4 var(--sans);text-transform:lowercase;-webkit-font-smoothing:antialiased;-webkit-tap-highlight-color:transparent}
a{color:inherit;text-decoration:none}
button{font:inherit;letter-spacing:inherit;text-transform:inherit;color:inherit;background:none;border:0;padding:0;margin:0;cursor:pointer}
button:focus,a:focus{outline:none}
body.kb button:focus-visible,body.kb a:focus-visible{outline:1px solid currentColor;outline-offset:3px}
img,video{display:block}
[hidden]{display:none !important}

/* ===== nav: two words at the bottom centre, as on the reference ===== */
@media (max-width:899px){ .nav{bottom:calc(12px + var(--sai-b))} }
.nav{position:fixed;left:0;right:0;bottom:calc(22px + var(--sai-b));z-index:40;display:flex;justify-content:center;align-items:center;gap:34px;font-size:11px;line-height:16px;letter-spacing:.02em;color:#fff;mix-blend-mode:difference;pointer-events:none}
.nav a{pointer-events:auto;position:relative;padding:12px 8px;margin:-12px -8px}
/* active link: it lands from a soft blur, then a narrow band of light passes through the word once, and again every ~8 s.
   Over the strip the nav draws in difference, so the band is a grey that reads as light; over paper it is a quieter grey */
.nav{--glint:#6e6e6e}
.nav a.on{background:linear-gradient(100deg,currentColor 0 44%,var(--glint) 50%,currentColor 56% 100%) 100% 0/300% 100% no-repeat;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
  animation:glint 8s var(--e-io) .35s infinite}
@keyframes glint{0%{background-position:100% 0}15%,100%{background-position:0 0}}
@media (hover:hover){ .nav a:hover{opacity:.6} }
@media (prefers-reduced-motion:reduce){ .nav a.on{animation:none;text-decoration:underline;text-underline-offset:3px} }
/* the gallery scrolls under the two words: they stay exactly where they are, drawn in difference as over the strip */

/* ===== roles: model / styling, two words at the top centre, mirroring the nav below ===== */
.roles{position:fixed;left:0;right:0;top:calc(22px + var(--sai-t));z-index:40;display:flex;justify-content:center;align-items:center;gap:34px;font-size:11px;line-height:16px;letter-spacing:.02em;color:#fff;mix-blend-mode:difference;pointer-events:none;transition:opacity var(--t-1) var(--e-out)}
.roles a{pointer-events:auto;position:relative;padding:12px 8px;margin:-12px -8px;transition:opacity var(--t-2) var(--e-out)}
/* the picked word gets a hairline that fades in under it; the other one steps back */
.roles a::after{content:"";position:absolute;left:8px;right:8px;bottom:10px;height:1px;background:currentColor;opacity:0;transition:opacity var(--t-2) var(--e-out)}
.roles a.on::after{opacity:1}
.roles.picked a:not(.on){opacity:.4}
@media (hover:hover){ .roles a:hover{opacity:.6} .roles.picked a:not(.on):hover{opacity:.75} }
@media (max-width:899px){ .roles{top:calc(14px + var(--sai-t))} }
/* a shoot, contact and the single-frame viewer own the top edge: the words step aside */
body.project-open .roles,body.contact-open .roles,body.viewer-open .roles,body.wardrobe-open .roles{opacity:0}
body.project-open .roles a,body.contact-open .roles a,body.viewer-open .roles a,body.wardrobe-open .roles a{pointer-events:none}
/* gallery: nothing above the first shoot but air; on a phone the photos run edge to edge, the names keep a small gutter */
.layer.wardrobe .scroll{padding-top:calc(40px + var(--sai-t))}
/* the paper rises under the bottom words: photos and shoot names scrolling beneath fade out instead of colliding with them */
.layer.wardrobe::after{content:"";position:absolute;left:0;right:0;bottom:0;height:calc(84px + var(--sai-b));z-index:1;pointer-events:none;
  background:linear-gradient(to top,var(--paper) 0,var(--paper) 42%,rgba(255,255,255,0) 100%)}
@media (max-width:899px){ .layer.wardrobe::after{height:calc(66px + var(--sai-b))} }
@media (max-width:899px){ .layer.wardrobe .scroll{padding:calc(28px + var(--sai-t)) 0 calc(96px + var(--sai-b))} }
/* switching roles: the new frames wait unseen until the first screen has decoded, then land */
.stage.arriving .pic{opacity:0}

/* opaque paper under everything: hides whatever the host page (Tilda) renders above the body */
.mh-bg{position:fixed;inset:0;z-index:0;background:var(--paper)}

/* ===== strip ===== */
.stage{position:fixed;inset:0;overflow:hidden;touch-action:pinch-zoom;cursor:grab;user-select:none;-webkit-user-select:none;z-index:1;background:var(--paper)}
.stage.dragging{cursor:grabbing}
.stage.static{cursor:default}
#stage{will-change:filter,opacity;transition:filter var(--t-3) var(--e-out),opacity var(--t-3) var(--e-out)}
body.project-open #stage,body.contact-open #stage{filter:blur(8px);opacity:.55}
.track{position:absolute;inset:0;will-change:transform}
.pic{position:absolute;left:0;top:0;display:block;background:var(--paper);cursor:pointer;overflow:hidden;contain:strict}
.pic.off{visibility:hidden}
.pic img,.pic video{width:100%;height:100%;object-fit:cover}
/* every photo and teaser, in the strip, a shoot, the wardrobe and the viewer: the frame holds the photo's tone,
   then the image resolves out of a soft blur. One class, one timing */
.media{opacity:0;filter:blur(6px);transition:opacity var(--t-2) var(--e-out),filter var(--t-2) var(--e-out)}
.media.in{opacity:1;filter:none}
.project .stage{position:absolute}
.p-title{position:absolute;left:50%;top:calc(30px + var(--sai-t));z-index:2;margin:0;padding:10px 12px;transform:translateX(-50%);max-width:calc(100% - 48px);
  font:14px/1.3 var(--sans);text-transform:lowercase;color:#000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;text-decoration:none}
.p-title::after{content:"";position:absolute;left:12px;right:12px;bottom:7px;height:1px;background:currentColor;opacity:0;transition:opacity var(--t-2) var(--e-out)}
.p-title.linked::after{opacity:.35}
@media (hover:hover){ .p-title:hover::after{opacity:1} }
.project .stage{cursor:default}

/* ===== layers ===== */
/* a shoot or the gallery does not slide in: the layer dissolves up over the blurred strip, and dissolves away the same way.
   A closed layer is invisible, not display:none, so its layout is already done when it is asked for and the fade starts clean */
.layer{position:fixed;inset:0;z-index:15;background:var(--paper);opacity:0;visibility:hidden;overflow:hidden;
  transition:opacity var(--t-3) var(--e-out),visibility 0s linear var(--t-3)}
.project{background:rgba(255,255,255,.8)}
.layer.open{opacity:1;visibility:visible;transition:opacity var(--t-3) var(--e-out),visibility 0s}
.layer .scroll{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;
  padding:calc(28px + var(--sai-t)) calc(var(--gutter) + var(--sai-r)) calc(96px + var(--sai-b)) calc(var(--gutter) + var(--sai-l))}

/* gallery: photos first, names in the same small type as the navigation, generous air between shoots */
.section{margin-bottom:56px;content-visibility:auto;contain-intrinsic-size:auto 600px}   /* shoots off screen are not laid out or painted */
.section-head{display:flex;align-items:baseline;gap:10px;margin:0 0 10px;font-size:11px;line-height:16px;letter-spacing:.02em;font-weight:400}
.section-head .yr{margin-left:0}
.rows{display:flex;flex-direction:column;gap:var(--tile-gap,4px)}
.row{display:flex;gap:var(--tile-gap,4px)}
@media (max-width:899px){ .section{margin-bottom:40px} .section-head{margin:0 0 8px;padding:0 calc(14px + var(--sai-l))} .rows{--tile-gap:2px} }
.tile{position:relative;background:var(--ghost);overflow:hidden;cursor:pointer;flex:none}
.tile img,.tile video{width:100%;height:100%;object-fit:cover}
.empty{color:var(--muted);padding:20px 0}
/* contact: translucent paper over the strip, centred text */
.contact{position:fixed;inset:0;z-index:20;background:rgba(255,255,255,.72);display:flex;align-items:center;justify-content:center;text-align:center;opacity:0;visibility:hidden;cursor:default;
  transition:opacity var(--t-3) var(--e-out),visibility 0s linear var(--t-3)}
.contact.open{opacity:1;visibility:visible;transition:opacity var(--t-3) var(--e-out),visibility 0s}
.contact .info{filter:blur(6px);transition:filter var(--t-3) var(--e-out)}
.contact.open .info{filter:none}
.contact .info{padding:0 24px calc(60px + var(--sai-b))}
.contact{-webkit-font-smoothing:auto;color:#000}
.contact h1{font:18px/1.15 var(--sans);text-transform:none;margin:0 0 5px;letter-spacing:-.01em}
.contact p{margin:0;font-size:12px;line-height:1.5}
.contact .role{display:none}
.contact a{text-transform:none;padding:8px 6px;margin:-6px -6px;display:inline-block}
@media (max-width:899px){ .contact h1{font-size:16px} .contact p{font-size:11px} }
.contact a:hover{text-decoration:underline;text-underline-offset:3px}

/* ===== viewer (single work, from wardrobe or a shoot) ===== */
.viewer{position:fixed;inset:0;z-index:30;background:var(--paper);display:flex;flex-direction:column;opacity:0;transition:opacity var(--t-2) var(--e-out)}
.viewer.open{opacity:1}
.viewer.fading{opacity:0}
.v-stage{position:absolute;inset:0;padding:calc(56px + var(--sai-t)) 48px calc(64px + var(--sai-b));display:flex;align-items:center;justify-content:center;touch-action:pinch-zoom}
.v-media{position:relative;background:var(--ghost);overflow:hidden}
.v-media img,.v-media video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.v-media img.hq{z-index:1}
.v-media video{background:#000}
.v-top{position:absolute;top:0;left:0;right:0;z-index:2;display:flex;justify-content:space-between;padding:calc(14px + var(--sai-t)) calc(var(--gutter) + var(--sai-r)) 12px calc(var(--gutter) + var(--sai-l));font-size:14px;pointer-events:none}
.v-top>*{pointer-events:auto}
.v-count{color:var(--muted)}
.v-close{padding:12px 10px;margin:-12px -10px}
.v-arrows button{position:absolute;top:0;bottom:0;width:18%;cursor:pointer;opacity:0}
.v-arrows .prev{left:0;cursor:w-resize}.v-arrows .next{right:0;cursor:e-resize}
.v-bottom{position:absolute;left:0;right:0;bottom:0;z-index:2;display:flex;flex-wrap:wrap;gap:6px 14px;align-items:baseline;justify-content:center;text-align:center;
  padding:12px calc(var(--gutter) + var(--sai-r)) calc(14px + var(--sai-b)) calc(var(--gutter) + var(--sai-l));font-size:14px;color:var(--muted)}
.v-bottom .cr,.v-bottom > span:not(.proj){display:none}
/* a shoot with a page: its name is the link, marked by a hairline only, in the same quiet grey */
.v-bottom a{padding:10px 3px;margin:-10px -3px;color:inherit;text-decoration:underline;text-decoration-color:rgba(0,0,0,.25);text-underline-offset:4px;text-decoration-thickness:1px}
.v-bottom a:hover{text-decoration-color:currentColor}
.v-ctl{position:absolute;left:0;right:0;bottom:0;z-index:3;display:flex;align-items:center;gap:14px;padding:22px 12px 10px;font-size:12px;color:#fff;cursor:default;background:linear-gradient(to top,rgba(0,0,0,.45),rgba(0,0,0,0))}
.v-ctl .bar{flex:1;height:14px;position:relative;cursor:pointer}
.v-ctl .bar::before{content:"";position:absolute;left:0;right:0;top:6.5px;height:1px;background:rgba(255,255,255,.45)}
.v-ctl .bar i{position:absolute;left:0;top:6.5px;height:1px;background:#fff;width:0}
.v-ctl .time{min-width:84px;text-align:right}
.v-ctl button{color:inherit}
@media (max-width:899px){ .v-stage{padding:calc(52px + var(--sai-t)) 12px calc(64px + var(--sai-b))} }

@media (prefers-reduced-motion:reduce){ *,*::before,*::after{transition-duration:0s !important;animation-duration:0s !important} }

/* year of a shoot: quiet, right after its name */
.yr{color:var(--muted);margin-left:.6em;font-variant-numeric:tabular-nums}

/* single-frame viewer has its own close and caption: the site nav steps aside so nothing overlaps */
.nav{transition:opacity var(--t-1) var(--e-out)}
body.viewer-open .nav{opacity:0;pointer-events:none;visibility:hidden;transition:opacity var(--t-1) var(--e-out),visibility 0s linear var(--t-1)}
body.viewer-open .nav a{pointer-events:none}   /* the links keep their own pointer-events, so they are switched off here too */
.v-bottom{padding-bottom:calc(20px + var(--sai-b))}
.mh-fail{position:fixed;inset:auto 0 50% 0;z-index:100;margin:0;text-align:center;color:var(--ink)}
.mh-fail a{text-decoration:underline}
/* credit: "powered by" + the XTC mark, at the top of the gallery and the archive; the letters wave on hover */
.credit{display:flex;align-items:center;justify-content:center;gap:5px;font-size:9px;line-height:12px;letter-spacing:.02em;color:var(--muted);user-select:none;-webkit-user-select:none}
.credit-text{display:inline-flex;transition:color var(--t-2) var(--e-out)}
.credit-text i{font-style:normal;display:inline-block;will-change:transform}
.credit-mark{height:8px;width:auto;flex:none;color:var(--muted);transition:color var(--t-2) var(--e-out)}   /* grey like the words: in black the stacked mark reads as a blot */
@media (hover:hover){ .credit:hover .credit-text,.credit:hover .credit-mark{color:var(--ink)} }
.credit-head{padding:0 0 30px}
@media (max-width:899px){ .credit-head{padding:0 0 22px} }
