/* Nathan — photographs
   A white ground, after Levon Biss (docs/references.md); settled Nov 2026.
   Every colour is a token in :root. */

/* Poppins, served from here rather than Google Fonts: the Google stylesheet was
   render-blocking, measured by Lighthouse at 0.45s on desktop and 0.8s on mobile,
   and it told a third party about every visitor. Latin subset, 400 and 500 —
   the only weights used — about 8 KB each. SIL Open Font License, in fonts/OFL.txt. */
@font-face{font-family:"Poppins";font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/poppins-400-latin.woff2) format("woff2")}
@font-face{font-family:"Poppins";font-style:normal;font-weight:500;font-display:swap;
  src:url(fonts/poppins-500-latin.woff2) format("woff2")}

:root{
  --ground:#ffffff;
  --ink:#111111;
  --dim:#6b6d71;          /* 5.2:1 on white; #7a7c80 was 4.18, under WCAG's 4.5 */
  --hair:#e4e4e2;
  --sel:#111111;
  --veil:rgba(255,255,255,.985);
  /* 40px between photographs at 1470px, as measured on levonbiss.com; it
     narrows to ~10px on a phone, where two columns need the width for photographs. */
  --gap:clamp(10px,2.75vw,40px);
  --gutter:clamp(16px,3vw,44px);
  /* 1680, not 1280: on a large monitor the photographs were capped at 1280px
     wide with the rest of the screen empty. Larger photographs were chosen
     over more per screen (Sept 2026); prose keeps its own ch-based measure. */
  --measure:1680px;
}

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--ground);
  color:var(--ink);
  font-family:"Poppins",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:15px;
  /* An explicit line-height, not "normal": "normal" comes from the font's own
     metrics, so when Poppins replaced the fallback every line changed height and
     the whole page moved — a layout shift Lighthouse scored 0.87–0.89, against a
     "good" of 0.1. A fixed ratio takes the same space in either font. */
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
  padding-top:env(safe-area-inset-top,0px);
  padding-bottom:env(safe-area-inset-bottom,0px);
}
img{display:block;max-width:100%}
a{color:inherit}
[hidden]{display:none!important}
:focus-visible{outline:2px solid var(--ink);outline-offset:2px}
@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}


/* Off-screen until focused, then pinned top-left over the banner. */
.skip{
  position:absolute;left:var(--gutter);top:8px;z-index:100;
  transform:translateY(-200%);
  font:inherit;font-size:14px;background:var(--ink);color:var(--ground);
  border:0;border-radius:999px;padding:8px 16px;cursor:pointer;
}
.skip:focus{transform:none}
#main:focus{outline:none}      /* the skip link's fallback target; the focus lands, no ring */

/* ── header ──────────────────────────────────────────────────────────────── */
.top{
  display:flex;align-items:center;gap:20px;flex-wrap:wrap;
  max-width:var(--measure);margin:0 auto;
  padding:40px var(--gutter) 0;
}
/* After Levon Biss (docs/references.md): Poppins 400 at 24px with slightly
   tightened tracking, left-aligned with the menu on the same line. His name is
   in capitals; this one is set as written, "Nathan's Photos", at the owner's
   request. */
.mark{
  /* "Nathan's Photos" is twice the length of "Nathan": it scales down on small
     phones so it keeps one line beside the Instagram icon. */
  font-size:clamp(17px,5.2vw,24px);font-weight:400;letter-spacing:-.02em;
  white-space:nowrap;
  text-decoration:none;line-height:1.1;
}
/* The menu in full ink rather than grey, as on levonbiss.com; the current page
   is underlined. */
.sections{display:flex;gap:21px;flex-wrap:wrap;margin-left:auto}
.sections a{
  font-size:16px;text-decoration:none;color:var(--ink);
  padding-bottom:3px;border-bottom:1px solid transparent;white-space:nowrap;
}
.sections a:hover{border-bottom-color:var(--hair)}
.sections a[aria-current="page"]{color:var(--sel);border-bottom-color:var(--sel)}
.social{display:inline-flex;color:var(--ink);padding:4px;margin:-4px 0}
.social:hover{color:var(--dim)}

