/* ===========================================================================
   word.css — the photo-over-giant-serif device                          (§7)

   A genuine z-index layering: a discrete rectangular photograph sitting ON TOP
   of live type that continues either side of it.

   PROHIBITION — load-bearing, do not "optimise" this away:
     never  background-clip: text        (letterform fill — rejected site B)
     never  mask-image on the glyphs
     never  a shader window in place of the photograph
   The client asked for a photograph. It is a photograph, on top of type.

   Owns: .word, .glyphs, .cut, the aperture clip-path, the 1px hairline.
   Does NOT own: .lede / .above / stage placement (leaf.css), .sr-only (base.css).
   =========================================================================== */

/* --- the block ------------------------------------------------------------ */

.word{
  position: relative;
  display: grid;
  place-items: center;
  margin: 0;
}

/* --- the glyph line ------------------------------------------------------- */

.glyphs{
  display: grid;
  grid-auto-flow: column;
  justify-content: center;
  align-items: baseline;
  z-index: 1;
  font-family: var(--f-display);
  font-variation-settings: "opsz" 96;
  font-weight: 400;
  font-size: var(--t-giant);
  letter-spacing: -0.022em;
  line-height: 0.80;
  color: var(--on-plate, var(--stencil));
}

/* --- long words --------------------------------------------------------------
   --t-giant (20.5cqw) is sized for a short word. Measured at 1440x900: a
   1293px card has a 1233px content box, and PROTECTED at 20.5cqw renders
   1492px wide — the P and D are silently clipped by the card's
   overflow:hidden. 16cqw (1164px) is the largest step that fits nine glyphs.
   YOURS, at five glyphs, keeps the full --t-giant.
   Re-measure this if the word, the face, or --card-pad changes. */
.word[data-word="protected"] .glyphs{
  font-size: clamp(3.5rem, 16cqw, 15rem);
}
@supports not (width: 1cqw){
  /* card is ~90.8vw at desktop, so 16cqw ~= 14.5vw */
  .word[data-word="protected"] .glyphs{ font-size: clamp(3.5rem, 14.5vw, 15rem) }
}

/* --- a phrase, not a word ----------------------------------------------------
   Three stacked glyph rows instead of one. The width ceiling is the same as
   PROTECTED's — the longest row, PACKAGING, is nine glyphs — but the HEIGHT
   ceiling now has to hold three of them plus the plate and the lede below, so
   the svh term is a third of what a single line could take. */
.word--phrase{
  row-gap: 0;
  justify-items: center;
}
.word--phrase .glyphs{
  font-size: min(clamp(2rem, 11.5cqw, 8.5rem), 12.5svh);
  letter-spacing: -0.015em;
}
@supports not (width: 1cqw){
  .word--phrase .glyphs{ font-size: min(clamp(2rem, 10.4vw, 8.5rem), 12.5svh) }
}
/* The word-space inside a row must not collapse in the column grid. */
.word--phrase .glyphs > span:empty,
.word--phrase .glyphs > span{ min-width: 0 }
.word--phrase .glyphs > span{ white-space: pre }

/* Centre-out stagger, computed from the layout rather than chosen.
   --a = distance from the first glyph, --b = distance from the last.
   min(--a,--b) is the distance from the nearest edge; subtracting it from --c
   (the middle glyph's edge-distance) gives the ring index outward from the
   centre. Works for any glyph count, so the narrow 5-glyph swap needs no
   special case. */
.glyphs > span{
  --a: 9;
  --b: 9;
  --d: calc(var(--c) - min(var(--a), var(--b)));
  --gd: calc(var(--d) * var(--d-glyph-stagger) * 1);
  opacity: 0;
  filter: blur(8px);            /* capped at 8px: nine spans at 19rem is the
                                   one place this design can drop frames */
  transition:
    opacity calc(var(--d-glyph) * 1) var(--e-lay) var(--gd),
    filter  calc(var(--d-glyph) * 1) var(--e-lay) var(--gd);
}

/* Zero translate on display type, ever. Only blur and opacity move. */
.leaf__body[data-live] .glyphs > span{
  opacity: 1;
  filter: blur(0px);
}
/* will-change is applied on entry and stripped on transitionend by js/word.js;
   it is deliberately absent here so nine composited layers are not permanent. */

.glyphs > span:nth-child(1){ --a: 0 }
.glyphs > span:nth-child(2){ --a: 1 }
.glyphs > span:nth-child(3){ --a: 2 }
.glyphs > span:nth-child(4){ --a: 3 }
.glyphs > span:nth-child(5){ --a: 4 }
.glyphs > span:nth-last-child(1){ --b: 0 }
.glyphs > span:nth-last-child(2){ --b: 1 }
.glyphs > span:nth-last-child(3){ --b: 2 }
.glyphs > span:nth-last-child(4){ --b: 3 }
.glyphs > span:nth-last-child(5){ --b: 4 }

