/* GENERATED FILE -- DO NOT EDIT.
 *
 * Lifted verbatim from the style block of the five gallery pages, which are
 * themselves generated by gal.py. Byte-identical on all five, which is what
 * makes it safe to extract: it is shared by construction, not by coincidence.
 *
 * To change anything in here, edit STYLE/SCRIPT in gal.py, run gal.py to
 * rebuild the pages, then run build-journal.py. Editing this file directly
 * changes the blog and not the galleries, which is the one outcome the whole
 * arrangement exists to prevent.
 *
 * source:  gal.py -> pages.json
 * sha1:    2a5ee757de659a605b4e2b5fbc0ce68977c669ae
 * bytes:   47515
 */

/* --- shapes the photographs actually are ---------------------------------
   The stylesheet knew only 4:5, 3:2 and 2:1, which was fine for placeholders
   and wrong for the work. Eleven of the twelve portraits uploaded on 6 Sep are
   landscape and three are 2.7:1 panoramas; forcing those into a 4:5 slot would
   have discarded 44% of the width, which in these frames is the composition.
   Native ratios instead, so the crop is nearly always nothing at all. */
.dv2-root .dv2-43{aspect-ratio:4/3}
.dv2-root .dv2-34{aspect-ratio:3/4}
/* 16:9 arrived with iLight, and it earns its line rather than borrowing 2:1.
   Forced into the 2:1 band the frame loses 11% of its HEIGHT, and because this
   picture is a reflection the crop is not merely a crop: it lifts the lit water
   up into the caption band and takes the caption from 8.44:1 to 2.29:1. A
   ratio that is nearly right is a contrast bug wearing a composition costume. */
.dv2-root .dv2-169{aspect-ratio:16/9}
.dv2-root .dv2-27{aspect-ratio:27/10}
.dv2-root .dv2-23{aspect-ratio:2/3}
.dv2-root .dv2-58{aspect-ratio:5/8}
/* One frame held off-centre to the right. It needs the same collapse to full
   width below 900px that the stylesheet's own column classes get. */
.dv2-root .dv2-c6-13{grid-column:6/13}
@media (max-width:899px){
  .dv2-root .dv2-c6-13{grid-column:1/-1}
}

/* --- the caption is allowed to take a second line -----------------------
   It is a flex row -- title left, credit right -- and it could not wrap, so
   whenever the two would not fit side by side the credit was pushed past the
   frame and cut off by the plate's overflow:hidden. Not a narrow-screen edge
   case either: on the HOME page it clipped at every desktop width, because
   "(c) Alvin Chan" is 153px and a work tile is 220px with 40px of padding.
   Measured before the fix -- home tiles 51px outside the frame at 1440 and
   30px at 1920; Travel losing three credits at 344px and six at 320px.

   Wrapping is the whole repair and it needs no breakpoint, which is why it is
   here rather than in a media query: the credit drops to its own line exactly
   when there is no room for it beside the title, at any width, and the caption
   band grows by two pixels when it does. */
.dv2-root .dv2-cap{flex-wrap:wrap;row-gap:2px}
/* The Instagram mark in the footer. Layout only -- the anchor also carries
   .dv2-link, which supplies the type, the tracking and the brass underline. */
.dv2-root .dv2-ig{display:inline-flex;align-items:center;gap:10px;min-height:44px}
.dv2-root .dv2-ig svg{display:block;flex:none}
/* --- the footer's three different centres --------------------------------
   Below 768 the footer centres its text, but the blocks inside it do not all
   span the column: the prose max-width holds .dv2-cold to 409px and the meta
   lines to 398px inside a 675px column. Measured at 767 the logo centred on
   376, the tagline on 240-odd and the Instagram mark on 307 -- three centres,
   none agreeing, which reads as everything being slightly out. It predates the
   icon; a short inline element just made it visible.

   Releasing the max-width inside the footer only -- where every block is one
   line rather than a paragraph anyone reads across -- puts all four on 376.
   Nothing moves on a phone: at 390 the column is 335px and they already fill
   it, so the centres were 187-188 before and after. */
@media (max-width:767px){
  .dv2-root .dv2-foot p{max-width:none}
}
/* --- the unfolded foldable, 768 to 899 -----------------------------------
   900px is the site's only breakpoint, so everything below it gets the phone
   layout: every frame spans the full measure. That is right at 390 and wrong
   at 884, which is what a Galaxy Fold or a Pixel Fold actually reports when it
   is opened. Measured on Events at 884x1104: the portrait frames rendered
   781x1041 and the tallest was 1171px -- a single photograph taller than the
   whole screen, so you scroll a frame at a time and never see a composition.

   Capping the plate rather than the grid item, because the caption, the marks
   and the play ring are all positioned inside the plate and follow it. 62% for
   the uprights and 84% for the landscapes brings the tallest frame from 1171px
   to 726px, which fits an unfolded screen with room to spare. The 16:9 bleed
   is left alone: it is 439px tall at that width and bleeding is the point.

   Stops at 899 because 900 and up already has real column spans -- measured at
   1000px the tallest frame is 590px with no help needed. */
@media (min-width:768px) and (max-width:899px){
  .dv2-root .dv2-frames .dv2-plate.dv2-34,
  .dv2-root .dv2-frames .dv2-plate.dv2-23,
  .dv2-root .dv2-frames .dv2-plate.dv2-45,
  .dv2-root .dv2-frames .dv2-plate.dv2-58{max-width:62%}
  .dv2-root .dv2-frames .dv2-plate.dv2-32,
  .dv2-root .dv2-frames .dv2-plate.dv2-43{max-width:84%}
}