/* Below the width the whole menu needs on one line beside the name (~1180px
   with eight entries), it takes a row of its own and scrolls sideways, bled to
   the gutter edges so it reads as something you can push. It used to do this
   only on phones: from 721 to ~1100px the menu wrapped onto two or three
   lines, a header up to 211px tall on a tablet. The row's height is held
   before site.js fills it (it is empty until then), so the page does not jump
   down when it does — a layout shift Lighthouse measured at 0.058. */
@media(max-width:1179px){
  .social{margin-left:auto}
  .sections{
    order:3;width:100%;margin-left:0;
    flex-wrap:nowrap;overflow-x:auto;overscroll-behavior-x:contain;
    scrollbar-width:none;-ms-overflow-style:none;
    margin-inline:calc(var(--gutter) * -1);
    padding-inline:var(--gutter);
    padding-bottom:3px;
    min-height:31px;            /* one line of links: 24px text, 3px + 1px underline, 3px */
  }
  .sections::-webkit-scrollbar{display:none}
}
@media(max-width:720px){
  .top{gap:12px 14px;padding-top:22px}
}
/* Wide enough for the menu beside the name: one row that never wraps. Should
   the sections outgrow it (a new folder, a longer label), the menu scrolls
   sideways in its place rather than wrapping — the header's height must not
   depend on the data site.js fills it with. Padding keeps the focus ring and
   the underline clear of the scroller's clipping. */
@media(min-width:1180px){
  .top{flex-wrap:nowrap}
  .mark,.social{flex-shrink:0}
  .sections{flex-wrap:nowrap;min-width:0;overflow-x:auto;scrollbar-width:none;
    padding:5px 4px;margin-block:-5px;
    min-height:38px}            /* filled height, held while site.js fills it */
  /* The menu's box always spans the free space between the name and the
     icons, so filling it moves nothing; the links sit at its right end. An
     auto margin rather than justify-content:flex-end, which would overflow
     off the start, where a scroller cannot reach. */
  .sections{flex:1 1 auto;margin-left:0}
  .sections>:first-child{margin-left:auto}
  .sections::-webkit-scrollbar{display:none}
}

/* ── section head + facets ───────────────────────────────────────────────── */
main{max-width:var(--measure);margin:0 auto;padding:0 var(--gutter) 64px;
  /* A new block formatting context, so a first child's top margin stays inside
     rather than collapsing through and moving main itself: the lead arriving
     with its margin shifted the whole page 47px (a layout shift, 0.032). */
  display:flow-root}
/* Until the manifest arrives main is empty, so the footer draws at the top of
   the screen and is then shoved down by the grid. Hold a screen's height open
   until site.js has drawn the first view. */
.booting main{min-height:100vh}
/* ...and the footer is not shown at all until then. On a short page (About,
   Contact) it otherwise sat below the fold while booting and then moved up
   into view when main let go of that height — a layout shift of up to 0.037
   on a desktop. Appearing in place is not a shift; moving is. */
@media (scripting:enabled){.booting .foot{visibility:hidden}}   /* never without script: nothing would lift it */

.head{display:flex;align-items:baseline;gap:18px;flex-wrap:wrap;padding:clamp(28px,5vw,64px) 0 0}
.head h1{font-size:clamp(22px,3vw,30px);font-weight:400;margin:0;letter-spacing:-.02em;line-height:1.2}
/* The section's note reads as an introduction, as on Levon Biss's project pages
   (22.7px Poppins under the title), rather than a grey aside beside it. It sits on
   its own line after the title and count. */
.head .note{order:3;flex:0 0 100%;margin:6px 0 0;
  /* The line length is held by padding, not max-width: a max-width shrinks the
     flex basis too, and on a page with no count (Collections) the note then fit
     beside the title instead of under it. */
  padding-right:max(0px,calc(100% - 46ch));
  font-size:clamp(17px,1.55vw,22px);line-height:1.5;color:var(--ink)}
.head .count{margin-left:auto;font-size:12px;color:var(--dim);align-self:center}

