/* ============================================================================
   THE PHOTO ALBUM - chapters of prints laid on a page, and a camcorder.

   PLATES KEEP THEIR OWN SHAPE. In a flex row, `flex: <ar> 1 0` makes each
   plate's width proportional to its aspect ratio, and `aspect-ratio: <ar>` then
   gives them all the SAME height without a single number being written down.
   So a row of a landscape and a portrait sits flush top and bottom while both
   photographs are still the shape they were taken in. The alternative, a grid
   of fixed cells with object-fit:cover, crops somebody's head off in a set
   nobody has curated yet.

   WHY A CAMCORDER AND NOT A TELEVISION. The couple asked for an analog TV of
   memories. Their footage is mostly 1080x1920 and a TV is landscape 4:3, so the
   frame would fight the content on every clip. A viewfinder is vertical-native,
   just as nostalgic, and it belongs INSIDE the album rather than standing next
   to it as a second device.
   ============================================================================ */
/* narrower than the section's own measure: at 60rem a single portrait plate
   stood taller than the viewport and the album read as a slideshow */
.pb{margin:clamp(2.5rem,6vw,4.5rem) auto 0;width:min(100%,62rem)}

/* ---- the book ----------------------------------------------------------
   A LEAF, NOT A SLIDE. Each leaf carries an odd page on its front and the next
   even one on its back and turns about the spine, so one turn reveals two new
   pages exactly as paper does. The alternative, cross-fading one page at a
   time, is a slideshow with a book's furniture bolted on.

   Depth comes from `perspective` on the frame rather than on each leaf: set on
   the leaves, every one gets its own vanishing point and the page swings
   through an arc that does not belong to the book it is part of.
   ------------------------------------------------------------------------ */
.pb-book{position:relative;perspective:2600px;perspective-origin:50% 46%}
.pb-stage{position:relative;display:grid;grid-template-columns:1fr 1fr;
  aspect-ratio:3/2;border-radius:3px;background:var(--paper);
  box-shadow:0 1px 0 rgba(255,255,255,.7) inset,
             0 18px 40px -26px rgba(36,28,23,.5),
             0 1px 2px rgba(36,28,23,.08)}
/* the gutter: two pages meeting, and the shadow they throw into each other */
/* above the leaves, whose own z-index runs to the number of them */
.pb-stage::after{content:"";position:absolute;top:0;bottom:0;left:50%;width:6%;
  transform:translateX(-50%);pointer-events:none;z-index:calc(var(--leaves) + 5);
  background:linear-gradient(90deg,transparent,rgba(36,28,23,.09) 46%,
             rgba(36,28,23,.13) 50%,rgba(36,28,23,.06) 54%,transparent)}

/* THE Z-INDEX CHANGES AT THE HALFWAY POINT, NOT AT THE START. Swapped with the
   class, the leaf drops behind the stack the instant you click and the page
   underneath is revealed whole before the paper has moved: what you see is the
   new photograph appearing and then a flap waving over it. z-index is not
   interpolated, but it can be transitioned, and a zero-length transition
   delayed to the moment the leaf is edge-on moves it when nobody can tell.

   THE PAGE LIFTS AS YOU CLICK. The curve was cubic-bezier(.36,.05,.26,1),
   which turned the leaf 10 degrees in the first tenth of a second and read
   as a quarter-second of nothing followed by a lurch: a heavy book. This one
   is 40 degrees in the first 100ms, the flick of a hand, and still settles
   softly. Edge-on falls at 187ms of the 750 on this curve, which is where the
   z-index delay has to sit; change one and the other has to be recomputed. */
.pb-leaf{position:absolute;top:0;right:0;width:50%;height:100%;
  transform-origin:left center;transform-style:preserve-3d;
  z-index:calc(var(--leaves) - var(--i));
  transition:transform .75s cubic-bezier(.3,.35,.2,1), z-index 0s .187s}
.pb-leaf.is-turned{transform:rotateY(-180deg);z-index:var(--i)}
/* out of reach of the open spread: not in the page at all (see album.js) */
.pb-leaf.is-far{display:none}
.pb-face{position:absolute;inset:0;overflow:hidden;
  backface-visibility:hidden;-webkit-backface-visibility:hidden;
  background:var(--paper)}
.pb-back{transform:rotateY(180deg)}
/* paper is not lit evenly across a spread: it falls away into the gutter */
.pb-front{box-shadow:inset 14px 0 22px -16px rgba(36,28,23,.5)}
.pb-back {box-shadow:inset -14px 0 22px -16px rgba(36,28,23,.5)}

/* ---- a page ------------------------------------------------------------ */
.pb-page{position:absolute;inset:0;display:flex;flex-direction:column;
  padding:clamp(.9rem,2.4%,2rem) clamp(.9rem,2.6%,2.1rem)}
.pb-page-year{margin:0 0 clamp(.5rem,1.4%,1rem);flex:none;text-align:center;
  font-family:var(--font-ui);font-size:clamp(9px,1.1vw,11.5px);font-weight:600;
  letter-spacing:.3em;text-transform:uppercase;color:var(--olive);opacity:.85}
.pb-folio{margin:clamp(.4rem,1.2%,.9rem) 0 0;flex:none;text-align:center;
  font-family:var(--font-ui);font-size:clamp(7px,.85vw,9px);
  letter-spacing:.14em;color:var(--ink-faint)}

/* THE PHOTOGRAPH SIZES ITSELF, and the mount goes. Scrolling, the page was as
   tall as its contents and a fixed-aspect box worked; in a book the page is a
   fixed size, and a box with both a definite height and a max-width cannot
   honour an aspect ratio, so it stretched and the image cropped. Letting the
   <img> fit itself inside the space with max-width and max-height instead
   means the print is never cut, and the shadow that made the mount read as
   paper simply moves onto the photograph. */
/* STRETCH, and let flex divide the height. `align-items:center` with
   `height:100%` on each plate was written for a row of two and doubled up the
   moment they stacked: two full-height plates in a column is 200% of the page,
   and the second photograph was simply clipped off the bottom. */