/* --- the masthead --------------------------------------------------------
   Measured 6 Sep: on a 1440x800 laptop the first photograph on this page began
   at 823px, twenty-three pixels BELOW the fold. A visitor clicked a photograph
   on the home page, landed on a gallery, and the entire first screen was type.

   The fix is a photograph behind the title block rather than a new band above
   it, because the title block already reserves the height -- so the masthead
   adds NOTHING to the page. Verified: section 805px, first frame still at 823px,
   before and after. The first screen becomes a picture; nothing moves.

   Both layers are decorative and aria-hidden. The heading is what introduces
   the page; an alt here would be announced ahead of "Portraits / Sit still a
   while" and talk over it. It is furniture, not a tenth frame: no caption, no
   Motion or Full frame mark, no lightbox, and it is not in the frame count.

   WHERE THE SITTER ACTUALLY IS, measured off the file rather than guessed from
   luminance: she spans image x 0.585 to 0.810. Her hair begins at 0.625, her
   near shoulder at about 0.585, her far arm ends at 0.810. The bright band from
   0.82 to 1.0 is NOT her -- it is a wedge of light falling across the red wall
   behind her, and it is the brightest thing in the frame. Mistaking that wedge
   for the subject is what produced two wrong crops in a row: first a framing
   that buried her under the scrim, then a 30% right crop that sliced her in
   half. Look at the picture before choosing a number.

   The framing rule now: put the visible RIGHT edge just past 0.81 -- around 0.83
   to 0.84 -- so she is whole, sits hard right, and the wall wedge stays out.

   THE LADDER IS GONE, and the file is why. On the 2.707:1 original the visible
   right edge was f + (1 - f) * P with f swinging from 0.501 at 920px to 0.954 at
   1920, so one position could not hold the edge past her arm and it took three
   media queries. dba-wide is already cropped to 2:1 with her at 0.63-0.94 of its
   width, so plain `100%` puts her hard right at every size: she lands at 45-91%
   across at 920, 58-93% at 1440, 63-94% at 1920, always whole, always with
   margin. One value, no breakpoints.

   NO DARKENING AT ALL. The photograph runs at full strength -- there is no
   brightness() here, only a saturate() that tones the red wall to the site's
   palette and takes nothing off the luminance. The sitter is untouched: her
   brightest pixel measures 0.642 with the scrim applied, the same value it has
   with no scrim whatsoever.

   That is possible because the ink was moved instead of increased. The scrim
   now covers ONLY the type column and the header strip and is fully clear by
   76% across, which is where she begins. Compare, at 1440x800:

     brightness(.80) + old scrim   sitter mean 0.0285, peak 0.303, worst 6.46:1
     full strength, no scrim       sitter mean 0.0891, peak 0.642, worst 1.02:1
     full strength, this scrim     sitter mean 0.0573, peak 0.642, worst 6.20:1

   The middle row is why "remove all shading" cannot be taken literally: with
   nothing behind it the Enquire link measures 1.02:1 against the wall, which is
   invisible, not merely hard to read. The third row keeps her at full strength
   anyway, by putting the ink only where the words are. */
.dv2-root .dv2-mast{position:relative}
.dv2-root .dv2-mast > .dv2-grid{position:relative;z-index:3}
.dv2-root .dv2-mast-img,.dv2-root .dv2-mast-ink{position:absolute;inset:0;display:block}
/* The framing is a per-page custom property, not a hard-coded value, because it
   is a property of the PHOTOGRAPH and not of the design. Sarah is a 2.707:1
   panorama that gets cropped horizontally and needs a right anchor; Babora is
   1.333:1 landscape and gets cropped VERTICALLY -- for her the x value does
   nothing at all on a desktop (cover fits her by width, so the whole width is
   always on screen) and the y value is everything, while on a PHONE the band
   turns taller than wide and the two swap over. Defaults are Sarah's. */
/* --- and how much of it is in frame -------------------------------------
   background-size was `cover` and nothing else for three mastheads, which was
   fine while every one of them was a panorama or a landscape wider than the
   band: cover fits by width, the whole width is on screen, and the only
   decision left is the vertical one.

   Gubbio broke that. He fills the LEFT two thirds of a 3:2 frame and the empty
   dark is on the right, which is backwards for a masthead -- the type column
   is on the left, so cover puts his face under the words. And cover offers no
   way to ask for anything else: the full width is on screen by definition, so
   background-position's x value does nothing at all.

   --mast-size lets a page say how wide to draw the picture as a share of the
   band, with the height following its own ratio; anchored left at 0%, the
   surplus falls off the right. At 125% the empty right goes, his face lands at
   about 72% across, and the dark ruff takes the type. The value is a per-page
   property of the PHOTOGRAPH, like the position beside it, and it defaults to
   cover so no other masthead moves.

   ONLY ON THE DESKTOP. Below 900px the band turns taller than wide, and a
   width-driven size stops covering it -- at 125% the picture would be 469 wide
   and 312 tall inside a 375x643 box, leaving half of it empty. The phone rule
   below resets to cover for that reason, and keeps its own position value. */
.dv2-root .dv2-mast-img{z-index:1;background-image:var(--mast);
  background-size:var(--mast-size,cover);
  background-position:var(--mast-pos,100% 50%);background-repeat:no-repeat;
  filter:saturate(.74)}
/* Three scrims, and the whole game is keeping them OFF the sitter.

   The horizontal one holds the type column and is fully clear by 76% -- she
   begins at about 60% across, so her lit side, from roughly 76% rightward, has
   no ink on it at all. Its earlier shape carried .38 all the way out to 74% and
   .05 to 86%, which was dimming her face for no benefit: the type ends at 64%.

   The top one is in PIXELS, not percent, because it protects a fixed 92px header
   whose height does not scale with the section. Without it the nav links over
   the bright side measured 2.37:1 (Services), 2.67:1 (About), 2.87:1 (Enquire).
   It has been raised twice, both times for the same reason -- at 1920px wide
   almost the whole panorama is in frame, so the wall wedge sits directly under
   the nav. At .90/.86 that was 4.03:1, at .96/.94 it was 4.65:1 once the
   brightness filter came off, and at .97/.95 it was 5.09:1. Moving to the 2:1
   crop brought the wedge back into frame at every width -- it sits in the same
   columns as her arm, so it cannot be cropped out without cutting her -- and
   Enquire fell to 5.00:1, hence .985/.975. This is the one place the masthead is
   allowed to be heavy and it costs almost nothing: measured over her face, mean
   luminance goes 0.0577 -> 0.0548 and the brightest pixel does not move at all.

   The last gradient fades the foot to page black. */