.facets{display:flex;gap:10px;flex-wrap:wrap;align-items:center;padding:20px 0 0}
.facet{position:relative;display:inline-flex}
.facet select{
  font:inherit;font-size:12.5px;
  appearance:none;-webkit-appearance:none;
  background:none;border:1px solid var(--hair);border-radius:999px;
  color:var(--ink);padding:6px 30px 6px 13px;cursor:pointer;max-width:15rem;
}
.facet select:hover{border-color:var(--ink)}
.facet::after{
  content:"";position:absolute;right:13px;top:50%;width:5px;height:5px;
  border-right:1px solid var(--dim);border-bottom:1px solid var(--dim);
  transform:translateY(-70%) rotate(45deg);pointer-events:none;
}
.facet select option{background:var(--ground);color:var(--ink)}
.facet-clear{
  font:inherit;font-size:12.5px;background:none;border:0;color:var(--dim);
  cursor:pointer;text-decoration:underline;text-underline-offset:3px;padding:6px 2px;
}
.facet-clear:hover{color:var(--ink)}

/* ── "scroll" (site.js) ──────────────────────────────────────────────────── */
/* A quiet pill at the foot of the screen: the word, and a short line that
   draws downward and fades, over and over. Readable over a photograph as
   well as the ground (the pill is the ground, nearly opaque, blurred). */
.scroll-cue{
  position:fixed;left:50%;bottom:calc(18px + env(safe-area-inset-bottom,0px));z-index:5;
  transform:translate(-50%,8px);display:flex;align-items:center;gap:9px;
  font:inherit;font-size:11px;letter-spacing:.16em;text-transform:uppercase;line-height:1;
  color:var(--ink);background:color-mix(in srgb,var(--ground) 86%,transparent);
  border:1px solid var(--hair);border-radius:999px;padding:8px 14px 8px 16px;cursor:pointer;
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  opacity:0;pointer-events:none;visibility:hidden;
  transition:opacity .6s ease,transform .8s cubic-bezier(.2,.7,.2,1),visibility 0s linear .8s;
  -webkit-tap-highlight-color:transparent;
}
.scroll-cue.on{opacity:1;transform:translate(-50%,0);pointer-events:auto;visibility:visible;
  transition:opacity .9s ease .5s,transform 1.1s cubic-bezier(.2,.7,.2,1) .5s,visibility 0s}
.scroll-cue i{position:relative;display:block;width:1px;height:14px;overflow:hidden}
.scroll-cue i::after{content:"";position:absolute;left:0;top:0;width:1px;height:100%;
  background:currentColor;animation:cue-line 1.8s cubic-bezier(.6,0,.3,1) infinite}
@keyframes cue-line{0%{transform:translateY(-100%)}55%{transform:translateY(0)}100%{transform:translateY(100%)}}
.scroll-cue:hover{border-color:var(--ink)}
@media (prefers-reduced-motion:reduce){.scroll-cue i::after{animation:none;transform:none}}

/* ── one click to full size (site.js) ────────────────────────────────────── */
/* A small pill in a photograph's top-right corner, over the photograph, as
   its caption is. In the grid it is positioned at the cell's top-right corner
   by site.js and pulled inside by the transform; on the lead, by the figure. */