.pb-plates{flex:1;min-height:0;display:flex;align-items:stretch;
  justify-content:center;gap:clamp(6px,1.8%,18px)}
.pb-page[data-lay="stack"] .pb-plates{flex-direction:column}
.pb-page[data-lay="side"] .pb-plates{flex-direction:row}
/* three to a page: a pair across the top and the third centred beneath them,
   which keeps the two that belong together side by side */
.pb-page[data-lay="trio"] .pb-plates{display:grid;
  grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;
  justify-items:center}
.pb-page[data-lay="trio"] .pb-plate{height:auto;min-height:0;width:100%}
.pb-page[data-lay="trio"] .pb-plate:last-child{grid-column:1 / -1;
  width:min(100%,62%)}
.pb-plate{display:flex;flex-direction:column;align-items:center;
  justify-content:center;min-height:0;min-width:0;margin:0;rotate:none;
  flex:1 1 0;height:auto}
.pb-page .pb-shot{flex:1;min-height:0;width:100%;
  display:flex;align-items:center;justify-content:center;
  background:none;box-shadow:none;border-radius:0;overflow:visible;
  aspect-ratio:auto}
.pb-page .pb-shot img{width:auto;height:auto;max-width:100%;max-height:100%;
  object-fit:contain;border-radius:2px;
  box-shadow:0 0 0 1px rgba(36,28,23,.07), 0 7px 16px -11px rgba(36,28,23,.55)}
/* A FRAME FITS THE WAY A PHOTOGRAPH DOES. A lone photograph is an <img> with
   max-width and max-height, which the browser shrinks to fit keeping its
   shape. A carousel and a camcorder are boxes with an aspect-ratio instead,
   and those were sized from the page's HEIGHT: wherever the width ran out
   first, the box gave way rather than the size, and object-fit:cover cropped
   what was left. 39 of them in the book: every portrait carousel sharing a
   page came out 0.41 instead of 0.75, and every landscape one alone on a page
   came out portrait, most of its width gone.

   So the plate is a size container and the frame takes whichever binds:
   the full width, or the full height less what sits under it (the caption,
   and a carousel's dots), times its shape. That is the largest box of the
   right shape that fits, which is what the <img> gets for free. */
.pb-plate{container-type:size}
.pb-page .pb-cam,.pb-page .pb-shot.pb-multi,.pb-page .pb-shot.pb-crop{--below:1.4rem;flex:none;height:auto;
  width:min(100cqw,(100cqh - var(--below)) * var(--ar));max-width:none;
  aspect-ratio:var(--ar);display:block}
.pb-page .pb-shot.pb-multi{--below:2.3rem}
.pb-plate figcaption{margin-top:.4rem;flex:none;
  font-family:var(--font-ui);font-size:clamp(6.5px,.8vw,8.5px);font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-faint);
  text-align:center}

/* ---- the title page ---------------------------------------------------- */
.pb-page-title{align-items:center;justify-content:center;text-align:center;
  gap:clamp(.5rem,1.6%,1.1rem)}
.pb-title-eyebrow{margin:0;font-family:var(--font-ui);
  font-size:clamp(7.5px,.95vw,10px);font-weight:600;letter-spacing:.3em;
  text-transform:uppercase;color:var(--olive)}
.pb-title-names{margin:0;font-family:var(--font-script);
  font-size:clamp(2.2rem,7vw,4.4rem);line-height:1;color:var(--ink)}
.pb-title-rule{margin:0;width:38px;height:1px;background:var(--rule-ink)}
.pb-title-span{margin:0;font-family:var(--font-display);font-style:italic;
  font-size:clamp(.9rem,1.7vw,1.15rem);color:var(--ink-mute)}
.pb-page-blank{background:var(--paper)}

/* ---- turning ------------------------------------------------------------ */
/* ABOVE EVERY LEAF. This was a fixed 45, which cleared the stack while the book
   had eighteen leaves and was buried the moment it had seventy-two: the pages
   themselves swallowed the clicks meant to turn them. */
/* THE OUTER MARGINS ONLY. At 46% each they covered the photographs, and
   since a leaf is its own stacking context nothing inside it can ever be
   raised above them: a click meant for a picture was always a page turn.
   The arrows under the book do the rest. */
.pb-turn{position:absolute;top:0;bottom:0;width:17%;z-index:calc(var(--leaves) + 10);
  appearance:none;border:0;background:transparent;cursor:pointer;padding:0}
.pb-prev{left:0}
.pb-next{right:0}
.pb-turn:disabled{cursor:default}
.pb-turn:focus-visible{outline:2px solid var(--olive);outline-offset:-4px}
/* A DOG-EAR, NOT A WEDGE. The first version was a pale triangle on pale paper,
   36px on a book 1240px wide, and on desktop nobody saw it. It also could not
   cast a shadow: clip-path is applied after filter, so the drop-shadow that
   was meant to lift it was clipped away with everything else. So it is drawn
   as a picture of a corner folded over: the flap, the shadow the flap throws
   onto the page, and the shadowed paper where the corner used to be. The box
   is a sixth larger than the fold, which is the room the shadow needs.

   Scaled from the page's own corner, so it grows out of the edge rather than
   sliding in. */
.pb-turn::after{content:"";position:absolute;bottom:0;
  width:clamp(40px,4.4vw,70px);aspect-ratio:1;opacity:0;
  background:no-repeat 0 0/100% 100%;
  transition:opacity .28s ease,transform .35s cubic-bezier(.16,1,.3,1)}
