@font-face{font-family:NeueHaas;src:url('fonts/Neue Haas Grotesk Text W02 Regular.woff2') format('woff2');font-weight:400;font-display:swap}@font-face{font-family:NeueHaas;src:url('fonts/Neue Haas Grotesk Text W02 Bold.woff2') format('woff2');font-weight:700;font-display:swap}
:root{--background:#332e2b;--text:#fff}*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden}body{background:var(--background);font-family:NeueHaas,Arial,sans-serif}.memorial{height:100dvh;position:relative;overflow:hidden;background:var(--background)}.memorial-header{position:fixed;z-index:6;top:12px;left:50%;transform:translateX(-50%);color:var(--text);text-align:left;min-width:105px;opacity:0;animation:fadeIn 1.15s ease forwards;pointer-events:auto;user-select:text}.memorial-header h1,.memorial-header p{font-size:10px;line-height:1.196;margin:0;font-weight:700;letter-spacing:-.035em}.memorial-header .date-dash{font-weight:400}.wordmark{position:fixed;z-index:5;left:25px;bottom:25px;width:min(78vw,1450px);height:auto;display:block;pointer-events:none;opacity:0;animation:fadeIn 1.15s ease forwards;filter:brightness(0)}.gallery-viewport{position:absolute;inset:0;overflow:hidden;z-index:2;opacity:0;transition:opacity 1.3s ease;outline:none;touch-action:pan-y;pointer-events:none}.gallery-viewport.is-ready{opacity:1}.gallery-track{position:absolute;left:0;top:0;width:100%;will-change:transform;pointer-events:none}.photo-item{position:absolute;margin:0;transform:translateZ(0);pointer-events:none}.photo-item img{display:block;width:100%;height:auto;box-shadow:none}.photo-item figcaption{font-size:11px;line-height:1.3;color:#f5f2ee;margin-top:7px;max-width:100%}.loading-note{position:fixed;right:16px;top:14px;font-size:11px;color:#fff;z-index:10}.loading-note:empty{display:none}@keyframes fadeIn{to{opacity:1}}@media(min-width:900px){.memorial-header{top:19px}.memorial-header h1,.memorial-header p{font-size:12px}}@media(max-width:600px){.wordmark{width:calc(100vw - 40px);left:10px;bottom:10px}.memorial-header{top:14px}.memorial-header h1,.memorial-header p{font-size:10px}}@media(prefers-reduced-motion:reduce){.memorial-header,.wordmark{animation:none;opacity:1}.gallery-viewport{transition:none}}

/* V4: the memorial typography sits two-thirds across the desktop viewport. */
@media (min-width: 900px){.memorial-header{left:66.6667%;transform:translateX(-50%)}}
/* Monochrome by default, original color on hover. Keep images behind type. */
.gallery-viewport,.gallery-track{pointer-events:none}
.photo-item{pointer-events:auto}
.photo-item img{filter:grayscale(100%);transition:filter .45s ease}
.photo-item:hover img{filter:grayscale(0)}
@media (hover:none){.photo-item img{filter:grayscale(100%)}}

/* V5: align the memorial text's left edge to an actual gallery column. */
@media (min-width: 900px){
  .memorial-header{left:var(--memorial-grid-x, 66.6667%);transform:none}
}

/* Light-background alternate: retains V5 layout and behavior. */
:root{--background:#e9e8e8;--text:#000}
.photo-item figcaption{color:#000}
.loading-note{color:#000}

/* V6 — light theme refinements */
.memorial-header .date-dash {
  display: inline-block;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 1em;
  font-weight: 100;
  letter-spacing: 0;
  transform: scaleX(1.5);
  transform-origin: center;
  margin: 0 0.24em;
  /* The margin offsets the extra visual width; there are no literal spaces in the HTML. */
}
/* A masked backdrop-filter blurs only the photograph pixels inside the SVG letterforms. */
.wordmark-glass {
  filter: none;
  background: rgba(23, 23, 23, .66);
  -webkit-backdrop-filter: blur(13px) saturate(.55);
  backdrop-filter: blur(13px) saturate(.55);
  -webkit-mask-image: url('assets/kaffer.svg');
  mask-image: url('assets/kaffer.svg');
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  aspect-ratio: 1824 / 513.1;
}
/* A fallback tint remains if backdrop-filter is unsupported. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wordmark-glass { background: rgba(23, 23, 23, .78); }
}


/* V7: thinner, narrower date separator; optical match to glass lettering. */
.memorial-header { color: #3c3b3b; }
.memorial-header .date-dash {
  transform: scaleX(1.125); /* 25% narrower than V6's 1.5x width */
  margin: 0 0.08em;
}

/* Increase opacity to reduce light transmitted through the mask by ~35%. */
.wordmark-glass { background: rgba(23, 23, 23, .78); }

/* Captions occupy a carved-out portion of the original image footprint.
   The grid coordinates, outer height, and adjacent images never change. */
.photo-item { overflow: hidden; }
.photo-item.has-caption { display: flex; flex-direction: column; }
.photo-item.has-caption img {
  height: calc(100% - var(--caption-space, 24px));
  flex: 1 1 auto;
  min-height: 0;
  object-fit: cover;
  object-position: center top;
}
.photo-item.has-caption figcaption {
  height: var(--caption-space, 24px);
  flex: 0 0 var(--caption-space, 24px);
  margin: 0;
  padding-top: 5px;
  overflow: hidden;
  color: #3c3b3b;
}

/* V8 — near-black lettering with restrained frosted-glass blur. */
.memorial-header,
.photo-item.has-caption figcaption { color: #141414; }
.wordmark-glass {
  background: rgba(0, 0, 0, .92);
  -webkit-backdrop-filter: blur(2px) saturate(.55);
  backdrop-filter: blur(2px) saturate(.55);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wordmark-glass { background: rgba(0, 0, 0, .92); }
}

/* V9 — optical baseline correction for the SVG lettering.
   Its bottom edge looked more recessed than its left edge at 25px. */
@media (min-width: 601px) {
  .wordmark-glass { bottom: 13px; }
}

/* V10 — refined typography and restrained image entrance animation. */
.memorial-header .date-dash { transform: scaleX(.95625); }
.memorial-header, .photo-item.has-caption figcaption { color: #0d0d0d; }
.wordmark-glass { background: rgba(0,0,0,.97); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wordmark-glass { background: rgba(0,0,0,.97); }
}
.photo-item { opacity: 0; transition: opacity 720ms ease-out; }
.photo-item.is-visible { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .photo-item { transition: none; }
}

/* V11 — restore a little translucency to the dark frosted-glass wordmark. */
.wordmark-glass { background: rgba(0, 0, 0, .92); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wordmark-glass { background: rgba(0, 0, 0, .92); }
}

/* V12 — softer dark glass with a visibly frosted image beneath. */
.memorial-header, .photo-item.has-caption figcaption { color: #262626; }
.wordmark-glass {
  background: rgba(0,0,0,.85);
  -webkit-backdrop-filter: blur(7.5px) saturate(.55);
  backdrop-filter: blur(7.5px) saturate(.55);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wordmark-glass { background: rgba(0,0,0,.85); }
}

/* Prevent opacity transitions during seamless loop state handoff. */
.photo-item.loop-handoff { transition: none !important; }

/* V14 — obituary reading mode. */
.memorial-header{cursor:pointer;transition:color .35s ease;}
.memorial-header:hover,.memorial-header:focus-visible{text-decoration:none;}
.wordmark{pointer-events:auto;cursor:pointer;transition:opacity .65s ease;}
.gallery-viewport{transition:opacity .8s ease;}
body.obituary-open .gallery-viewport{opacity:0 !important;pointer-events:none;}
body.obituary-open .wordmark{opacity:0 !important;pointer-events:none;animation:none;}
.obituary-overlay{position:fixed;inset:0;z-index:7;pointer-events:none;visibility:hidden;transition:visibility 0s linear .65s;}
body.obituary-open .obituary-overlay{visibility:visible;transition-delay:0s;}
.obituary-outside{position:absolute;inset:0;pointer-events:auto;}
.obituary-panel{position:absolute;left:var(--obituary-x,66.6667%);top:var(--obituary-top,82px);bottom:24px;width:min(410px,calc(100vw - var(--obituary-x,66.6667%) - 28px));overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin;outline:none;pointer-events:auto;opacity:0;transform:translateY(9px);transition:opacity .65s ease,transform .65s ease;transition-delay:0s;}
body.obituary-open .obituary-panel{opacity:1;transform:none;transition-delay:.28s;}
.obituary-copy{font-size:13px;line-height:1.55;color:#262626;padding-bottom:32px;}
.obituary-copy p{margin:0 0 1.1em;}
.obituary-copy h2{font-size:13px;line-height:1.35;margin:1.8em 0 .75em;font-weight:700;}
.obituary-copy a{color:inherit;text-decoration:underline;text-underline-offset:2px;}
.obituary-close{position:absolute;right:25px;top:var(--obituary-close-top,19px);border:0;padding:0;background:transparent;color:#262626;font:400 24px/1 NeueHaas,Arial,sans-serif;cursor:pointer;pointer-events:auto;opacity:0;transition:opacity .4s ease;}
body.obituary-open .obituary-close{opacity:1;transition-delay:.25s;}
@media(max-width:650px){.obituary-panel{width:calc(100vw - var(--obituary-x,50%) - 20px);bottom:15px}.obituary-copy{font-size:12px}.obituary-close{right:15px}}
@media(prefers-reduced-motion:reduce){.obituary-panel,.obituary-close,.gallery-viewport,.wordmark{transition:none !important}}
/* Keep the fixed memorial heading interactive above the click-away layer. */
.memorial-header{z-index:8;}

/* V15 — after the opening fade, release animation fill-mode so the SVG
   can fade out and back in with the gallery in obituary mode. */
.wordmark.intro-complete { animation: none; opacity: 1; }
body.obituary-open .wordmark { animation: none !important; opacity: 0 !important; }

/* V17 — full-width reading column, hidden scrollbar, soft upper scroll fade. */
.obituary-panel {
  right: 25px;
  width: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 42px, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 42px, #000 100%);
}
.obituary-panel::-webkit-scrollbar { display: none; width: 0; height: 0; }
.obituary-copy .obituary-quote { margin: 0 0 1.1em; padding-left: 2em; border: 0; }
.obituary-copy .obituary-quote p { margin: 0 0 1.1em; }
@media (max-width: 650px) { .obituary-panel { right: 15px; width: auto; } }

/* V18 — match the obituary column to the actual gallery right edge.
   Show the fade only once the reader has scrolled into the copy. */
.obituary-panel {
  right: var(--gallery-right-inset, 25px);
  -webkit-mask-image: none;
  mask-image: none;
}
.obituary-panel.is-scrolled {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 42px, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 42px, #000 100%);
}
@media (max-width: 650px) {
  .obituary-panel { right: var(--gallery-right-inset, 20px); }
}
.photo-item { cursor: pointer; }

/* V19 — smoothly reveal the upper reading fade rather than switching masks. */
.obituary-panel,
.obituary-panel.is-scrolled {
  -webkit-mask-image: none;
  mask-image: none;
}
.obituary-overlay::after {
  content: "";
  position: absolute;
  z-index: 2;
  left: var(--obituary-x, 66.6667%);
  right: var(--gallery-right-inset, 25px);
  top: var(--obituary-top, 82px);
  height: 48px;
  background: linear-gradient(to bottom, var(--background) 0%, rgba(233,232,232,.92) 20%, rgba(233,232,232,0) 100%);
  opacity: 0;
  pointer-events: none;
  transition: opacity 520ms ease;
}
.obituary-overlay:has(.obituary-panel.is-scrolled)::after { opacity: 1; }
/* Keep the obituary's leading close to the fixed memorial heading. */
.obituary-copy { line-height: 1.25; }
@media (max-width: 650px) {
  .obituary-overlay::after { right: var(--gallery-right-inset, 20px); }
}
@media (prefers-reduced-motion: reduce) {
  .obituary-overlay::after { transition: none; }
}

/* V20 — permanent upper fade, placed above the first line of obituary copy.
   The fade never switches on during scrolling; copy moves behind it. */
.obituary-panel {
  top: calc(var(--obituary-top, 82px) - 48px);
}
.obituary-copy {
  padding-top: 48px;
  line-height: 1.4;
}
.obituary-overlay::after {
  top: calc(var(--obituary-top, 82px) - 48px);
  height: 48px;
  opacity: 1;
  transition: none;
}

/* V21 — softer, fixed fade zones at both ends of the obituary viewport.
   Start the copy one line lower, beyond the top fade; scrolling carries
   it behind the heading without ever exposing a hard clipping edge. */
.obituary-panel {
  top: calc(var(--obituary-top, 82px) - 64px);
}
.obituary-copy {
  padding-top: 88px;
}
.obituary-overlay::after {
  top: calc(var(--obituary-top, 82px) - 64px);
  height: 88px;
  background: linear-gradient(to bottom,
    var(--background) 0%,
    var(--background) 26%,
    rgba(233,232,232,.94) 48%,
    rgba(233,232,232,.55) 74%,
    rgba(233,232,232,0) 100%);
}
/* Bottom fade is always present and does not affect the scrollable panel. */
.obituary-overlay::before {
  content: "";
  position: absolute;
  z-index: 2;
  left: var(--obituary-x, 66.6667%);
  right: var(--gallery-right-inset, 25px);
  bottom: 24px;
  height: 88px;
  background: linear-gradient(to bottom,
    rgba(233,232,232,0) 0%,
    rgba(233,232,232,.55) 52%,
    var(--background) 100%);
  pointer-events: none;
}
@media (max-width: 650px) {
  .obituary-overlay::before {
    right: var(--gallery-right-inset, 20px);
    bottom: 15px;
  }
}

/* V22 — extra scroll runway after the final paragraph so the ending can
   move fully above the fixed bottom fade and remain legible. */
.obituary-copy {
  padding-bottom: 152px;
}

/* V23 — fade the reading panel itself, not a separate overlay.
   This keeps both fades confined to the obituary and prevents them from
   appearing over the gallery during the close transition. */
.obituary-overlay::before,
.obituary-overlay::after {
  content: none !important;
  display: none !important;
}
.obituary-panel,
.obituary-panel.is-scrolled {
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0,
    transparent 24px,
    #000 88px,
    #000 calc(100% - 44px),
    transparent 100%);
  mask-image: linear-gradient(to bottom,
    transparent 0,
    transparent 24px,
    #000 88px,
    #000 calc(100% - 44px),
    transparent 100%);
}
/* Enough room for the last line to clear the shorter bottom fade. */
.obituary-copy { padding-bottom: 56px; }

/* V24 — restore the longer, gentler top fade independently of the
   shorter 44px bottom fade. Keep both on the panel so they disappear
   with the obituary when returning to the gallery. */
.obituary-panel,
.obituary-panel.is-scrolled {
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0px,
    transparent 23px,
    rgba(0,0,0,.06) 42px,
    rgba(0,0,0,.45) 65px,
    #000 88px,
    #000 calc(100% - 44px),
    transparent 100%);
  mask-image: linear-gradient(to bottom,
    transparent 0px,
    transparent 23px,
    rgba(0,0,0,.06) 42px,
    rgba(0,0,0,.45) 65px,
    #000 88px,
    #000 calc(100% - 44px),
    transparent 100%);
}

/* V25 — mobile-only layout. Desktop declarations remain untouched. */
@media (max-width: 600px) {
  /* Match the gallery's 20px inset on both sides. */
  .wordmark.wordmark-glass {
    left: var(--gallery-right-inset, 20px);
    right: var(--gallery-right-inset, 20px);
    width: calc(100vw - 2 * var(--gallery-right-inset, 20px));
    bottom: 10px;
  }

  /* Full-width heading, right edge shared with wordmark and photo field. */
  .memorial-header {
    left: var(--gallery-right-inset, 20px);
    right: var(--gallery-right-inset, 20px);
    width: auto;
    min-width: 0;
    transform: none;
    text-align: right;
  }
  .memorial-header h1,
  .memorial-header p { font-size: 13px; }

  /* Reading area fills the same inset-to-inset width. */
  .obituary-panel {
    left: var(--gallery-right-inset, 20px);
    right: var(--gallery-right-inset, 20px);
    width: auto;
  }
  .obituary-copy { font-size: 15.6px; }
  .obituary-copy h2 { font-size: 16.9px; }
  .obituary-close {
    right: var(--gallery-right-inset, 20px);
    font-size: 31.2px;
  }
}

/* V26 — mobile-only: heading left-aligned to the full gallery field. */
@media (max-width: 600px) {
  .memorial-header { text-align: left; }
  .memorial-header h1, .memorial-header p { text-align: left; }
}

/* V29 — mobile-only image color passage and typography adjustments. */
@media (max-width: 600px) {
  /* Dissolve from grayscale to full color when the image reaches the mobile trigger. */
  .photo-item img { transition: filter 900ms ease-in-out; }
  .photo-item:hover img { filter: grayscale(100%); }

  /* 25% smaller captions; 35% larger heading, obituary and close. */
  .photo-item figcaption,
  .photo-item.has-caption figcaption { font-size: 8.25px; }
  .memorial-header h1,
  .memorial-header p { font-size: 17.55px; }
  .obituary-copy { font-size: 21.06px; }
  .obituary-copy h2 { font-size: 22.815px; }
  .obituary-close { font-size: 42.12px; }
}

/* V30 — mobile only: matched reading typography and heading-aligned close. */
@media (max-width: 600px) {
  .obituary-copy, .obituary-copy h2 { font-size: 17.55px; }
  /* Optical alignment: move the multiplication glyph's visible top to the heading. */
  .obituary-close { top: calc(var(--obituary-close-top, 14px) - 8px); }
}

/* V36 — mobile-only: slower image entrance, with color retained after activation. */
@media (max-width: 600px) {
  .photo-item { transition: opacity 1300ms ease-out; }
}

/* V40 — suppress filter interpolation while resetting off-screen or handing off loop copies. */
@media (max-width: 600px) {
  .photo-item.color-reset img, .photo-item.loop-handoff img { transition: none !important; }
}

/* V46 — mobile reverse travel: show returning photos without an opacity fade.
   This does not modify grayscale or the forward entrance transition. */
@media (max-width: 600px) {
  .photo-item.reverse-reveal { transition: none !important; }
}

/* V47 — reverse movement retains full opacity until the image is fully offscreen. */