.full-chip{
  position:absolute;z-index:1;display:none;
  font:inherit;font-size:11px;letter-spacing:.08em;text-transform:uppercase;line-height:1;
  color:#fff;background:rgba(10,10,10,.55);border:1px solid rgba(255,255,255,.35);
  border-radius:999px;padding:7px 11px;cursor:zoom-in;white-space:nowrap;   /* at the grid's right edge it had no room and broke onto two lines */
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  opacity:0;transition:opacity .3s ease;
}
.rows>.full-chip{transform:translate(calc(-100% - 10px),10px)}
.lead-fig{position:relative}
.lead-fig>.full-chip{top:10px;right:10px}
.full-chip:hover{background:rgba(10,10,10,.75);border-color:#fff}
@media (hover:hover) and (pointer:fine){
  .full-chip{display:block}
  .cell:hover+.full-chip,.lead-frame:hover+.full-chip,.full-chip:hover{opacity:1}
}

/* ── the lead ────────────────────────────────────────────────────────────── */
/* The front page's first photograph, alone above the grid (site.js). Whole
   and uncropped, like every frame on the site: as wide as the page allows, but
   never taller than the first screen leaves room for — --room is the header
   above it, the caption below and a strip of the grid still showing, so the
   page reads as more to come. Sized from the aspect ratio (--a, from the
   manifest) before anything loads. svh after vh: the height with a phone's
   toolbar showing, so the frame does not grow and shrink as it hides. */
.lead{margin-top:clamp(24px,3.5vw,48px)}
.lead-fig{--room:260px;margin:0 auto;
  width:min(100%,calc((100vh - var(--room)) * var(--a)));
  width:min(100%,calc((100svh - var(--room)) * var(--a)))}
/* Below 1180px the menu has a row of its own, 51px more above the lead. */
@media(max-width:1179px){.lead-fig{--room:305px}}
@media(max-width:720px){.lead-fig{--room:190px}}
.lead-frame{
  position:relative;display:block;width:100%;aspect-ratio:var(--a);
  overflow:hidden;border:0;padding:0;cursor:zoom-in;
  background-size:cover;background-position:center;
  -webkit-tap-highlight-color:transparent;
}
.lead-frame::before{content:"";position:absolute;inset:-24px;background:inherit;filter:blur(20px)}
.lead-frame.loaded::before{content:none}
/* It arrives by settling: a slow ease from slightly close to its own size, as
   it fades up. Hover then eases it in a touch, like the grid's cells. */
.lead-frame img{position:relative;width:100%;height:100%;object-fit:cover;opacity:0;
  transition:transform .9s cubic-bezier(.2,.7,.2,1)}
/* The fade is on the image and short; the long settle is on a wrapper
   (.lead-zoom, clipped by the frame). Chrome times the largest paint from
   when an animated image stops animating: one 2.4s animation on the <img>
   recorded the lead 2.4s after it was on screen, now 0.7s. */
.lead-zoom{position:relative;display:block;width:100%;height:100%}
.lead-frame img.ready{opacity:1;animation:lead-fade .7s ease-out backwards}
.lead-frame.loaded .lead-zoom{animation:lead-settle 2.4s cubic-bezier(.16,1,.3,1) backwards}
@keyframes lead-fade{from{opacity:0}}
@keyframes lead-settle{from{transform:scale(1.045)}}
@media (hover:hover){.lead-frame:hover img.ready{transform:scale(1.015)}}
.lead-cap{display:flex;gap:6px 14px;flex-wrap:wrap;align-items:baseline;
  padding-top:12px;font-size:12.5px;color:var(--dim);
  opacity:0;transform:translateY(6px);
  transition:opacity .8s ease .5s,transform 1s cubic-bezier(.2,.7,.2,1) .5s}
.lead-cap b{font-weight:500;font-size:14px;color:var(--ink)}
.lead.in .lead-cap{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.lead-cap{opacity:1;transform:none}}

/* ── columns ─────────────────────────────────────────────────────────────── */
/* site.js places every cell absolutely from the manifest's aspect ratios and
   sizes this box to what is drawn. row-gap is not used for layout — it is how
   site.js reads --gap back resolved to pixels. */
.rows{position:relative;row-gap:var(--gap);margin-top:clamp(24px,3.5vw,48px)}
.cell{
  position:absolute;overflow:hidden;display:block;
  border:0;padding:0;cursor:zoom-in;background-size:cover;background-position:center;
  /* iOS flashes a grey box over anything tapped; over a photograph it looks broken. */
  -webkit-tap-highlight-color:transparent;
}
/* The placeholder is a 16px-wide thumbnail inlined in the manifest. Stretched to a
   430px cell it showed as blocky tiles of colour; blurred, it reads as a soft
   impression of the photograph until the real file fades in over it. The cell
   clips it, and inset:-24px keeps the blur's faded edge outside the frame. */
.cell::before{content:"";position:absolute;inset:-24px;background:inherit;filter:blur(20px)}
.cell.loaded::before,.card .shot.loaded::before{content:none}
.cell img{position:relative;width:100%;height:100%;object-fit:cover;opacity:0;
  transition:opacity .3s ease,transform .9s cubic-bezier(.2,.7,.2,1)}
.cell img.ready{opacity:1}
/* Rising into place as it is scrolled to (site.js adds .pre below the fold
   and takes it away on arrival). The cell a photograph is flying to or from
   is hidden at once, never faded — the stand-in is already covering it. */
.cell{transition:opacity .8s ease,transform 1.1s cubic-bezier(.16,1,.3,1)}
.cell.pre{opacity:0;transform:translateY(26px)}
.cell.away{transition:none}
/* ── the hover caption ─────────────────────────────────────────────────────
   Only where a pointer can hover (a phone's grid stays clean; a tap goes
   straight to the lightbox) and for keyboard focus. The photograph eases in by
   3.5% rather than dimming — dimming works against the picture, a slow push
   draws you into it — while a scrim rises from the foot of the frame, just dark
   enough to carry white type. Then, staggered: a hairline draws itself, the
   title rises, the place and date follow a beat later. The cell is a size
   container, so a narrow frame shows the title alone. */
.cell{container-type:inline-size}
.cap{
  position:absolute;left:0;right:0;bottom:0;
  display:flex;flex-direction:column;align-items:flex-start;gap:3px;
  padding:clamp(40px,22%,72px) 16px 14px;
  color:#fff;text-align:left;pointer-events:none;
  background:linear-gradient(to top,rgba(8,8,8,.74) 0%,rgba(8,8,8,.36) 40%,rgba(8,8,8,0) 100%);
  opacity:0;transition:opacity .4s ease;
  text-shadow:0 1px 10px rgba(0,0,0,.28);
}
.cap-rule{display:block;width:0;height:1px;margin-bottom:9px;background:rgba(255,255,255,.85);
  transition:width .55s cubic-bezier(.2,.7,.2,1) .05s}
.cap-title,.cap-meta{
  display:block;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;
  opacity:0;transform:translateY(8px);
  transition:opacity .45s ease,transform .6s cubic-bezier(.2,.7,.2,1);
}
.cap-title{font-size:15px;font-weight:500;letter-spacing:-.01em;line-height:1.3;transition-delay:.08s}
.cap-meta{font-size:12.5px;line-height:1.35;color:rgba(255,255,255,.82);transition-delay:.16s}
@container (max-width:230px){
  .cap{padding:34px 12px 10px}
  .cap-title{font-size:13px}
  .cap-meta{display:none}
  .cap-rule{margin-bottom:7px}
}
@media (hover:hover){
  .cell:hover img.ready{transform:scale(1.035)}
  .cell:hover .cap{opacity:1}
  .cell:hover .cap-rule{width:28px}
  .cell:hover .cap-title,.cell:hover .cap-meta{opacity:1;transform:none}
  /* The same while the pointer is on its Full size button (the cell's next
     sibling), so the caption does not fade as the pointer reaches for it. */
  .cell:has(+.full-chip:hover) img.ready{transform:scale(1.035)}
  .cell:has(+.full-chip:hover) .cap{opacity:1}
  .cell:has(+.full-chip:hover) .cap-rule{width:28px}
  .cell:has(+.full-chip:hover) .cap-title,.cell:has(+.full-chip:hover) .cap-meta{opacity:1;transform:none}
}
.cell:focus-visible .cap{opacity:1}
.cell:focus-visible .cap-rule{width:28px}
.cell:focus-visible .cap-title,.cell:focus-visible .cap-meta{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  .cell:hover img.ready{transform:none}
  .cap-title,.cap-meta{transform:none}
}

.more{display:flex;justify-content:center;padding:var(--gap) 0 0}
.more button{
  font:inherit;font-size:12.5px;background:none;border:1px solid var(--hair);
  color:var(--dim);border-radius:999px;padding:9px 22px;cursor:pointer;
}
.more button:hover{color:var(--ink);border-color:var(--ink)}

.empty{padding:50px 0;color:var(--dim);font-size:14px}

/* ── collection cards ────────────────────────────────────────────────────── */
.cards{display:grid;gap:var(--gap);padding:clamp(24px,3.5vw,48px) 0 0;grid-template-columns:1fr}
@media(min-width:620px){.cards{grid-template-columns:repeat(2,1fr)}}
@media(min-width:980px){.cards{grid-template-columns:repeat(3,1fr)}}
.card{
  display:block;text-decoration:none;background:none;border:0;padding:0;
  cursor:pointer;text-align:left;
}
.cards{align-items:start}
.card .shot{position:relative;overflow:hidden;background-size:cover;background-position:center}
.card .shot::before{content:"";position:absolute;inset:-24px;background:inherit;filter:blur(20px)}
.card .shot img{position:relative;width:100%;height:100%;opacity:0;transition:opacity .45s ease}
.card .shot img.ready{opacity:1}
.card b{display:block;font-weight:500;font-size:15px;padding:12px 0 2px}
.card span{font-size:12px;color:var(--dim)}

/* ── about ───────────────────────────────────────────────────────────────── */
.about{max-width:60ch;padding:clamp(28px,5vw,64px) 0 0}
.about h2{font-size:12px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--dim);font-weight:500;margin:0 0 20px}
.about p{font-size:16px;line-height:1.75;margin:0 0 16px}