.dv2-root .dv2-mast-ink{z-index:2;background:
  linear-gradient(180deg,rgba(10,10,14,.985) 0,rgba(10,10,14,.975) 130px,rgba(10,10,14,0) 260px),
  linear-gradient(0deg,rgba(10,10,14,1) 0,rgba(10,10,14,.85) 60px,rgba(10,10,14,0) 190px),
  linear-gradient(90deg,rgba(10,10,14,.96) 0%,rgba(10,10,14,.94) 40%,
    rgba(10,10,14,.88) 60%,rgba(10,10,14,.30) 68%,rgba(10,10,14,0) 76%,
    rgba(10,10,14,0) 100%)}
/* Below 900px the type block goes full width, so it can no longer hide in the
   dark left third, and the scrim becomes vertical instead.

   THE SITTER CANNOT COME TO THE PHONE, and this is a measured limit rather than
   a preference. At 375px cover shows only 21.6% of the panorama's width; she
   occupies 22.5% of it, so she does not fit whole at any position. Worse, the
   type here runs the full width and would land on her face, which is the
   brightest thing in the frame: framed on her, the "All work" link measures
   2.44:1, and 3.41:1 even with the scrim pushed as far as it will go. Both fail
   WCAG AA badly. So the phone keeps the dark wall at 40% -- window image x
   0.314-0.530 -- and the brightness filter is gone here too: mean luminance
   0.0087 -> 0.0125, worst element 5.31:1. It still reads as a near-black band,
   because that is what a fifth of this panorama's dark half contains.

   The real fix is a second file cropped to something nearer 4:3 around her, not
   a different crop of this panorama. */
@media (max-width:899px){
  .dv2-root .dv2-mast-img{background-position:var(--mast-pos-m,40% 50%);
    background-size:cover;filter:saturate(.74)}
  .dv2-root .dv2-mast-ink{background:
    linear-gradient(180deg,rgba(10,10,14,.88) 0%,rgba(10,10,14,.66) 26%,
      rgba(10,10,14,.70) 72%,rgba(10,10,14,.95) 100%)}
  /* A denser phone scrim, for a masthead whose photograph has no true black in
     it. The default above was measured against Sarah, whose wall reads 0.0003 --
     effectively zero -- so a light scrim was enough. Tangmo's room was mid-grey
     everywhere, roughly 0.05 to 0.10, with nowhere for type to hide: at 375px
     her page failed WCAG AA at EVERY horizontal position, the lead line landing
     between 2.75:1 and 3.96:1. Opt in per page, because applying it to Sarah
     would only crush a band that is already near-black.

     TWO THINGS WERE TRIED ON THIS RAMP AND BOTH WERE PUT BACK. Recorded so
     nobody spends the afternoon again.

     Babora's face sits at image y 0.09, and on a phone the band shows the
     picture's full height, so her face is always in the top fifth and reads as
     a shape rather than a portrait. The first read was that the CROP was wrong
     and the fix was a phone-specific export. It is not. A 0.68-ratio crop taken
     tight around her measured IDENTICAL to the full frame against this scrim --
     6.34, 6.35, 14.93, every element to two decimals. Of course it did: this
     ramp never goes lighter than .88 anywhere, and at 88% ink it makes no
     difference at all what photograph is underneath. Do not re-export for this.

     The second was to open the top, where there is no type: 0 to 10% at .45,
     closing to .90 by 20%. It measures well -- her face more than doubles in
     luminance, 0.0047 to 0.0110, and everything still clears AA at 390, 344 and
     320, the wordmark binding at 5.34:1. Alvin looked at it live and preferred
     the closed band, so it came straight back out. It is a taste call, not a
     contrast one, and the numbers above are here if it is ever revisited. */
  .dv2-root .dv2-mast-deep .dv2-mast-ink{background:
    linear-gradient(180deg,rgba(10,10,14,.95) 0%,rgba(10,10,14,.88) 22%,
      rgba(10,10,14,.90) 72%,rgba(10,10,14,.98) 100%)}
}

/* --- the row of three ----------------------------------------------------
   Eleven usable tracks will not divide into three equal parts. Spanning
   4 + 4 + 3 rendered the last frame 199px against its neighbours' 270 -- a 26%
   difference at the same aspect ratio, which reads as a mistake rather than a
   rhythm. The three frames get their own grid across the full measure. */
.dv2-root .dv2-trio{grid-column:2/13;display:grid;align-items:start;
  grid-template-columns:repeat(3,minmax(0,1fr));gap:0 var(--col-gap)}
@media (max-width:899px){
  .dv2-root .dv2-trio{grid-column:1/-1;grid-template-columns:minmax(0,1fr);
    gap:var(--sp-l) 0}
}

/* --- a frame that opens --------------------------------------------------
   Only a frame that actually has a clip becomes a button, because an
   affordance that does nothing is worse than no affordance at all. */
.dv2-root .dv2-lbo{appearance:none;-webkit-appearance:none;background:none;
  border:0;margin:0;padding:0;width:100%;display:block;font:inherit;
  color:inherit;text-align:inherit;cursor:pointer}
.dv2-root .dv2-lbo::before,.dv2-root .dv2-lbo::after{content:none;display:none}
.dv2-root .dv2-lbo .dv2-tag{position:absolute;right:var(--sp-s);top:var(--sp-s);
  z-index:5;display:flex;align-items:center;gap:8px;font-size:var(--s-micro);
  font-weight:200;letter-spacing:var(--t-eyebrow);text-transform:uppercase;
  color:var(--bone);text-shadow:0 1px 14px rgba(6,6,10,.95);opacity:.55;
  transition:opacity var(--d-tone) var(--e-tone)}
.dv2-root .dv2-lbo .dv2-tag::before{content:"";width:16px;height:1px;
  background:var(--brass)}
.dv2-root .dv2-lbo:hover .dv2-tag,
.dv2-root .dv2-lbo:focus-visible .dv2-tag{opacity:1}
.dv2-root .dv2-lbo .dv2-play{position:absolute;left:50%;top:50%;z-index:5;
  width:72px;height:72px;margin:-36px 0 0 -36px;border:1px solid var(--brass-soft);
  border-radius:50%;display:grid;place-items:center;pointer-events:none;
  opacity:0;transform:scale(.94);
  transition:opacity var(--d-tone) var(--e-tone),
             transform var(--d-glass) var(--e-settle),
             border-color var(--d-tone) var(--e-tone)}