.pb-next::after{right:0;transform-origin:100% 100%;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 120'%3E%3Cdefs%3E%3ClinearGradient id='u' x1='1' y1='1' x2='.42' y2='.42'%3E%3Cstop offset='0' stop-color='%23ece3d3'/%3E%3Cstop offset='1' stop-color='%23c4b596'/%3E%3C/linearGradient%3E%3ClinearGradient id='f' x1='.05' y1='.05' x2='.55' y2='.55'%3E%3Cstop offset='0' stop-color='%23e2d7c3'/%3E%3Cstop offset='.75' stop-color='%23fcf9f3'/%3E%3Cstop offset='1' stop-color='%23f3ecdf'/%3E%3C/linearGradient%3E%3Cfilter id='s' x='-40%25' y='-40%25' width='180%25' height='180%25'%3E%3CfeGaussianBlur stdDeviation='3.5'/%3E%3C/filter%3E%3C/defs%3E%3Cpath d='M120 20 L120 120 L20 120 Z' fill='url%28%23u%29'/%3E%3Cpath d='M20 120 L120 20 Q68 27 21 21 Q27 68 20 120 Z' transform='translate%28-4 -4%29' fill='%23241c17' opacity='.34' filter='url%28%23s%29'/%3E%3Cpath d='M20 120 L120 20 Q68 27 21 21 Q27 68 20 120 Z' fill='url%28%23f%29'/%3E%3Cpath d='M20 120 L120 20' stroke='%23fff' stroke-opacity='.8' stroke-width='1.2'/%3E%3C/svg%3E")}
.pb-prev::after{left:0;transform-origin:0 100%;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 120'%3E%3Cdefs%3E%3ClinearGradient id='u' x1='1' y1='1' x2='.42' y2='.42'%3E%3Cstop offset='0' stop-color='%23ece3d3'/%3E%3Cstop offset='1' stop-color='%23c4b596'/%3E%3C/linearGradient%3E%3ClinearGradient id='f' x1='.05' y1='.05' x2='.55' y2='.55'%3E%3Cstop offset='0' stop-color='%23e2d7c3'/%3E%3Cstop offset='.75' stop-color='%23fcf9f3'/%3E%3Cstop offset='1' stop-color='%23f3ecdf'/%3E%3C/linearGradient%3E%3Cfilter id='s' x='-40%25' y='-40%25' width='180%25' height='180%25'%3E%3CfeGaussianBlur stdDeviation='3.5'/%3E%3C/filter%3E%3C/defs%3E%3Cg transform='matrix%28-1 0 0 1 120 0%29'%3E%3Cpath d='M120 20 L120 120 L20 120 Z' fill='url%28%23u%29'/%3E%3Cpath d='M20 120 L120 20 Q68 27 21 21 Q27 68 20 120 Z' transform='translate%28-4 -4%29' fill='%23241c17' opacity='.34' filter='url%28%23s%29'/%3E%3Cpath d='M20 120 L120 20 Q68 27 21 21 Q27 68 20 120 Z' fill='url%28%23f%29'/%3E%3Cpath d='M20 120 L120 20' stroke='%23fff' stroke-opacity='.8' stroke-width='1.2'/%3E%3C/g%3E%3C/svg%3E")}
/* THE NEXT CORNER IS ALWAYS A LITTLE LIFTED. A hover-only cue never appears
   on a phone, which has no hover, so a guest was left to guess that the page
   turns at all. At rest it is smaller, the way a well-thumbed page curls on
   its own, and a pointer or focus lifts it fully. Only the leading edge: a
   real page does not curl on both sides, and anyone who has turned forward
   once knows the way back. */
.pb-next:not(:disabled)::after{opacity:1;transform:scale(.8)}
.pb-turn:hover:not(:disabled)::after,
.pb-turn:focus-visible:not(:disabled)::after{opacity:1;transform:scale(1.1)}
/* THE NUDGE. Once per visit, when the book first comes into view, the corner
   lifts twice as if a thumb were at it. js/album.js sets the class and takes
   it off again, so it never replays on a page you have already turned. */
.pb.is-nudging .pb-next:not(:disabled)::after{animation:pbNudge 1.3s ease-in-out 2}
@keyframes pbNudge{
  0%,100%{transform:scale(.8)}
  38%    {transform:scale(1.45)}
  58%    {transform:scale(1.25)}
}

/* ---- the strip underneath ---------------------------------------------- */
.pb-nav{display:flex;flex-direction:column;align-items:center;gap:.7rem;
  margin-top:clamp(1rem,2.5vw,1.8rem)}
.pb-jumps{display:flex;flex-wrap:wrap;justify-content:center;
  gap:clamp(.25rem,1.2vw,.9rem)}
.pb-jump{appearance:none;border:0;background:transparent;cursor:pointer;
  padding:.5rem .55rem;min-height:40px;position:relative;
  font-family:var(--font-ui);font-size:10px;font-weight:600;letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-faint);transition:color .25s ease}
.pb-jump:hover{color:var(--olive)}
.pb-jump[aria-current="true"]{color:var(--olive)}
.pb-jump[aria-current="true"]::after{content:"";position:absolute;left:.55rem;
  right:.55rem;bottom:.3rem;height:1px;background:var(--olive);opacity:.6}
.pb-jump:focus-visible{outline:2px solid var(--olive);outline-offset:2px}
/* HEARD, NOT SEEN. The folios are printed on the pages themselves, so a
   second "Pages 17 and 18" under the book was saying it twice. It stays in
   the markup as the live region that tells a screen reader a page turned. */
.pb-count{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

@media (max-width:760px){
  /* ONE PAGE AT A TIME, TURNED LIKE PAPER. A two-page spread on a phone halves
     every photograph, so a phone shows one page. It used to do that by spinning
     a whole leaf about its own centre, front to back, which kept it on screen
     but read as a card being flipped over rather than a page being turned.

     So here every page is its own sheet, stacked in reading order with the
     earliest on top, and the leaves stop being anything but containers. The
     page you are leaving lifts at its right edge and turns over towards the
     spine on the left; the next one is already lying underneath. Going back,
     it swings in from the left and settles on top again. Past 90 degrees a
     sheet is edge-on and its back is hidden, so it never leaves the stage.

     A LEAF MUST NOT BE A STACKING CONTEXT HERE. Its front and back have to sit
     in one stack with every other page, so the leaf gives up its transform,
     its z-index and preserve-3d, each of which would box its two pages in.
     The perspective goes into the page's own transform for the same reason:
     nothing above it is 3D any more. js/album.js numbers the pages as --f. */
  .pb-stage{grid-template-columns:1fr;aspect-ratio:3/4}
  .pb-stage::after{display:none}
  .pb-leaf,.pb-leaf.is-turned,.pb-leaf.is-current{top:0;left:0;right:0;bottom:auto;
    width:100%;height:100%;transform:none;transform-style:flat;z-index:auto;
    transition:none}
  .pb-front,.pb-back{box-shadow:none;transform-origin:left center;
    z-index:calc(var(--leaves) * 2 - var(--f,0));
    transform:perspective(1800px) rotateY(0deg);
    transition:transform .75s cubic-bezier(.3,.35,.2,1), visibility 0s}
  /* A TURNED PAGE IS PUT AWAY, not just turned face-down. Lying at -180deg it
     sits off the book's left edge, and backface-visibility hides the page but
     not what the browser paints on layers of their own: every clip and every
     carousel on every page already turned was still drawn there, a stack of
     videos peeking out beside the book. So once the turn is over it goes to
     visibility:hidden. Turning back, the base rule's zero delay shows it
     again before it starts to move. */
  .pb-face.is-gone{transform:perspective(1800px) rotateY(-180deg);visibility:hidden;
    transition:transform .75s cubic-bezier(.3,.35,.2,1), visibility 0s .75s}
  /* above every page, which is twice as many as there are leaves */
  .pb-turn{top:0;bottom:0;width:22%;z-index:calc(var(--leaves) * 2 + 10)}
  .pb-prev{left:0}
  .pb-next{right:0}
  .pb-page{padding:clamp(.8rem,3%,1.4rem) clamp(.8rem,3.2%,1.5rem)}
}

@media (prefers-reduced-motion:reduce){
  .pb-leaf,.pb-front,.pb-back{transition:none}
  .pb-turn::after{transition:none}
  .pb.is-nudging .pb-next:not(:disabled)::after{animation:none}
}

/* ---- the camcorder ------------------------------------------------------ */
/* A BODY, NOT A BORDER. Everything else on this page is a print laid on paper;
   these are a device, and at a glance the only thing telling them apart was the
   REC overlay. So the viewfinder gets the casing around it: a dark moulding
   over the edge of the picture with a lit inner lip, which is what the bezel of
   a flip-out screen looks like.

   DRAWN ON A PSEUDO-ELEMENT, not as an inset shadow. An inset box-shadow paints
   above the background but BELOW the content, so under a <video> filling the
   box it is invisible. It also cannot be an outset ring, the way the television
   does it: these plates sit in a flex row whose heights are derived from their
   widths, and a ring outside the box would eat the gap between them. */
/* NO PERCENTAGES IN A BORDER WIDTH. This was `clamp(3px, 1.7%, 9px)`, which is
   a valid length anywhere a percentage is allowed and invalid for
   `border-width`, so the whole shorthand was thrown away and the computed value
   came back `0px none`. Nothing drew. The same variable reads fine inside the
   `inset: calc(6% + ...)` below, which is why the overlay had moved but the
   casing had not appeared. Sized off the viewport instead. */
/* Thin and well rounded, which is what the surround of a flip-out screen
   actually is. The first visible version was nearly three times this and read
   as a picture frame rather than a housing. */
.pb-cam{--bezel:clamp(2px,0.34vw,5px);
  position:relative;background:#171310;
  border-radius:calc(var(--bezel) + 9px);
  box-shadow:0 0 0 1px rgba(36,28,23,.24), 0 9px 20px -12px rgba(36,28,23,.55)}
/* graphite, not near-black: against the vignetted edge of a clip a dark casing
   is the same colour as the picture and reads as nothing at all */
.pb-cam::before{content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  box-sizing:border-box;
  border:var(--bezel) solid #3d3831;
  border-radius:calc(var(--bezel) + 9px);
  box-shadow:inset 0 0 0 1px rgba(255,248,236,.16),
             0 0 0 1px rgba(36,28,23,.34)}
/* THE VIDEO ROUNDS ITS OWN CORNERS. `overflow:hidden` plus a radius on the
   housing ought to be enough, but a <video> gets promoted to its own
   compositing layer and Chrome does not always apply an ancestor's rounded
   clip to it: square corners poked out past the bezel wherever the picture was
   pale enough to see them. Rounding the element itself is clipped by the
   element's own painter, so it always holds. */
/* EVERY VIDEO IN A VIEWFINDER, not just the album's. This was `.pb-vid`, which
   the engagement tapes do not carry: they are `.eng-vid`, so they had no sizing
   rule at all and were laying out at their intrinsic 720x1280 inside a 330px
   housing, clipped to the top-left corner. The framing looked zoomed because it
   was: you were seeing a 1:1 crop of the corner of the picture. */
/* AND IT MUST NOT BE HANDED TO A HARDWARE OVERLAY. The plate sits inside a
   `.pb-face`, which is `backface-visibility:hidden` so the reverse of a leaf
   does not show through. On Windows that puts the clip on Chrome's
   DirectComposition overlay path: the compositor punches a hole for the video
   and the backface-hidden layer is drawn over the hole, so the clip decodes
   normally, its timecode runs, and the picture stays black. A filter on the
   element disqualifies it from that path, and it is composited as an ordinary
   texture like everything else on the page. The grade is real rather than a
   no-op, because an identity filter can be optimised back out: tape stock is a
   shade flatter than a phone camera, which is what the viewfinder wants. */
.pb-cam > video{filter:saturate(.96);
  border-radius:inherit;
  display:block;width:100%;height:100%;object-fit:cover;
  /* the poster again, as a background. A <video> drops its poster the instant
     a load begins, so a clip that has fetched but not decoded showed black. */
  background-size:cover;background-position:center}
/* the viewfinder's corner brackets, four gradients on one element rather than
   four more nodes */
.pb-cam-frame{position:absolute;inset:calc(5% + var(--bezel));pointer-events:none;
  --b:rgba(255,252,245,.82);--t:1.5px;--l:14px;
  background:
    linear-gradient(var(--b),var(--b)) 0 0/var(--l) var(--t) no-repeat,
    linear-gradient(var(--b),var(--b)) 0 0/var(--t) var(--l) no-repeat,
    linear-gradient(var(--b),var(--b)) 100% 0/var(--l) var(--t) no-repeat,
    linear-gradient(var(--b),var(--b)) 100% 0/var(--t) var(--l) no-repeat,
    linear-gradient(var(--b),var(--b)) 0 100%/var(--l) var(--t) no-repeat,
    linear-gradient(var(--b),var(--b)) 0 100%/var(--t) var(--l) no-repeat,
    linear-gradient(var(--b),var(--b)) 100% 100%/var(--l) var(--t) no-repeat,
    linear-gradient(var(--b),var(--b)) 100% 100%/var(--t) var(--l) no-repeat}
.pb-cam-ui{position:absolute;inset:calc(5% + var(--bezel));pointer-events:none;
  display:grid;grid-template-columns:auto 1fr auto;align-content:space-between;
  padding:5% 6%;
  font-family:var(--font-mono,ui-monospace,monospace);font-size:clamp(7px,1.5vw,10px);
  letter-spacing:.12em;color:rgba(255,252,245,.9);
  text-shadow:0 1px 3px rgba(0,0,0,.6)}
.pb-rec{grid-column:1;grid-row:1;display:flex;align-items:center;gap:.4em}
.pb-rec i{width:.62em;height:.62em;border-radius:50%;background:#d8443c;
  box-shadow:0 0 6px rgba(216,68,60,.8)}
.pb-tc{grid-column:3;grid-row:1;justify-self:end;font-variant-numeric:tabular-nums}
/* the date is stamped on the picture, the way a camcorder does it */
.pb-date{grid-column:1;grid-row:2;align-self:end;letter-spacing:.16em}
.pb-bat{grid-column:3;grid-row:2;justify-self:end;
  width:1.5em;height:.78em;border:1px solid rgba(255,252,245,.8);border-radius:1px;
  position:relative}
.pb-bat::before{content:"";position:absolute;inset:1.5px;right:38%;
  background:rgba(255,252,245,.85)}
.pb-bat::after{content:"";position:absolute;left:100%;top:26%;bottom:26%;width:1.5px;
  background:rgba(255,252,245,.8)}
/* a viewfinder is never perfectly clean */
.pb-cam::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:radial-gradient(ellipse at 50% 45%,transparent 52%,rgba(0,0,0,.42) 100%)}

@media (prefers-reduced-motion:no-preference){
  .pb-rec i{animation:pbRec 1.9s steps(1,end) infinite}
  @keyframes pbRec{0%,55%{opacity:1}56%,100%{opacity:.12}}
  /* the prints land on the page as it comes into view */
  @supports (animation-timeline:view()){
    .pb-plate{animation:pbDrop linear both;
      animation-timeline:view();animation-range:entry 4% entry 46%}
    @keyframes pbDrop{
      from{opacity:0;transform:translateY(22px) scale(.975)}
      to  {opacity:1;transform:none}}
  }
}

/* The `max-width:640px` block that used to sit here belonged to the scrolling
   album, whose `.pb-row`, `.pb-spread` and `.pb-host` no longer exist. One rule
   in it outlived them and did real damage: `.pb-plate{flex:none;width:100%}`
   overrode the book's `flex:1 1 0`, so on a phone the two plates of a
   side-by-side page each took the full page width, overflowed it by half a page
   in both directions and were clipped by the face. A page is a fixed box now
   and the plates share it by flexing, at every width. */

@media (prefers-reduced-motion:reduce){
  .pb-plate{animation:none;opacity:1;transform:none}
  .pb-rec i{animation:none}
}


/* ============================================================================
   THE ENGAGEMENT - a television, and two tapes under it.

   THE TUBE IS 7:5 AND THE FILM IS 16:9, so it letterboxes a little. A strict
   4:3 spent a quarter of the screen on black; this still reads as a boxy set
   and gives most of that height back. It is also why the album's clips are in
   a camcorder and not in here: they are vertical, and a vertical phone video
   in a tube is two black walls and a sliver.

   THE DIALS WORK. One is play, one is volume, and the volume one is a real
   rotary control you can drag or arrow through, because a television with
   painted-on knobs is a photograph of a television.
   ============================================================================ */
.eng{display:flex;flex-direction:column;align-items:center;
  gap:clamp(2rem,4.5vw,3.4rem);margin:clamp(2.4rem,5vw,4rem) auto 0}
.eng-piece{margin:0;display:flex;flex-direction:column;align-items:center}
.eng-tv{width:min(100%,46rem)}
/* the pair spans the set: two tapes and the gap between them come to the same
   width as the television above, so the section reads as one object */
.eng-tapes{display:flex;flex-wrap:wrap;justify-content:center;
  gap:clamp(1.2rem,3vw,2.4rem);width:min(100%,46rem)}
.eng-tape{flex:1 1 0;min-width:min(100%,12rem)}

/* ---- the set ------------------------------------------------------------ */
.eng-set{position:relative;width:100%;padding-bottom:clamp(10px,1.6vw,18px)}
/* walnut, built from bands rather than a texture file */
/* the moulding scales with the viewport, and on a phone the floor was doing
   all the work: 11px of ivory around a 330px tube is a picture frame */
.eng-case{--ring:clamp(5px,1.35vw,17px);
  position:relative;display:grid;
  grid-template-columns:1fr clamp(52px,11%,86px);
  gap:clamp(8px,1.4%,16px);align-items:stretch;
  padding:clamp(8px,1.9%,20px);border-radius:18px/15px;
  background:
    repeating-linear-gradient(94deg, rgba(60,34,18,.10) 0 2px,
                                     rgba(60,34,18,0) 2px 7px),
    repeating-linear-gradient(88deg, rgba(255,232,196,.10) 0 1px,
                                     rgba(255,232,196,0) 1px 11px),
    linear-gradient(165deg,#8a5a34,#6f4526 46%,#5a3620);
  box-shadow:0 2px 0 rgba(255,214,166,.28) inset,
             0 -3px 8px rgba(0,0,0,.4) inset,
             0 22px 38px -24px rgba(36,28,23,.7),
             0 2px 4px rgba(36,28,23,.2)}
/* the bezel the tube is set into */
/* A TUBE, NOT A PANEL. The first pass was a thin ring round a sharp-cornered
   black rectangle, which is a tablet lying on a plank. What says cathode ray is
   the corners: real picture tubes are almost barrel-shaped, so the radius is a
   percentage and a large one, and the moulding round it is thick ivory plastic
   rather than a hairline.

   THE BEZEL IS AN OUTSET SHADOW, so it paints OUTSIDE the element's box and
   into the cabinet's padding. Boxes that measured a symmetrical 19px of wood
   all round were showing 6px beside the tube and 19px beside the panel. The
   screen is inset by the width of its own moulding, so what you see is even. */
.eng-screen{position:relative;aspect-ratio:7/5;overflow:hidden;
  margin:var(--ring);
  border-radius:9% / 12%;
  background:#14120f;                      /* a dark tube is never pure black */
  box-shadow:
    0 0 0 2px #100e0c,                     /* the lip the glass sits down into */
    0 0 0 var(--ring) #e7dac0,             /* the moulding */
    0 0 0 calc(var(--ring) + 1px) rgba(46,28,14,.5),
    0 3px 7px rgba(46,28,14,.45),
    0 0 46px rgba(0,0,0,.85) inset,
    0 5px 16px rgba(0,0,0,.4) inset}
.eng-screen video{display:block;width:100%;height:100%;object-fit:contain;
  background:#14120f center/contain no-repeat}
/* the curve of the glass: one broad sweep across the top, a small return glint
   low and right, and corners that fall away hard */
.eng-glass{position:absolute;inset:0;pointer-events:none;border-radius:9% / 12%;
  background:
    radial-gradient(85% 55% at 26% 4%,rgba(255,255,255,.24),transparent 58%),
    radial-gradient(38% 18% at 74% 92%,rgba(255,255,255,.08),transparent 62%),
    radial-gradient(128% 118% at 50% 50%,transparent 44%,rgba(0,0,0,.74) 100%)}
/* scanlines across, and the aperture grille down: one alone reads as a filter,
   the pair reads as a picture tube */
.eng-scan{position:absolute;inset:0;pointer-events:none;opacity:.34;
  background:
    repeating-linear-gradient(to bottom,rgba(0,0,0,.3) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(to right,rgba(0,0,0,.16) 0 1px, transparent 1px 3px)}

/* ---- the control panel -------------------------------------------------- */
.eng-panel{position:relative;display:flex;flex-direction:column;align-items:center;
  gap:9%;padding:8% 0 6%;border-radius:8px;
  background:linear-gradient(160deg,#e8dcc2,#cdbb9a 70%,#b9a684);
  box-shadow:0 1px 0 rgba(255,255,255,.7) inset, 0 -2px 4px rgba(0,0,0,.16) inset}
.eng-brand{font-family:var(--font-display);font-size:clamp(8px,1.4vw,12px);
  letter-spacing:.16em;color:rgba(46,28,14,.5)}
.eng-speaker{position:relative;width:66%;display:flex;justify-content:center}
.eng-grille{width:100%;height:clamp(16px,3.4vw,30px);border-radius:2px;
  background:repeating-linear-gradient(to bottom,
    rgba(46,28,14,.3) 0 1px, transparent 1px 4px)}
/* NOTES OUT OF THE SPEAKER, only while it is playing. They are hung off the
   grille rather than the cabinet, so they still come out of the right place
   after the panel moves under the tube on a phone. Nothing clips them: no
   ancestor here hides its overflow, which is what lets them drift over the
   woodwork. */
.eng-note{position:absolute;left:50%;top:0;width:var(--s);height:var(--s);
  margin-left:calc(var(--s) / -2);
  color:rgba(46,28,14,.5);opacity:0;pointer-events:none}
.eng-note :first-child{fill:currentColor}
@media (prefers-reduced-motion:no-preference){
  .eng-tv.is-playing .eng-note{animation:engNote var(--t) ease-out infinite var(--d)}
}
@keyframes engNote{
  0%  {opacity:0;   transform:translate(0,4px) rotate(0) scale(.7)}
  18% {opacity:.85; transform:translate(calc(var(--x) * .3),-16px) rotate(calc(var(--r) * .4)) scale(1)}
  70% {opacity:.5;  transform:translate(calc(var(--x) * .8),-52px) rotate(var(--r)) scale(1.05)}
  100%{opacity:0;   transform:translate(var(--x),-78px) rotate(calc(var(--r) * 1.3)) scale(1.1)}}
/* A KNOB, NOT A CIRCLE. The first pass was one flat disc and it read as a
   decal: no bezel, no ticks, nothing saying it turned. It has a dished cap
   catching light off-centre, a ring of ticks around it so the travel is
   legible, and an engraved label underneath, because a control nobody
   recognises as a control is decoration. */
/* the tick ring reaches past the cap, so the label needs clearance or the
   two collide */
.eng-ctrl{display:flex;flex-direction:column;align-items:center;gap:.85em;
  width:68%}
.eng-knob{appearance:none;padding:0;cursor:pointer;position:relative;
  width:100%;aspect-ratio:1;border-radius:50%;border:0;touch-action:none;
  background:
    radial-gradient(circle at 50% 50%, rgba(46,28,14,.10) 0 33%, transparent 34%),
    radial-gradient(circle at 36% 26%, #fffaf0 0 8%, #e4d6b8 44%, #a8926f 100%),
    linear-gradient(#d9c9a6,#a89272);
  box-shadow:0 2px 4px rgba(36,20,8,.5),
             0 0 0 1px rgba(46,28,14,.3),
             0 2px 0 rgba(255,255,255,.75) inset,
             0 -3px 5px rgba(46,28,14,.28) inset}
.eng-knob-vol{width:86%;align-self:center}
/* the ring of ticks it turns against */
.eng-knob::after{content:"";position:absolute;inset:-21%;border-radius:50%;
  pointer-events:none;
  background:repeating-conic-gradient(from 218deg,
    rgba(46,28,14,.5) 0 1.4deg, transparent 1.4deg 27deg);
  -webkit-mask:radial-gradient(circle, transparent 0 60%, #000 62% 72%, transparent 74%);
          mask:radial-gradient(circle, transparent 0 60%, #000 62% 72%, transparent 74%)}
/* the pointer, cut into the cap rather than sat on it */
.eng-knob i{position:absolute;left:50%;top:9%;width:3px;height:30%;
  border-radius:2px;background:rgba(40,24,10,.85);
  box-shadow:0 1px 0 rgba(255,255,255,.55);
  transform-origin:50% 100%;
  transform:translateX(-50%) rotate(calc(-135deg + var(--v,0) * 270deg))}
.eng-knob-play i{transform:translateX(-50%) rotate(-42deg);transition:transform .3s ease}
.eng-tv.is-playing .eng-knob-play i{transform:translateX(-50%) rotate(42deg)}
.eng-knob:focus-visible{outline:2px solid var(--olive);outline-offset:4px}
/* stamped into the panel: dark ink with a light edge below it */
.eng-cap{font-family:var(--font-ui);font-size:clamp(6.5px,1vw,8.5px);
  font-weight:600;letter-spacing:.2em;text-transform:uppercase;
  color:rgba(46,28,14,.62);text-shadow:0 1px 0 rgba(255,255,255,.6)}
.eng-led{width:20%;aspect-ratio:1;border-radius:50%;background:#6d2b28;
  box-shadow:0 0 0 1px rgba(46,28,14,.3) inset}
.eng-tv.is-playing .eng-led{background:#e05a4a;
  box-shadow:0 0 7px rgba(224,90,74,.9), 0 0 0 1px rgba(46,28,14,.3) inset}
/* the feet it stands on */
.eng-foot{position:absolute;bottom:0;width:9%;height:clamp(9px,1.5vw,16px);
  background:linear-gradient(180deg,#4a2c17,#33200f);border-radius:0 0 4px 4px}
.eng-foot-l{left:14%} .eng-foot-r{right:14%}

.eng-tape .pb-shot{aspect-ratio:var(--ar,.5625);width:100%}

.eng-play{margin-top:1.15rem;display:inline-flex;align-items:center;gap:.65rem;
  appearance:none;cursor:pointer;background:transparent;min-height:44px;
  border:1px solid var(--rule-ink);border-radius:999px;padding:.66rem 1.25rem;
  color:var(--olive);font-family:var(--font-ui);font-size:10.5px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;
  transition:border-color .25s ease,background-color .25s ease,color .25s ease}
.eng-play:hover{border-color:var(--olive);background:rgba(85,103,47,.07)}
.eng-play:focus-visible{outline:2px solid var(--olive);outline-offset:3px}
.eng-tri{width:0;height:0;border-left:8px solid currentColor;
  border-top:5.5px solid transparent;border-bottom:5.5px solid transparent}
.eng-piece.is-playing .eng-tri{border:0;width:8px;height:10px;
  border-left:3px solid currentColor;border-right:3px solid currentColor}
.eng-piece.is-missing .eng-play,.eng-piece.is-missing .eng-knob{
  pointer-events:none;opacity:.5}
.eng-piece.is-missing figcaption::after{content:" \00b7 awaiting the re-encode";
  color:var(--gold-deep)}
.eng-piece figcaption{margin-top:.55rem;font-family:var(--font-ui);font-size:9px;
  font-weight:600;letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-faint)}

@media (max-width:640px){
  .eng-tapes{gap:1.6rem}
  .eng-tape{flex:1 1 100%;max-width:17rem}
  /* THE PANEL GOES UNDER THE TUBE. Down the side it costs a seventh of a
     phone's width, and this set is already the widest thing on the page; below
     it, the picture gets the whole screen and the controls are under the thumb
     rather than off at the edge. */
  .eng-case{grid-template-columns:1fr;gap:clamp(6px,1.8vw,12px);
    padding:clamp(6px,2vw,12px)}
  /* THE PANEL IS A STRIP, NOT A SHELF. Laid out across the bottom it is only as
     tall as the tallest control, so the dials and the clearance under them are
     the whole budget: it was taking a fifth of the set's height. */
  .eng-panel{flex-direction:row;align-items:center;justify-content:center;
    gap:clamp(13px,4.6vw,26px);padding:clamp(4px,1.4vw,9px) clamp(8px,3vw,16px)}
  /* the knobs are sized off the panel's column width, which no longer exists */
  .eng-ctrl{width:auto;gap:.45em}
  .eng-knob{width:clamp(25px,7.6vw,36px)}
  .eng-knob-vol{width:clamp(22px,6.6vw,31px)}
  .eng-knob::after{inset:-17%}
  .eng-cap{font-size:clamp(5.5px,1.55vw,7.5px);letter-spacing:.16em}
  .eng-brand{order:-1}
  .eng-speaker{width:clamp(34px,11vw,52px)}
  .eng-grille{height:clamp(14px,4vw,20px)}
  .eng-led{width:clamp(9px,2.6vw,13px)}
  .eng-foot{height:clamp(7px,2vw,11px)}
}
@media (prefers-reduced-motion:reduce){
  .eng-knob i,.eng-play{transition:none}
}

/* ============================================================================
   THE LIGHTBOX. A photograph in a book is thumbnail-sized; this is the way to
   actually look at one.

   Cream, like every other full-screen thing here. The file it shows is the same
   980px asset the page already has, so opening one costs nothing that was not
   already fetched.
   ============================================================================ */
.lb{position:fixed;inset:0;z-index:calc(var(--z-gate) + 10);display:none;
  align-items:center;justify-content:center;padding:clamp(2.6rem,7vh,5rem) clamp(1rem,5vw,4rem)}
.lb[hidden]{display:none}
.lb.is-open{display:flex}
.lb-veil{position:absolute;inset:0;background:var(--cream);opacity:0;
  transition:opacity .35s ease}
.lb.is-lit .lb-veil{opacity:.97}
.lb-fig{position:relative;margin:0;display:flex;flex-direction:column;
  align-items:center;gap:.9rem;max-width:100%;max-height:100%;
  opacity:0;transform:scale(.985);
  transition:opacity .35s ease,transform .35s cubic-bezier(.2,.7,.2,1)}
.lb.is-lit .lb-fig{opacity:1;transform:none}
.lb-img{display:block;max-width:100%;max-height:calc(100vh - 12rem);
  width:auto;height:auto;border-radius:2px;
  box-shadow:0 0 0 1px rgba(36,28,23,.08), 0 26px 50px -30px rgba(36,28,23,.6)}
.lb-cap{font-family:var(--font-ui);font-size:9.5px;font-weight:600;
  letter-spacing:.2em;text-transform:uppercase;color:var(--ink-faint)}

.lb-btn{position:absolute;appearance:none;border:0;cursor:pointer;
  background:transparent;width:56px;height:56px;color:var(--ink-mute);
  transition:color .25s ease}
.lb-btn:hover{color:var(--ink)}
.lb-btn:focus-visible{outline:2px solid var(--olive);outline-offset:2px}
.lb-btn::before,.lb-btn::after{content:"";position:absolute;left:50%;top:50%;
  width:13px;height:1.5px;background:currentColor;border-radius:1px}
.lb-close{top:clamp(10px,2.5vh,26px);right:clamp(10px,2.5vw,26px)}
.lb-close::before{transform:translate(-50%,-50%) rotate(45deg)}
.lb-close::after{transform:translate(-50%,-50%) rotate(-45deg)}
/* chevrons, drawn from the same two bars */
.lb-prev{left:clamp(2px,1.5vw,22px);top:50%;margin-top:-28px}
.lb-next{right:clamp(2px,1.5vw,22px);top:50%;margin-top:-28px}
.lb-prev::before{transform:translate(-50%,-50%) translateY(-4.5px) rotate(-45deg)}
.lb-prev::after {transform:translate(-50%,-50%) translateY(4.5px) rotate(45deg)}
.lb-next::before{transform:translate(-50%,-50%) translateY(-4.5px) rotate(45deg)}
.lb-next::after {transform:translate(-50%,-50%) translateY(4.5px) rotate(-45deg)}
.lb-btn:disabled{opacity:.25;cursor:default}

html.lb-open{overflow:hidden}
html.lb-open body{position:fixed;left:0;right:0;width:100%;overflow:hidden}

/* the photographs say they can be opened */
.pb-page .pb-shot img{cursor:zoom-in}

@media (prefers-reduced-motion:reduce){
  .lb-veil,.lb-fig{transition:none}
}

/* ---- a slot that cycles ---------------------------------------------------
   Frames of one moment are one memory, not four. They share a plate and switch
   between them. Only the open spread cycles; js/album.js adds the class, so
   with no script at all the first frame simply stays put.

   EVERY FRAME IS ABSOLUTE, INCLUDING THE FIRST. The first was in the flow and
   sized the box while the rest were absolutely positioned to fill it, so the
   box was the slot and the first frame was letterboxed inside it: each switch
   made the picture jump wider and off-centre. Giving the slot the aspect ratio
   and taking all the frames out of the flow means every one lands in exactly
   the same rectangle.

   THE SWITCH IS QUICK. A nine-tenths-of-a-second dissolve between two nearly
   identical photographs reads as a fault rather than a transition; at a fifth
   of that it reads as a change of frame.
   -------------------------------------------------------------------------- */
/* its size is set with the camcorder's, under "A FRAME FITS" near the top */
.pb-page .pb-shot.pb-multi{position:relative;margin-inline:auto}
.pb-page .pb-multi img{position:absolute;inset:0;
  width:100%;height:100%;max-width:none;max-height:none;object-fit:cover;
  border-radius:2px;opacity:0;transition:opacity .2s linear;
  box-shadow:0 0 0 1px rgba(36,28,23,.07), 0 7px 16px -11px rgba(36,28,23,.55)}
.pb-page .pb-multi img:first-child{opacity:1}
.pb-page .pb-multi.is-cycling img{opacity:0}
.pb-page .pb-multi.is-cycling img.is-on{opacity:1}

/* ---- a cropped plate ----
   A window onto the photograph, set in book.py by filename. The plate takes
   the window's shape, and the whole picture sits behind it, scaled up by the
   window's size and slid so the window's corner lands on the plate's. --cx
   and --cy are its top-left, --cw and --ch its size, all fractions of the
   photograph, so the same numbers hold at any resolution. The shadow moves
   onto the plate, since the picture itself is mostly out of sight. */
.pb-page .pb-shot.pb-crop{position:relative;overflow:hidden;border-radius:2px;
  margin-inline:auto;
  box-shadow:0 0 0 1px rgba(36,28,23,.07), 0 7px 16px -11px rgba(36,28,23,.55)}
.pb-page .pb-crop img{position:absolute;inset:auto;max-width:none;max-height:none;
  width:calc(100% / var(--cw));height:calc(100% / var(--ch));
  left:calc(-100% * var(--cx) / var(--cw));top:calc(-100% * var(--cy) / var(--ch));
  object-fit:fill;border-radius:0;box-shadow:none}

/* how many frames are in there */
/* the dots have to be readable as "there are more in here", not mistaken for
   dirt on the paper, so they are a little larger and the active one is clear */
.pb-dots{display:flex;justify-content:center;gap:5px;margin-top:.5rem;flex:none}
.pb-dots i{width:5px;height:5px;border-radius:50%;
  background:rgba(36,28,23,.22);transition:background-color .4s ease}
.pb-dots i.is-on{background:var(--olive)}

@media (prefers-reduced-motion:reduce){
  .pb-multi > img{transition:none}
}