/* ── contact ─────────────────────────────────────────────────────────────── */
.contact-form{display:grid;gap:18px;margin-top:28px}
.contact-form label{display:grid;gap:6px;font-size:12.5px;color:var(--dim)}
/* 16px: under that, iPhone Safari zooms the page when a field takes focus. */
.contact-form input,.contact-form textarea{
  font:inherit;font-size:16px;color:var(--ink);background:var(--ground);
  border:1px solid var(--hair);border-radius:0;padding:10px 12px;width:100%}
.contact-form textarea{resize:vertical;min-height:140px}
.contact-form input:focus,.contact-form textarea:focus{outline:none;border-color:var(--ink)}
.contact-form button{
  justify-self:start;font:inherit;font-size:12.5px;cursor:pointer;
  background:var(--ink);color:var(--ground);border:1px solid var(--ink);
  border-radius:999px;padding:9px 26px}
.contact-form button:disabled{opacity:.5;cursor:default}
.contact-form .trap{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
.about .contact-status{font-size:14px;color:var(--dim);margin:0;min-height:1.5em}

/* ── footer ──────────────────────────────────────────────────────────────── */
.foot{
  display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  max-width:var(--measure);margin:0 auto;
  padding:22px var(--gutter) 40px;
  border-top:1px solid var(--hair);
  font-size:13px;color:var(--dim);
}
.foot a{text-decoration:none;border-bottom:1px solid var(--hair)}
.foot a:hover{color:var(--ink)}

/* ── lightbox ────────────────────────────────────────────────────────────── */
.box{
  position:fixed;inset:0;z-index:90;background:var(--veil);
  display:flex;align-items:center;justify-content:center;
  padding:calc(18px + env(safe-area-inset-top,0px)) 16px
          calc(18px + env(safe-area-inset-bottom,0px));
}
.box-figure{margin:0;display:flex;flex-direction:column;align-items:center;
  gap:14px;max-width:100%;max-height:100%}
/* dvh after vh: on iOS Safari 100vh is the height with the toolbar hidden, so
   with it showing the caption sat underneath it. site.js reads max-height back
   to choose the file, so this is the one place the room is defined. */
.box-figure img{max-width:100%;max-height:calc(100vh - 150px);max-height:calc(100dvh - 150px);width:auto;height:auto;
  object-fit:contain;opacity:0;transition:opacity .2s ease}
/* Stepping swaps the caption immediately; without this the previous photograph
   stays on screen under the new title until the next one decodes. */
.box-figure img.ready{opacity:1}
.box-cap{display:flex;gap:16px;flex-wrap:wrap;justify-content:center;align-items:baseline;
  font-size:12.5px;color:var(--dim);text-align:center}
.box-cap b{font-weight:500;color:var(--ink);font-size:14px}
/* The camera line sits under the title, place and date, on its own line. */
/* The camera line and, when the original is here, its size — one row, under
   the title, place and date. */
.box-tech{flex-basis:100%;font-size:12px;letter-spacing:.01em}
.box-exif:empty,.box-size:empty{display:none}
.box-exif:not(:empty)+.box-size::before{content:" \00b7  "}
.box-close{
  position:absolute;top:calc(14px + env(safe-area-inset-top,0px));right:16px;
  font:inherit;font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  background:none;border:1px solid var(--hair);color:var(--dim);
  border-radius:999px;padding:6px 13px;cursor:pointer;
}
.box-close:hover{color:var(--ink);border-color:var(--ink)}
/* Drag down to close (site.js): the veil thins as the photograph is pulled away,
   via --drag (0–1). color-mix is ignored where unsupported, leaving the plain veil. */
.box{background:color-mix(in srgb, var(--veil) calc((1 - var(--drag, 0)) * 100%), transparent)}
.box.settling .box-figure{transition:transform .25s ease}
.card{-webkit-tap-highlight-color:transparent}

/* "Full size", left of Close. */
.box-zoom{
  position:absolute;top:calc(14px + env(safe-area-inset-top,0px));right:98px;z-index:2;
  font:inherit;font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  background:var(--ground);border:1px solid var(--hair);color:var(--dim);
  border-radius:999px;padding:6px 13px;cursor:pointer;
}
.box-zoom:hover{color:var(--ink);border-color:var(--ink)}
.box.can-zoom .box-figure img{cursor:zoom-in}
/* A double-tap is the lightbox's own (look closer), not the page's zoom. */
.box{touch-action:manipulation}

/* The loupe (site.js): a round glass of original pixels following the pointer.
   It is positioned by transform from the top-left corner, and never takes the
   pointer, so the click beneath still opens Full size. */
.loupe{position:fixed;left:0;top:0;z-index:3;width:clamp(180px,20vw,300px);aspect-ratio:1;
  border-radius:50%;pointer-events:none;background-repeat:no-repeat;background-color:var(--ground);
  box-shadow:0 0 0 1px rgba(255,255,255,.9),0 0 0 2px rgba(0,0,0,.18),0 12px 34px rgba(0,0,0,.28);
  display:flex;align-items:flex-end;justify-content:center;overflow:hidden}
.loupe.soft{filter:saturate(.92)}
.loupe-tag{margin-bottom:12%;font-size:11px;letter-spacing:.06em;color:#fff;
  background:rgba(0,0,0,.52);border-radius:999px;padding:3px 10px;white-space:nowrap}
.box.zoomed .loupe{display:none}

/* Full size: the figure becomes a scroller over the whole veil and the image
   drops its screen-fitting limits. Auto margins in a flex box centre a frame
   smaller than the screen without clipping one that is larger. */
.box.zoomed{padding:0}
.box.zoomed .box-figure{position:fixed;inset:0;display:flex;max-width:none;max-height:none;
  overflow:auto;overscroll-behavior:contain;cursor:grab;
  scrollbar-width:none}                       /* dragging is the way around; no bars across the photograph */
.box.zoomed .box-figure::-webkit-scrollbar{display:none}
.box.zoomed .box-figure img{max-width:none;max-height:none;margin:auto;flex:0 0 auto;cursor:grab;
  -webkit-user-drag:none;user-select:none}
.box.zoomed.panning .box-figure,.box.zoomed.panning .box-figure img{cursor:grabbing}
.box.zoomed .box-cap,.box.zoomed .box-nav{display:none}
.box.zoomed .box-close,.box.zoomed .box-zoom{z-index:2;background:var(--ground)}
@keyframes pulse{50%{opacity:.45}}
.box.loading-full .box-zoom{animation:pulse 1.2s ease-in-out infinite}
.box-nav{
  position:absolute;top:50%;transform:translateY(-50%);
  font:inherit;font-size:19px;line-height:1;
  background:none;border:1px solid var(--hair);color:var(--dim);
  width:44px;height:44px;border-radius:50%;cursor:pointer;
}
.box-nav:hover{color:var(--ink);border-color:var(--ink)}
/* The flight (site.js): a stand-in for the photograph, carried between its
   cell and the lightbox above everything else; while it is in the air the
   lightbox's own image and the cell it left stand empty. Closing, a copy of the
   veil fades out under it. Neither takes a click. */
.fx-ghost{position:fixed;z-index:95;margin:0;transform-origin:0 0;object-fit:cover;
  pointer-events:none;will-change:transform}
.fx-veil{position:fixed;inset:0;z-index:89;pointer-events:none}
.box.flying .box-figure img{visibility:hidden}
/* The cell it left: transparent, not visibility:hidden — a hidden element
   cannot hold focus, and closing hands focus back to this very cell. */
.away{opacity:0}
.box-prev{left:12px}
.box-next{right:12px}
@media(max-width:600px){
  .box-nav{top:auto;bottom:calc(16px + env(safe-area-inset-bottom,0px));transform:none}
  .box-figure img{max-height:calc(100vh - 210px);max-height:calc(100dvh - 210px)}
  /* On a phone the photograph runs edge to edge — every pixel of width goes to it —
     and the caption keeps the margin. site.js reads this padding back, so the file
     chosen is sized for the full width. The title takes its own line, so the
     caption reads the same on every phone: title / place · date · count / camera. */
  .box{padding-left:0;padding-right:0}
  .box-cap{padding:0 16px;row-gap:6px}
  .box-cap b{flex-basis:100%}
}