.dv2-root .dv2-lbo .dv2-play::before{content:"";width:0;height:0;margin-left:4px;
  border-left:12px solid var(--brass-lit);
  border-top:8px solid transparent;border-bottom:8px solid transparent}
.dv2-root .dv2-lbo:hover .dv2-play,
.dv2-root .dv2-lbo:focus-visible .dv2-play{opacity:1;transform:none;
  border-color:var(--brass-lit)}
/* A frame that opens a still rather than a clip. Same button, same tag slot,
   but it must not claim to move — so the play ring is replaced by four brass
   corner marks, which is the design's own .dv2-marks motif at a smaller size.
   It says "the whole frame", which is exactly what the lightbox gives.

   Worth knowing what "full frame" currently means: Hostinger's CDN caps
   DELIVERED width at 1600px, so the -scaled and un-scaled copies of a
   photograph arrive identical to the pixel and there is no sharper file to
   open. It does NOT overwrite the originals -- an earlier note here said it
   did, and that was wrong. WordPress still has 2560px and 2048px versions on
   disk; the shrinking happens on the way out. The gain here is that the plate
   CROPS to its slot and shows the picture at 349-845px, while this shows the
   whole uncropped frame at up to 1180px. Turn the cap off in hPanel and every
   lightbox sharpens at once, with no code change and nothing to re-upload. */
.dv2-root .dv2-lbo .dv2-view{position:absolute;left:50%;top:50%;z-index:5;
  width:72px;height:54px;margin:-27px 0 0 -36px;pointer-events:none;
  opacity:0;transform:scale(.94);
  transition:opacity var(--d-tone) var(--e-tone),
             transform var(--d-glass) var(--e-settle)}
.dv2-root .dv2-lbo .dv2-view > span{position:absolute;inset:0}
.dv2-root .dv2-lbo .dv2-view::before,
.dv2-root .dv2-lbo .dv2-view::after,
.dv2-root .dv2-lbo .dv2-view > span::before,
.dv2-root .dv2-lbo .dv2-view > span::after{content:"";position:absolute;
  width:16px;height:16px;border:0 solid var(--brass-soft);
  transition:border-color var(--d-tone) var(--e-tone)}
.dv2-root .dv2-lbo .dv2-view::before{top:0;left:0;
  border-top-width:1px;border-left-width:1px}
.dv2-root .dv2-lbo .dv2-view::after{top:0;right:0;
  border-top-width:1px;border-right-width:1px}
.dv2-root .dv2-lbo .dv2-view > span::before{bottom:0;left:0;
  border-bottom-width:1px;border-left-width:1px}
.dv2-root .dv2-lbo .dv2-view > span::after{bottom:0;right:0;
  border-bottom-width:1px;border-right-width:1px}
.dv2-root .dv2-lbo:hover .dv2-view,
.dv2-root .dv2-lbo:focus-visible .dv2-view{opacity:1;transform:none}
.dv2-root .dv2-lbo:hover .dv2-view::before,
.dv2-root .dv2-lbo:hover .dv2-view::after,
.dv2-root .dv2-lbo:hover .dv2-view > span::before,
.dv2-root .dv2-lbo:hover .dv2-view > span::after,
.dv2-root .dv2-lbo:focus-visible .dv2-view::before,
.dv2-root .dv2-lbo:focus-visible .dv2-view::after,
.dv2-root .dv2-lbo:focus-visible .dv2-view > span::before,
.dv2-root .dv2-lbo:focus-visible .dv2-view > span::after{
  border-color:var(--brass-lit)}

/* No hover to reveal them on a touch screen, so they are simply always there. */
@media (hover:none){
  .dv2-root .dv2-lbo .dv2-play,
  .dv2-root .dv2-lbo .dv2-view{opacity:1;transform:none}
}

/* --- a frame too pale to carry light type --------------------------------
   Every gallery until Travel was low-key, so the furniture could be bone and
   the caption could simply trust the photograph to be dark behind it. The
   Namibia frames are the opposite: bleached sky, white grass, animals as dark
   marks on nothing. Measured on the home page's giraffe tile, bone caption on
   that white read 1.02:1 -- not faint, INVISIBLE -- and the credit 2.40:1.

   A scrim cannot fix it. Forced permanently on it still left the caption at
   2.14:1, and dragging the dim credit line to AA would have needed about 96%
   ink across the whole band: a black bar across the bottom of the picture.

   So the furniture inverts. Dark ink on a pale photograph needs no scrim at all.

   THE WORST CASE INVERTS WITH IT, which is the part that bit. For bone type the
   danger is the brightest pixel behind it; for dark type it is the DARKEST --
   and these frames are not uniformly white. A giraffe's neck, a zebra's stripes
   and an oryx's shadow all run through the caption band. Values here are
   measured against the DARK end of each band, not the light one. */