/* --c = floor((glyphs - 1) / 2). 9 glyphs (PROTECTED) is the default. */
.glyphs{ --c: 4 }
.word[data-word="yours"] .glyphs{ --c: 2 }               /* no-:has fallback */
.glyphs:has(> span:nth-child(4):last-child){ --c: 1 }    /* SAFE, narrow */
.glyphs:has(> span:nth-child(5):last-child){ --c: 2 }    /* YOURS */
.glyphs:has(> span:nth-child(6):last-child){ --c: 2 }
.glyphs:has(> span:nth-child(7):last-child){ --c: 3 }
.glyphs:has(> span:nth-child(8):last-child){ --c: 3 }

/* --- the cut: a rectangular photograph over the middle glyphs ------------- */

.cut{
  /* No longer positioned over the letterforms — see compose.css, which places
     it as a plate beneath the word. Only the surface treatment lives here. */
  position: relative;
  overflow: hidden;
  border-radius: 2px;
  margin: 0;
  box-shadow: inset 0 0 0 1px var(--panel-edge);
}

/* No white gutter: the panel butts hard against the letterforms.
   One 1px inset hairline, no drop shadow — flush, like a tipped-in plate. */

.cut img{
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(calc(1.09 - 0.09 * var(--cut-t)));
  /* --cut-t is registered inherits:false, so the image drives its own copy —
     which is what lets the picture keep settling for 1400ms after the 860ms
     aperture has already stopped. */
  transition: --cut-t calc(var(--d-cut-img) * 1) var(--e-grade)
              calc(var(--d-cut-delay) * 1);
}

.word[data-word="yours"] .cut{ --cut-dur: 980 }   /* the page's final beat */

.leaf__body[data-live] .cut,
.leaf__body[data-live] .cut img{ --cut-t: 1 }

/* The hairline draws in, +700ms over 300ms. The flat box-shadow above is the
   base so the hairline is simply present without @property support.

   THE `transition` THAT USED TO BE HERE NEVER RAN, in any browser, since it was
   written. Two independent reasons, either one fatal:
     • `--hair-delay` is defined nowhere in the tree, so `calc(var(--hair-delay) * 1)`
       had no substitution and the delay was not a <time>.
     • `--cut-dur` is declared UNITLESS at :154 (`--cut-dur: 980`), so
       `calc(var(--cut-dur) * 1)` is a <number>, not a <time>, either.
   A single invalid component voids a whole shorthand at computed-value time, so
   the entire declaration — both properties — was discarded every time.

   It is deleted rather than repaired because css/motion.css:141-149 already
   implements these exact three beats correctly, and has all along. Do NOT
   "fix" this by giving --cut-dur a unit-bearing value either: css/motion.css's
   own header warns at length that `calc(<time-token> * 1ms)` is ms×ms and
   silently voids the declaration. Multiply by a plain number only. */
@supports (color: color-mix(in srgb, red, blue)){
  .cut{
    box-shadow: inset 0 0 0 1px
      color-mix(in srgb, var(--panel-edge) calc(var(--hair) * 100%), transparent);
  }
  .leaf__body[data-live] .cut{ --hair: 1 }
}

/* --- container-query-unit fallback --------------------------------------- */
/* card is ~90.8vw at desktop, so 1cqw ~= 0.908vw */
@supports not (width: 1cqw){
  .cut{ width: clamp(150px, 30.9vw, 460px) }
}

/* --- narrow cards: the 5-glyph variant ----------------------------------- */
/* js/word.js swaps the glyph line to data-narrow (SAFE on leaf 01, YOURS on
   leaf 08) so the two-legible-glyphs-either-side rule survives; the centre
   index above recomputes itself from the new count with no further CSS. */
@media (max-width: 560px){
  .cut{ width: clamp(150px, 28cqw, 460px) }
  @supports not (width: 1cqw){
    .cut{ width: clamp(150px, 25.4vw, 460px) }
  }
}

/* --- no JS: the composition is identical, the motion is simply absent ----- */
html:not(.js) .glyphs > span{
  opacity: 1;
  filter: none;
  transition: none;
}
html:not(.js) .cut,
html:not(.js) .cut img{
  --cut-t: 1;
  --hair: 1;
  transition: none;
}

/* --- reduced motion: panel open, glyphs solid ---------------------------- */
@media (prefers-reduced-motion: reduce){
  .glyphs > span{
    opacity: 1;
    filter: none;
    transition: none;
  }
  .cut,
  .cut img{
    --cut-t: 1;
    --hair: 1;
    transition: none;
  }
}