.dv2-root .dv2-plate.dv2-light .dv2-cap{color:#0A0A0E}
.dv2-root .dv2-plate.dv2-light .dv2-cap em{color:#33333A}
.dv2-root .dv2-plate.dv2-light .dv2-cap::before{background:#463719}
.dv2-root .dv2-plate.dv2-light .dv2-tag{color:#3A3A42;
  text-shadow:0 1px 14px rgba(250,250,252,.92)}
.dv2-root .dv2-plate.dv2-light .dv2-tag::before{background:#463719}
.dv2-root .dv2-plate.dv2-light .dv2-play{border-color:#5A4826}
.dv2-root .dv2-plate.dv2-light .dv2-play::before{border-left-color:#2E2416}
.dv2-root .dv2-lbo:hover .dv2-plate.dv2-light .dv2-play,
.dv2-root .dv2-lbo:focus-visible .dv2-plate.dv2-light .dv2-play{border-color:#2E2416}

/* --- and a frame where NEITHER works -------------------------------------
   Inverting assumes the caption band is uniformly pale. Two of the Namibia
   frames are not: the oryx's band runs 0.091 to 0.947 because his dark legs and
   the scrub cross it, and the zebra group's runs 0.000 to 0.823 -- true black to
   near-white inside one strip of caption. Bone fails at the light end and ink
   fails at the dark end, so there is no colour that survives both.

   Measured, before this was added: oryx caption 3.55:1 as ink, zebra group
   2.45:1 as ink and 1.09:1 as bone. Neither treatment was ever going to work.

   These frames get ink of their own instead. The design already has the right
   component -- the bottom scrim on .dv2-plate::after -- it is simply hover-only
   and too light, so this pins it on and strengthens it until the DIM half of the
   caption clears AA: the credit line is the binding constraint at 5.16:1, the
   caption itself sits at 12.17:1. Type stays bone, so a scrimmed frame is
   indistinguishable from every other frame on the site.

   THIS IS NOW THE MAJORITY TREATMENT ON TRAVEL, eight frames of eleven, and
   the reason is the shape rather than the subject. A 27:10 caption band is a
   strip 1281px wide and 60 deep: across that much of a landscape it will find
   both a lit sky and something in shadow, whatever the picture is. Measured on
   the six added on 6 Sep, every single band ran from about 0.00 to somewhere
   between 0.09 and 0.61. There is no type colour that survives a range like
   that, so there is nothing to choose between -- they all take the scrim.

   PETS TAKES IT ON ALL FOUR, which is the same lesson from the other end. Two
   of those frames are near-black studio portraits of a husky, and bone still
   measured 1.10:1 and 1.03:1 on them, because the dog's rim-lit white chest
   sits exactly in the caption band. A dark background is not a dark caption
   band, and only one of the two is being measured.

   IT IS CALLED veil BECAUSE band WAS ALREADY TAKEN, and taking it cost a day.
   .dv2-band exists in the Customizer stylesheet as a full-height section --
   display:flex, min-height:78vh, align-items:center. Adding that class to a
   plate turned it into a 702px-tall flex box, and since the plate sizes itself
   with aspect-ratio, the ratio then resolved from the HEIGHT: the oryx rendered
   1895px wide inside a 1281px slot and the zebra group 1053px inside 530px,
   both hanging half a screen off the right edge of the page.

   The dv2- prefix is not protection when the collision is with our own older
   stylesheet. Before using a new class name, put a bare div in the live DOM
   with that class on it and read its computed style back: if anything differs
   from a naked div, the name is taken. dv2-scrim is taken too. */
/* AND IT IS HOVER-ONLY AGAIN, 7 Sep, which reverses the sentence above and is
   Alvin's call rather than a measurement.

   Pinned on, this scrim is a dark band across the foot of every frame that
   carries it -- eight on Travel, five on Events, four on Pets -- while
   Portraits and Boudoir, which needed none, showed nothing at rest. Two pages
   clean, three banded. He saw the inconsistency, preferred the clean ones, and
   chose consistency over the measurement with the trade spelled out.

   THE TRADE, WRITTEN DOWN SO IT IS NOT REDISCOVERED AS A BUG: at rest those
   seventeen captions now sit on the bare photograph, where they measure 1.02:1
   to 1.58:1. That is not faint, it is invisible, and it fails WCAG AA badly.
   The text is legible on hover and on keyboard focus, when the scrim comes up
   exactly as it does on Portraits, and it is legible nowhere else.

   The alternative offered was to fade the CAPTION in on hover with the scrim,
   which gives the same clean frame with nothing illegible because nothing is
   shown. It was not taken. If this ever comes back as "the captions cannot be
   read", that is the fix, not more ink.

   What stays pinned is the 96px band at the TOP -- see the note below. It
   protects the Motion mark, which IS visible at rest, and it is not what
   anybody means by a dark tinge at the bottom of the picture. */
.dv2-root .dv2-plate.dv2-veil::after{height:58%;
  background:linear-gradient(to top,rgba(6,6,10,.98) 0%,rgba(6,6,10,.96) 50%,
    rgba(6,6,10,.45) 80%,rgba(6,6,10,0) 100%)}
/* The Motion mark sits top-right, OUTSIDE that scrim, and on the zebra group it
   landed on bare mid-grey measuring 0.168 to 0.212. That is the worst background
   there is: at 0.19 the best contrast ANY colour can reach is about 4:1, so bone
   gave 3.64:1, ink gave 3.13:1, and no third choice existed. It is not a colour
   problem, it is a background problem, so the fix is a little ink of its own --
   a light top gradient which takes bone to 5.48:1 there.

   IT IS IN PIXELS, NOT PERCENT, and that was the second attempt. As a
   percentage of the frame it protected the mark well on a tall plate and badly
   on a short one, because THE MARK DOES NOT MOVE: it sits at a fixed offset
   from the top edge, so on a 474px bleed it landed a third of the way into the
   gradient and on a 278px frame nearly half way, where there is much less ink.
   Deepening the colour did not fix it -- at .62 the Xinjiang lakeshore still
   measured 4.20:1 and Sayram Lake 4.33:1, both failing.

   A fixed 96px band puts the same ink behind the mark on every frame,
   whatever its height. Measured after: the lakeshore 7.12:1, Sayram 8.35:1,
   and nothing else on the page moved by more than a rounding error. It is the
   same lesson as the masthead's top scrim, which is in pixels for the same
   reason: it is protecting something whose position is in pixels. */
.dv2-root .dv2-plate.dv2-veil::before{content:"";position:absolute;
  left:0;right:0;top:0;height:96px;z-index:3;pointer-events:none;
  background:linear-gradient(to bottom,rgba(6,6,10,.70) 0,
    rgba(6,6,10,.58) 48px,rgba(6,6,10,0) 96px)}
/* And on the oryx the same corner is bleached sky instead, where the answer is
   the opposite one: leave the sky alone and flip the mark to ink. Measured live
   at 1440 with the top gradient applied, the oryx corner came down to 0.224-0.288
   -- mid-grey, the one place NOTHING works: bone 3.03:1, ink 3.92:1. The gradient
   was making the problem it was built to solve. Off, the corner is 0.823-0.939
   again and ink reads 12.50:1.

   Giacomo on Pets is the second frame to need it: backlit evening sun puts blown
   bokeh in exactly that corner, where bone reads 1.08:1 and even this 96px band
   only reaches 4.86:1 -- too thin to trust. Ink there measures 10.98:1.

   So the two rules are one rule with two halves: a frame whose mark sits on grey
   gets the gradient, and a frame whose mark sits on white gets ink and no
   gradient at all. tagdark therefore SUPPRESSES the top scrim rather than adding
   to it. One frame, two treatments, because the top and the bottom of it are
   different photographs as far as type is concerned. */
.dv2-root .dv2-plate.dv2-veil.dv2-tagdark::before{content:none}
.dv2-root .dv2-plate.dv2-tagdark .dv2-tag{color:#26262C;
  text-shadow:0 1px 14px rgba(250,250,252,.92)}
.dv2-root .dv2-plate.dv2-tagdark .dv2-tag::before{background:#463719}

/* --- the corner marks and the caption scrim were fighting over ::after ----
   Alvin: "the dark tinge on mouse over is not seen on hands in the air and
   hard light. There seems to be some markings on the left side of the image
   instead." Both halves of that sentence are one bug.

   .dv2-marks draws four brass corner brackets. Two of them -- the bottom
   pair -- already live on a child span, .dv2-b, because a plate has only two
   pseudo-elements and the caption scrim had claimed ::after. The TOP pair was
   left on the plate itself, on ::before and ::after, which is to say on top of
   the scrim and the veil's header band.

   One element, one ::after. The browser does not pick a winner, it merges the
   two rules property by property, and the result is neither. Measured live on
   Hands in her hair: the scrim came out 14px wide (the corner mark's width,
   which the scrim rule never sets), 12px from the top (the corner mark's
   offset, which the scrim rule never sets), 180px tall (the scrim's 38%), with
   a 1px top and right border. A full-width wash across the foot of the picture
   had become a bordered sliver hanging near the top left corner -- exactly
   what he saw, and the reason no tinge appeared where one should.

   Six frames carried it, on every page: Hands in her hair, Hard light, The
   dust gives him away, Everyone came out small, The made bed, The water had it
   too, He heard the gate. It has been there since the marks were introduced,
   and it hid because until today the veiled frames had their scrim pinned on,
   so the only place to notice was a page with no veil at all.

   The fix finishes the job .dv2-b started: a second span, so the top pair has
   a box of its own and the plate's own two pseudo-elements go back to the
   scrim and the veil band. The neutralising rule below is the other half --
   .dv2-marks still targets the plate from the Customizer stylesheet, which
   cannot be patched, so this overrides the three properties it imposes and
   leaves everything else alone.

   The lesson is the same one the veil taught under a different name: a class
   whose selector reaches a pseudo-element is a class that OWNS that
   pseudo-element, and two of them on one tag is a collision the cascade will
   not report. Before adding a pseudo-element rule, check what else can land on
   the same tag. */
.dv2-root .dv2-plate.dv2-marks .dv2-t::before,
.dv2-root .dv2-plate.dv2-marks .dv2-t::after{content:"";position:absolute;
  z-index:3;width:14px;height:14px;top:12px;
  border:0 solid var(--brass-soft);pointer-events:none}
.dv2-root .dv2-plate.dv2-marks .dv2-t::before{left:12px;
  border-top-width:1px;border-left-width:1px}
.dv2-root .dv2-plate.dv2-marks .dv2-t::after{right:12px;
  border-top-width:1px;border-right-width:1px}
.dv2-root .dv2-plate.dv2-marks::before,
.dv2-root .dv2-plate.dv2-marks::after{width:auto;border-width:0}
.dv2-root .dv2-plate.dv2-marks::after{top:auto}
.dv2-root .dv2-plate.dv2-marks:not(.dv2-veil)::before{content:none}

/* --- the lightbox --------------------------------------------------------
   Not an enlarger: the frames on a gallery page are already large. This is
   where the moving version of a photograph lives, at full resolution. The
   grain sits at z-index 40 and the header at 32, both inside .dv2-root's
   stacking context, so 60 clears them. */
body.dv2-locked{overflow:hidden}
/* THERE IS NO SCROLLBAR CORRECTION HERE, and one was tried. A fixed element's
   containing block already excludes the scrollbar, so the overlay was centred
   correctly all along; the 7px error was in the measurement, which compared
   against innerWidth (1440, scrollbar included) instead of clientWidth (1425).
   Pulling the right edge in by 15px to "fix" it created the very offset it was
   meant to remove. Measure against clientWidth. */
.dv2-lb{position:fixed;inset:0;z-index:60;padding:clamp(14px,1.9vw,30px)}
.dv2-lb[hidden]{display:none}
.dv2-lb-field{position:absolute;inset:0;background:rgba(6,6,10,.94);
  -webkit-backdrop-filter:blur(20px) saturate(.85);
  backdrop-filter:blur(20px) saturate(.85);
  opacity:0;transition:opacity var(--d-glass) var(--e-glass)}
.dv2-lb.open .dv2-lb-field{opacity:1}
/* --- centred by arithmetic, not by the grid ------------------------------
   This used to be a grid item under place-items:center, and it was 100px above
   the middle of the screen. The grid reported THREE rows -- 553px and two of
   115.9px -- for a box with one in-flow child, because the field and the close
   button are absolutely positioned children of the same grid and this browser
   sizes implicit tracks for them anyway. The stage was being centred inside a
   row that was itself only part of the box.

   Left at 50% of the padding box with a -50% translate, there is nothing to
   negotiate with: the centre of the photograph is the centre of the viewport,
   on any browser, at any size. The opening transition rides in the same
   transform, which is why both states name the translate in full.

   No gap either, because the caption is out of flow -- see .dv2-lb-cap. */
.dv2-lb-stage{position:absolute;left:0;right:0;top:50%;z-index:1;
  margin-inline:auto;width:fit-content;
  display:flex;flex-direction:column;align-items:stretch;
  max-width:100%;
  opacity:0;transform:translateY(calc(-50% + 12px)) scale(.986);
  transition:opacity var(--d-glass) var(--e-develop),
             transform var(--d-develop) var(--e-develop)}
.dv2-lb.open .dv2-lb-stage{opacity:1;transform:translateY(-50%)}

/* --- BOTH EDGES ANCHORED, and that is the whole point -------------------
   Centring this with left:50% alone looked right and was quietly wrong. An
   absolutely positioned box with only `left` set computes its shrink-to-fit
   width against the space from that point to the container's right edge --
   half the window -- so every photograph in the lightbox was capped at about
   713px instead of 1180. Six of Boudoir's eight stills opened SMALLER than
   they appear on the page; the made bed opened at 31% of its own area.

   left:0 right:0 with width:fit-content and auto margins measures against the
   whole box and still centres exactly. translateY(-50%) does the vertical
   half, which needs no width to work. */
.dv2-lb-media{position:relative;align-self:center;max-width:100%;line-height:0;
  background:var(--ink-void);border:1px solid var(--hairline-soft)}
.dv2-lb-media::before,.dv2-lb-media::after{content:"";position:absolute;
  width:16px;height:16px;border:0 solid var(--brass-soft);pointer-events:none;z-index:1}
.dv2-lb-media::before{top:-1px;left:-1px;border-top-width:1px;border-left-width:1px}
.dv2-lb-media::after{bottom:-1px;right:-1px;border-bottom-width:1px;border-right-width:1px}
.dv2-lb-vid,.dv2-lb-img{display:block;width:auto;height:auto;max-width:100%;
  max-height:min(76vh,76svh)}
/* The display:block above would otherwise beat the UA stylesheet's rule for
   the hidden attribute, so the two swap by class, not by luck. */
.dv2-lb-vid[hidden],.dv2-lb-img[hidden]{display:none}
/* --- cropping the letterbox out of a clip --------------------------------
   Eight of this site's fourteen clips are a 1920x1080 container with the
   photograph letterboxed inside it, because a 4:3 or 27:10 still animated into
   a 16:9 export leaves black. The oryx carries 184px top and 185px bottom --
   34% of that file is black pixels -- Kristyna brushing her hair 528 each side
   (54%), the giraffe and the zebra pair 240 each side.

   The script works the picture's box out per clip from the still beside it (see
   the note on measure) and sizes THIS box to the picture rather than to the
   file, then pushes the video up and left so the black falls outside. Every
   number comes from JS because the geometry is per-file and per-viewport; the
   class below only supplies the clipping. A clip that is already the right
   shape gets no class and keeps the plain max-width/max-height sizing above.

   This is a workaround, not the fix: the black is still downloaded. Re-exported
   at their real dimensions these files would be materially smaller AND sharper
   at the same display size, and the measurement would then find nothing to do,
   which is exactly how it should degrade. */
.dv2-lb-media.dv2-lb-fit{overflow:hidden}
.dv2-lb-media.dv2-lb-fit .dv2-lb-vid,
.dv2-lb-media.dv2-lb-fit .dv2-lb-img{position:absolute;left:0;top:0;
  max-width:none;max-height:none}
/* --- THE LIGHTBOX IS NEVER SMALLER THAN THE PAGE -------------------------
   That is the whole rule, and it took two goes to arrive at it. A measure of
   1180px sounds generous until you notice a full-bleed frame is 1281px wide on
   the page at 1440 and grows with the window: any constant is beaten by the
   page at some width, so there is no constant to pick.

   Measured at 1425x900 before this: NINE of the fifteen clips and SIX of the
   fifteen stills opened smaller than the frame the visitor had just clicked --
   Behind glass at 0.52 of its page area, Rain on the glass and Holding on at
   0.58. Clicking a photograph to see it better and getting a smaller one is
   the plainest possible bug.

   So there is one rule for both now: as large as the window allows, less the
   margin and the caption. No cap, no percentage, no separate case for stills
   and clips -- which is also why the class that used to distinguish them is
   gone. The margin is thin for the same reason.

   What neither can outgrow is the file. Hostinger's CDN delivers photographs
   at most 1600px wide, and a clip is 1920 with the picture inside it often
   narrower -- 1440 for the giraffe. Past roughly 800 CSS pixels on a 2x screen
   both are being enlarged. That was already true at 1180; this makes it a
   little more visible. Turning the CDN cap off in hPanel, and re-exporting the
   clips cropped, are the two things that actually fix it. */
/* Held blank until the measurement lands, so the box is never seen at the wrong
   size. It is hidden behind the lightbox's own 340ms opening transition, so on
   a first open there is normally nothing to see and on every later open the
   answer is already cached. */
.dv2-lb-media{transition:opacity var(--d-tone) var(--e-tone)}
.dv2-lb-media.dv2-hold{opacity:0}
/* --- the caption hangs, it does not stack -------------------------------
   It used to be the second item in a centred column, which meant the thing
   being centred was the photograph PLUS the caption -- so the photograph sat
   above the middle of the screen by half the caption's height. Measured on
   Boudoir at 908px tall: the picture's centre was 49 to 64 pixels high. On a
   black field with one lit object in it, that reads as a mistake.

   Out of flow, the stage is exactly the height of the photograph, so what the
   grid centres IS the photograph. The caption hangs underneath it, and the
   sizing in layout() reserves the same height again ABOVE the picture so the
   two stay balanced and the caption can never run off the bottom. */
.dv2-lb-cap{position:absolute;left:0;right:0;top:calc(100% + var(--sp-s));
  display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--sp-m);margin:0;max-width:none;
  border-top:1px solid var(--hairline-soft);padding-top:var(--sp-xs);
  font-size:var(--s-meta);font-weight:200;text-transform:uppercase;
  letter-spacing:.22em;color:var(--bone)}
.dv2-lb-cap em{flex:none;font-style:normal;color:var(--ash-dim);
  letter-spacing:var(--t-nav)}
/* Absolute inside .dv2-lb, which is itself fixed to the viewport -- NOT fixed
   in its own right. The stage carries a transform while it opens, and a
   transformed ancestor turns position:fixed into position:absolute, so a fixed
   close button would jump for the length of the transition. Living outside the
   stage, it also stays still while the stage settles. */
.dv2-lb-x{appearance:none;-webkit-appearance:none;background:none;border:0;
  position:absolute;top:clamp(14px,2.6vw,34px);right:clamp(14px,2.6vw,34px);
  z-index:2;width:48px;height:48px;padding:0;cursor:pointer;color:var(--ash-dim);
  transition:color var(--d-tone) var(--e-tone)}
.dv2-lb-x::before,.dv2-lb-x::after{content:"";position:absolute;
  left:14px;right:14px;top:50%;height:1px;background:currentColor}
.dv2-lb-x::before{transform:rotate(45deg)}
.dv2-lb-x::after{transform:rotate(-45deg)}
.dv2-lb-x:hover,.dv2-lb-x:focus-visible{color:var(--brass-lit)}
body.admin-bar .dv2-lb-x{top:calc(32px + clamp(14px,2.6vw,34px))}
@media screen and (max-width:782px){
  body.admin-bar .dv2-lb-x{top:calc(46px + clamp(14px,2.6vw,34px))}
}
@media (max-width:599px){
  .dv2-lb{padding:16px}
  .dv2-lb-cap{flex-direction:column;align-items:flex-start;gap:4px;
    letter-spacing:var(--t-nav)}
}
@media (prefers-reduced-motion:reduce){
  .dv2-lb-field,.dv2-lb-stage{transition:none}
  .dv2-lb-stage{transform:none}
  .dv2-root .dv2-lbo .dv2-play{transition:opacity 1ms linear;transform:none}
}

/* The parent theme wraps page content in .dba-prose.dba-reveal and leaves a
   translateY(26px) sitting on it — a reveal that never completes on these
   templates. A transformed ancestor makes position:fixed resolve against THAT
   element instead of the viewport, so the header, the Edge and the lightbox
   were all being positioned against a box thousands of pixels tall starting
   26px down the page. Measured live: the Edge reported 6373px tall against a
   908px viewport, and the header sat 26px low and scrolled away with the page
   instead of staying put. Clearing the transform restores them at once — the
   Edge measures exactly the viewport height again. Scoped to our pages, so the
   theme's own reveal is untouched everywhere else. */
body:has(.dv2-root) .dba-reveal{transform:none!important;opacity:1!important;
  transition:none!important}

/* --- the collapsed menu, below 900px -------------------------------------
   Until now .dv2-nav was simply display:none under 900px with nothing put in
   its place: on a phone the header was the logo and nothing else, and all six
   links were unreachable. 900px is deliberate — it is where the Edge already
   folds from a rail into a top progress bar, so the two stay in step.

   The panel's links are cloned from .dv2-nav at runtime rather than written
   out a second time, so this menu cannot drift from the desktop one. */
.dv2-burger{display:none}
@media (max-width:899px){
  .dv2-burger{appearance:none;-webkit-appearance:none;background:none;border:0;
    margin:0;padding:0;position:relative;z-index:2;flex:none;cursor:pointer;
    display:flex;flex-direction:column;align-items:flex-end;justify-content:center;
    gap:6px;width:44px;height:44px}
  /* Two hairlines of unequal length — the mark the logo rule, the eyebrow and
     the rail ticks already use. Not three even bars: those belong to another
     design language entirely. They cross into the same close mark the lightbox
     uses, so the two behave as one system. */
  .dv2-burger::before,.dv2-burger::after{content:"";display:block;height:1px;
    transition:width var(--d-tone) var(--e-tone),
               transform var(--d-wipe) var(--e-settle),
               background var(--d-tone) var(--e-tone)}
  .dv2-burger::before{width:22px;background:var(--brass)}
  .dv2-burger::after{width:14px;background:var(--ash-dim)}
  .dv2-burger[aria-expanded="true"]::before,
  .dv2-burger[aria-expanded="true"]::after{
    position:absolute;left:11px;top:calc(50% - 1px);width:22px;
    background:var(--brass-lit)}
  .dv2-burger[aria-expanded="true"]::before{transform:rotate(45deg)}
  .dv2-burger[aria-expanded="true"]::after{transform:rotate(-45deg)}
}
/* The bar sits at z-index 32 and this at 31, so the logo and the mark stay
   above the field, and the mark itself is what closes it. */
.dv2-menu{position:fixed;inset:0;z-index:31;display:grid;place-items:center;
  padding:var(--sp-xl) var(--gutter)}
.dv2-menu[hidden]{display:none}
.dv2-menu-field{position:absolute;inset:0;background:rgba(6,6,10,.97);
  -webkit-backdrop-filter:blur(20px) saturate(.85);
  backdrop-filter:blur(20px) saturate(.85);
  opacity:0;transition:opacity var(--d-glass) var(--e-glass)}
.dv2-menu.open .dv2-menu-field{opacity:1}
body.dv2-menu-open .dv2-head.stuck{background:none;
  -webkit-backdrop-filter:none;backdrop-filter:none}
.dv2-menu nav{position:relative;z-index:1;display:flex;flex-direction:column;
  width:100%;max-width:420px;gap:0}
/* A ledger of six rules with the current page struck in brass — the same idiom
   as the services table, at a size a thumb can hit. */
.dv2-menu nav a{position:relative;display:flex;align-items:center;min-height:60px;
  border-top:1px solid var(--hairline-soft);
  font-size:clamp(1.05rem,4.6vw,1.35rem);font-weight:200;
  letter-spacing:var(--t-nav);text-transform:uppercase;color:var(--ash-dim);
  opacity:0;transform:translateY(10px);
  transition:opacity var(--d-line) var(--e-develop),
             transform var(--d-line) var(--e-develop),
             color var(--d-tone) var(--e-tone);
  transition-delay:var(--dly,0ms)}
.dv2-menu nav a:last-child{border-bottom:1px solid var(--hairline-soft)}
.dv2-menu.open nav a{opacity:1;transform:none}
.dv2-menu nav a.on{color:var(--bone)}
.dv2-menu nav a.on::after{content:"";position:absolute;left:0;right:0;top:-1px;
  height:1px;background:var(--brass)}
@media (prefers-reduced-motion:reduce){
  .dv2-menu-field,.dv2-menu nav a{transition:none}
  .dv2-menu nav a{transform:none}
  .dv2-burger::before,.dv2-burger::after{transition:none}
}
