/* Jovonte's wall. Black, his photographs edge to edge. One typeface, kept with the site: Inter Tight, set heavy and tight
   for his name and titles, small and widely spaced for labels, light for reading. One easing pair and one set of icons
   for the whole page. */
@font-face { font-family: "Inter Tight"; src: url("fonts/inter-tight.woff2") format("woff2"); font-weight: 300 700; font-style: normal; font-display: block; }
@font-face { font-family: "Tenor Sans"; src: url("fonts/tenor-sans.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: block; }

:root {
  color-scheme: dark;
  /* All motion on the page shares these, so it reads as one piece. */
  --arrive: cubic-bezier(0.22, 0.7, 0.2, 1); /* arrivals: they begin at once, then settle slowly */
  --fade: cubic-bezier(0.45, 0, 0.55, 1);  /* fades and departures: gentle at both ends */
  --sans: "Inter Tight", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --soft: rgba(255, 255, 255, 0.72);
  --glass-line: rgba(255, 255, 255, 0.13);
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.24) #000;
}
/* No scrollbar over the intro: it only appears once the GoFundMe page begins to rise. */
html:has(body:not(.fundup)) { scrollbar-width: none; }
* { box-sizing: border-box; }
[hidden] { display: none !important; }
html { background: #000; color: #FFF; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
html:has(dialog[open]) { overflow: hidden; }
body { margin: 0; min-height: 100vh; font: 400 16px/1.5 var(--sans); letter-spacing: 0.005em; -webkit-font-smoothing: antialiased; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 3px; }
button { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid #FFF; outline-offset: 2px; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
/* Every icon on the site is drawn the same way: a fine round line in the colour of its text. */
svg path { fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
svg circle { fill: currentColor; }

/* ---------- Intro. One slow piece of film, told by one light.
   Under the words runs a quiet edit of his photographs in black and white, each one blurring in with its edges falling
   away to nothing, so it melts into the one before and the one after. The last fills the screen. A light crosses and the
   dedication is there where it has passed; later the light moves on and takes it away. The room dims, his name rises
   faintly out of the dark, and the same light crosses it and leaves it lit. His name goes, and the GoFundMe page comes
   up out of the dark. Nothing glows, nothing is warm, and nothing is ever quite still. The schedule is at the top of the
   intro in wall.js. All of it moves on --fade. ---------- */
.intro {
  --edge: clamp(20px, 4.2vw, 64px);
  --ink: #ECECEC;
  /* The light's edge, for words it brings and words it takes. A soft ramp slid across the words from left to right. */
  --bring: linear-gradient(to right, #000 0%, #000 42%, transparent 58%, transparent 100%);
  --take: linear-gradient(to right, transparent 0%, transparent 42%, #000 58%, #000 100%);
  position: fixed; inset: 0; z-index: 10; overflow: hidden; background: #000;
}
/* The real wall waits unseen. It comes after the GoFundMe page. */
body:not(.fundup) main { visibility: hidden; }

/* The edit: a dark room with soft-edged plates in it (wall.js paints them), each only ever faded and slowly moved. Where
   two overlap they add, like a double exposure. */
.intro-film { position: absolute; inset: 0; z-index: 0; overflow: hidden; background: #070707; }
.plate { position: absolute; mix-blend-mode: screen; will-change: transform, opacity; }
.plate canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; }
/* The room. The edges fall away, and the whole picture is exposed lower or higher as the beats change: it dims before
   his name, and lifts as the light crosses it. */
.intro-shade {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(4, 4, 4, 0.5), rgba(4, 4, 4, 0) 26%),
    radial-gradient(120% 100% at 50% 48%, rgba(4, 4, 4, 0) 34%, rgba(4, 4, 4, 0.7) 100%);
}
/* A band of shadow gathers where his name will be, so it sits on calm dark and not on a face. */
.intro-shade::before { content: ""; position: absolute; inset: 0; opacity: 0; background: linear-gradient(to bottom, rgba(4, 4, 4, 0) 26%, rgba(4, 4, 4, 0.62) 50%, rgba(4, 4, 4, 0) 74%); transition: opacity 2.8s var(--fade); }
.named .intro-shade::before { opacity: 1; }
.intro-shade::after { content: ""; position: absolute; inset: 0; background: #040404; opacity: 0.06; transition: opacity 3s var(--fade); }
.dedicate .intro-shade::after { opacity: 0.18; }
.dedicated .intro-shade::after { opacity: 0.56; transition-duration: 2.6s; }
.named .intro-shade::after { opacity: 0.34; transition-duration: 2.8s; }
.name-lit .intro-shade::after { opacity: 0.22; transition-duration: 4.4s; }
/* The light itself, on the cover: a wide, faint, cool band that crosses with the edge on the words, so the words and
   the wall are lit by the same thing. */
.intro-sweep {
  position: absolute; z-index: 3; top: -30%; bottom: -30%; left: 0; width: 80vw; opacity: 0; pointer-events: none;
  background: radial-gradient(closest-side, rgba(226, 226, 226, 0.13), rgba(226, 226, 226, 0.05) 52%, rgba(226, 226, 226, 0));
  transform: translate3d(-85vw, 0, 0) rotate(7deg); will-change: transform;
}
@keyframes crossing { 0% { opacity: 0; transform: translate3d(-85vw, 0, 0) rotate(7deg); } 22% { opacity: 1; } 78% { opacity: 1; } 100% { opacity: 0; transform: translate3d(105vw, 0, 0) rotate(7deg); } }
@keyframes crossing-again { 0% { opacity: 0; transform: translate3d(-85vw, 0, 0) rotate(7deg); } 22% { opacity: 1; } 78% { opacity: 1; } 100% { opacity: 0; transform: translate3d(105vw, 0, 0) rotate(7deg); } }
.dedicate .intro-sweep { animation: crossing 3.2s var(--fade) both; }
.name-lit .intro-sweep { animation: crossing-again 4.4s var(--fade) both; }
/* No photos on the wall (or they did not load): his own photo, blurred and dark, stands in. */
.intro::before {
  content: ""; position: absolute; inset: -80px; z-index: -1;
  background: #3a3431 url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAkGBwgHBgkICAgKCgkLDhcPDg0NDhwUFREXIh4jIyEeICAlKjUtJScyKCAgLj8vMjc5PDw8JC1CRkE6RjU7PDn/2wBDAQoKCg4MDhsPDxs5JiAmOTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTn/wAARCAA/ACQDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDvWJGOnQdh6UzJ9vyFYZ8RvkbrRMYGcOfSo7jxZa2kXm3FtIqk4G1gSTXprFUn1PCWX4mUlGMbtnSKT7fkKeCfQfkK5a18WtfLusdLlkXcVyzAdMcnsOo71di8V6cXSGVJ4bhhkxOuCOSO+PQ0e3pvqa1MpxdNe9D8nb1SOlt87DwOvoKKzbbX7HYflm6/3P8A69FZOtC+5y/VK38rONcBV3NgKBkmuJ8R6vNcXhhtoY5LaIgKTkMzGu11WF2sJlVSr7OMiud0TRzPcz7o96ox+UNgkjAznt/FXlOXKz6zDucZKUHZmHb+ItWsENtHFJEqkkopBAJ69R9Kp6jrs99cedcs6yhQmdgAAHTpXaX+mi3jicwoskrFHCSFsD2Y965DXLVbO6hkkjaJJH2MpO48H1FVCpc6qmIrRfO2r97L80keh6NcrcaXbTB9++NSW9TgZorntCvmttPWEnzEU/IwGfl7UVVkec3qdJCBNa5YkiQHn61W0qwaytb2C2xHJKQ2em4Z5/nVOw160MCqGDKq4yrZx9avaJqltq888dszrcQJuw64xzj8ayqq8bnRRjOEk7aMqa7aYt0kZIvtK4O0LwD9c/riuU8WTieWJJ5lEiAH5e7e1dN4k1DUY28k6eqsf+WoOVP0rgJI5LvVJN53FMAmikr6mmInZWRuWeqfZ4in2SNznJOSOwopEtsKMCiulJHDYLyVI9XuUtpQYFk+UDpj2qv4a1WXT9YmuosHKMCrDhgT0/SnrDY4dxfTBjlj5lsOT+DVT0CzaeWX97DG20YEm7nPpgGotdNM9ySXPShJd+nkdPrniY3SIFtZFYrkZYEZ6VzHhv8AfXV5u5bIb9TWleaXdWsX2naklu2SJY24/I4P6Vk+F5NupyjGfMUj8etKnFRWhyY2EU1y+Z06R8dKKPMfJwMc9BRWhwu19D//2Q==") center 30% / cover;
  filter: blur(60px) brightness(0.32) saturate(0.6);
  opacity: 0;
}
.empty .intro::before, .failed .intro::before, .no-back .intro::before { opacity: 1; }
.empty .intro-film, .failed .intro-film, .no-back .intro-film { background: none; }
/* The faintest film grain, over the words as well as the wall, so they are the same picture. */
.intro::after {
  content: ""; position: absolute; inset: -60px; z-index: 7; pointer-events: none; opacity: 0.1;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1.1 0 0 0 -.3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain 1.5s steps(1) infinite;
}
@keyframes grain { 0% { transform: translate(0, 0); } 20% { transform: translate(-22px, 14px); } 40% { transform: translate(16px, -26px); } 60% { transform: translate(-12px, -18px); } 80% { transform: translate(26px, 20px); } }
/* Nothing else on the page shows through: not the footer, not its add button. */
body:not(.shown) .memory, body:not(.shown) .foot, body:not(.shown) .wall-status { visibility: hidden; }
/* Done: the GoFundMe page covers the screen, so the intro simply goes. */
.shown .intro { visibility: hidden; pointer-events: none; }
/* Skip: the smallest arrow, almost hidden, top right, level with the dedication and on the same margin. It comes up with
   the dedication and leaves with his name. The button around it keeps a full finger's worth to tap. */
.intro-skip {
  position: fixed; z-index: 13; display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 0; background: none; cursor: pointer;
  top: calc(max(clamp(20px, 4.2vw, 64px), env(safe-area-inset-top)) - 14px);
  right: calc(max(clamp(20px, 4.2vw, 64px), env(safe-area-inset-right)) - 16px);
  color: rgba(236, 236, 236, 0.34);
  opacity: 0; transition: opacity 2.4s var(--fade), color 0.6s var(--fade);
}
.dedicate .intro-skip { opacity: 1; }
.intro-skip:hover, .intro-skip:focus-visible { color: rgba(236, 236, 236, 0.9); }
.name-out .intro-skip { opacity: 0; pointer-events: none; transition-duration: 1.4s; }
.shown .intro-skip { display: none; }
.instant .fp-photo img, .instant .fp-text, .instant .fp-bar::after, .instant .fp-bar span::after { animation: none; }
.instant .fp-bar span { transition: none; }

/* The dedication: small spaced capitals in the top left corner, on the margin. The light's edge brings it, and when the
   light moves on it goes the same way, left to right. It opens out a little the whole time it is there. */
.title-line {
  position: absolute; z-index: 6; margin: 0; white-space: nowrap;
  left: max(var(--edge), env(safe-area-inset-left)); top: max(var(--edge), env(safe-area-inset-top));
  font: 400 clamp(14px, 1.5vw, 25px) / 1 "Tenor Sans", var(--sans); letter-spacing: 0.36em; text-transform: uppercase; color: var(--ink);
  text-rendering: geometricPrecision; transform-origin: 0 50%;
  -webkit-mask: var(--bring) 87% 0 / 300% 100% no-repeat; mask: var(--bring) 87% 0 / 300% 100% no-repeat;
}
@keyframes light-pass { from { -webkit-mask-position: 87% 0; mask-position: 87% 0; } to { -webkit-mask-position: 13% 0; mask-position: 13% 0; } }
.dedicate .title-line { animation: light-pass 3.2s var(--fade) both; scale: 1.04; transition: scale 8s linear; }
.dedicated .title-line { -webkit-mask-image: var(--take); mask-image: var(--take); animation: light-pass-on 2.6s var(--fade) both; }
@keyframes light-pass-on { from { -webkit-mask-position: 87% 0; mask-position: 87% 0; } to { -webkit-mask-position: 13% 0; mask-position: 13% 0; } }

/* His name: dead centre, fitted between the margins (wall.js), one line on a wide screen and two on a tall one. It rises
   as a dim silver, and comes very slowly toward us until it goes. */
.intro-name {
  position: absolute; z-index: 5; left: var(--edge); right: var(--edge); top: 50%; margin: 0; transform: translateY(-50%);
  font: 400 100px / 1.1 "Tenor Sans", var(--sans); letter-spacing: 0.03em; text-transform: uppercase; text-align: center; white-space: nowrap;
  text-rendering: geometricPrecision; color: rgba(190, 190, 190, 0.42);
  opacity: 0; will-change: opacity, scale;
}
.intro-name .nm { display: block; }
@media (min-aspect-ratio: 6 / 5) { .intro-name .nm { display: inline; } }
.named .intro-name { opacity: 1; scale: 1.026; transition: opacity 2.8s var(--fade), scale 10s linear; }
.name-out .intro-name { opacity: 0; transition: opacity 2s var(--fade), scale 10s linear; }
/* The same letters, lit. The light's edge uncovers them from left to right and leaves them on; a faint, slow sheen keeps
   moving through them afterward, so the white is never flat. */
.intro-name .shine {
  position: absolute; inset: 0; color: transparent;
  background: linear-gradient(100deg, #D2D2D2 0%, #F5F5F5 30%, #FFFFFF 50%, #F5F5F5 70%, #D2D2D2 100%) 100% 0 / 260% 100% no-repeat;
  -webkit-background-clip: text; background-clip: text;
  -webkit-mask: var(--bring) 87% 0 / 300% 100% no-repeat; mask: var(--bring) 87% 0 / 300% 100% no-repeat;
}
@keyframes sweep { from { background-position: 130% 0; } to { background-position: -30% 0; } }
@keyframes name-sheen { from { background-position: 100% 0; } to { background-position: 0% 0; } }
.name-lit .intro-name .shine { animation: light-pass 4.4s var(--fade) both, name-sheen 7s linear both; }

/* ---------- GoFundMe: the intro ends in the fund's page, which comes up out of the dark and fills the screen.
   His photo on one side (on top on a phone), the fund, how far it has come, and one way to give. Scrolling down
   leaves it behind for the wall, and their small logo waits in the corner. ---------- */
.fund-page {
  position: relative; z-index: 11; display: none;
  grid-template-rows: minmax(0, 1fr) auto auto;
  height: 100vh; height: 100svh; min-height: 340px; overflow: hidden;
  border-radius: 0 0 28px 28px; background: #0B0B0B; color: #FFF; font-family: var(--sans);
}
/* It comes up in two steps so no photo ever fades through another: the edit sinks into the page's dark, then his photo
   rises in it. */
.fundup .fund-page { display: grid; animation: card-up 1.6s var(--fade) both; }
.fundup .fp-photo { animation: card-up 1.8s var(--fade) 1.2s both; }
.instant .fp-photo { animation: none; }
.quick .fund-page { animation-duration: 1s; }
.instant .fund-page { animation: none; }
.folded .fund-page { display: none; } /* folded into the corner logo */
@keyframes card-up { from { opacity: 0; } }

.fp-photo { position: relative; display: block; min-height: 0; overflow: hidden; }
.fp-photo img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 30% 28%; filter: saturate(0.88) brightness(0.9); }
.fp-photo::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to bottom, rgba(11, 11, 11, 0.25), rgba(11, 11, 11, 0) 22%, rgba(11, 11, 11, 0) 50%, #0B0B0B 98%); }
/* As the page comes up its photo warms into colour and settles, and the words drift in after. */
.fundup .fp-photo img { animation: settle 6s var(--arrive) both, warm 3s var(--fade) both; }
@keyframes settle { from { transform: scale(1.06); } }
@keyframes warm { from { filter: saturate(0.55) brightness(0.32); } }
.fundup .fp-text { animation: words-in 1.6s var(--arrive) 2.4s both; }
@keyframes words-in { from { opacity: 0; transform: translateY(14px); } }
.quick .fp-text { animation-delay: 0.5s; }
.instant .fp-photo img, .instant .fp-text { animation: none; }

.fp-text { position: relative; z-index: 1; justify-self: center; width: 100%; max-width: 620px; margin-top: -72px; padding: 0 max(28px, env(safe-area-inset-left)) 0 max(28px, env(safe-area-inset-right)); }
.fp-logo { display: block; width: 92px; height: 32px; background: #16A05A; -webkit-mask: url("gofundme.svg") left center / contain no-repeat; mask: url("gofundme.svg") left center / contain no-repeat; }
.fp-title { margin: 16px 0 0; font: 700 clamp(30px, 9vw, 48px) / 0.92 var(--sans); letter-spacing: -0.035em; text-transform: uppercase; }
.fp-title span { display: block; }
.fp-line { margin: 10px 0 0; font-size: clamp(16px, 4.4vw, 20px); font-weight: 300; line-height: 1.4; color: rgba(255, 255, 255, 0.7); }
.fp-stats { margin-top: clamp(18px, 3svh, 30px); }
.fp-raised { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; margin: 0; }
.fp-amount { font-size: clamp(30px, 8vw, 40px); font-weight: 300; line-height: 1; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.fp-of { font-size: 15px; font-weight: 300; color: rgba(255, 255, 255, 0.6); }
.fp-pct { margin-left: auto; font-size: 12px; letter-spacing: 0.16em; color: #6FD69B; font-variant-numeric: tabular-nums; }
/* The progress bar: GoFundMe green, deepening along its length. It fills as the page arrives; then a soft light passes
   along it now and then and its leading edge breathes, so it feels quietly alive. */
.fp-bar { position: relative; height: 4px; margin-top: 16px; border-radius: 4px; background: rgba(255, 255, 255, 0.1); }
.fp-bar span { position: relative; display: block; width: calc(min(var(--p), 1) * 100%); height: 100%; overflow: hidden; border-radius: inherit; background: linear-gradient(90deg, #0B6B3D, #16A05A 60%, #49C982); transform: scaleX(0); transform-origin: 0 50%; }
.fundup .fp-bar span { transform: none; transition: transform 2.4s var(--arrive) 1.2s; }
.instant .fp-bar span { transition: none; }
.fp-bar span::after { content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 45%; background: linear-gradient(90deg, rgba(255, 255, 255, 0), rgba(220, 255, 232, 0.55), rgba(255, 255, 255, 0)); transform: translateX(-110%); }
.fundup .fp-bar span::after { animation: sheen 5.5s var(--fade) 4s infinite; }
@keyframes sheen { 0% { transform: translateX(-110%); } 55%, 100% { transform: translateX(240%); } }
.fp-bar::after { content: ""; position: absolute; top: 50%; left: calc(min(var(--p), 1) * 100%); width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 50%; background: #8BEAB0; box-shadow: 0 0 10px 3px rgba(73, 201, 130, 0.45); opacity: 0; }
.fundup .fp-bar::after { animation: tip-in 1s var(--fade) 3.2s forwards, breathe 3.2s var(--fade) 4.2s infinite; }
@keyframes tip-in { to { opacity: 1; } }
@keyframes breathe { 0%, 100% { opacity: 1; box-shadow: 0 0 10px 3px rgba(73, 201, 130, 0.45); } 50% { opacity: 0.7; box-shadow: 0 0 16px 6px rgba(73, 201, 130, 0.22); } }
.fp-meta { display: flex; align-items: center; flex-wrap: wrap; margin: 12px 0 0; font-size: 11.5px; line-height: 1.6; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(255, 255, 255, 0.52); }
.fp-dot { width: 3px; height: 3px; margin: 0 12px; border-radius: 50%; background: currentColor; }
@media (max-aspect-ratio: 1 / 1) { .fp-meta { flex-direction: column; align-items: flex-start; } .fp-dot { display: none; } }
.fp-donate {
  display: flex; align-items: center; justify-content: center; height: 56px; margin-top: clamp(18px, 3svh, 30px);
  border-radius: 999px; background: #CCF88E; color: #1E3A28; text-decoration: none;
  font: 500 13px / 1 var(--sans); letter-spacing: 0.22em; text-transform: uppercase; padding-left: 0.22em;
  transition: background-color 0.4s var(--fade);
}
.fp-donate:hover { background: #DAFBA8; }
.fp-down { display: flex; flex-direction: column; align-items: center; gap: 2px; justify-self: center; padding: 14px 20px calc(10px + env(safe-area-inset-bottom)); color: rgba(255, 255, 255, 0.45); text-decoration: none; font-size: 10.5px; letter-spacing: 0.3em; text-transform: uppercase; transition: color 0.4s var(--fade); }
.fp-down span { padding-left: 0.3em; }
.fp-down svg { animation: nudge 3.4s var(--fade) infinite; }
@keyframes nudge { 0%, 100% { transform: none; } 50% { transform: translateY(4px); } }
.fp-down:hover { color: #FFF; }

/* A wide screen: his photo fills the left, the fund sits on the right. */
@media (min-aspect-ratio: 1 / 1) {
  .fund-page { grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
  .fp-photo { grid-column: 1; grid-row: 1 / 3; }
  .fp-photo img { object-position: 28% 30%; }
  .fp-photo::after { background: linear-gradient(to right, rgba(11, 11, 11, 0) 58%, #0B0B0B 100%); }
  .fp-text { grid-column: 2; grid-row: 1; align-self: center; width: 100%; max-width: 640px; margin: 0; padding: 48px clamp(32px, 6vw, 110px) 0 clamp(28px, 3.4vw, 60px); }
  .fp-logo { width: 104px; height: 36px; }
  .fp-title { margin-top: 24px; font-size: clamp(34px, 3.9vw, 64px); }
  .fp-line { margin-top: 14px; font-size: clamp(17px, 1.45vw, 22px); }
  .fp-stats { margin-top: clamp(24px, 5svh, 48px); }
  .fp-amount { font-size: clamp(34px, 3.3vw, 52px); }
  .fp-of { font-size: 16px; }
  .fp-meta { font-size: 12px; }
  .fp-donate { max-width: 400px; height: 58px; margin-top: clamp(24px, 4.5svh, 44px); }
  .fp-down { grid-column: 2; grid-row: 2; }
}
@media (min-aspect-ratio: 1 / 1) and (max-height: 560px) {
  .fp-text { padding-top: 16px; }
  .fp-logo { width: 80px; height: 28px; }
  .fp-title { margin-top: 10px; font-size: 26px; }
  .fp-title span { display: inline; }
  .fp-line { display: none; }
  .fp-stats { margin-top: 14px; }
  .fp-amount { font-size: 28px; }
  .fp-donate { height: 46px; margin-top: 14px; }
  .fp-down { padding-top: 4px; }
}

/* Small: GoFundMe's own wordmark on their lime, top right, once their page has scrolled away. */
.fund {
  position: fixed; z-index: 11;
  top: calc(10px + env(safe-area-inset-top)); right: calc(10px + env(safe-area-inset-right));
  display: flex; align-items: center; height: 40px; padding: 0 15px; border-radius: 999px; background: #CCF88E;
  box-shadow: 0 6px 22px rgba(0, 0, 0, 0.35);
  opacity: 0; visibility: hidden; transform: translateY(-6px);
  transition: opacity 0.7s var(--fade), transform 0.7s var(--fade), visibility 0s linear 0.7s;
}
.shown.past .fund, .shown.minimized .fund, .viewer .fund { opacity: 1; visibility: visible; transform: none; transition: opacity 0.7s var(--fade), transform 0.7s var(--fade); }
.fund-word { display: block; width: 78px; height: 27px; background: #274A34; -webkit-mask: url("gofundme.svg") center / contain no-repeat; mask: url("gofundme.svg") center / contain no-repeat; }
.fund::after { content: ""; position: absolute; inset: -4px; } /* a full 48 px to tap */

/* ---------- The one solid button: Add photos, Try again. ---------- */
.add {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 52px; margin-top: 30px; padding: 0 32px 0 36px;
  border: 1px solid #FFF; border-radius: 999px; background: #FFF; color: #000;
  font: 500 13px / 1 var(--sans); letter-spacing: 0.24em; text-transform: uppercase; white-space: nowrap; cursor: pointer;
  transition: background-color 0.35s var(--fade), color 0.35s var(--fade);
}
.add:hover { background: transparent; color: #FFF; }

/* ---------- The grid: three across, all the way down. Black only in the seams. ---------- */
@keyframes rise { from { opacity: 0; } }
.wall { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 2px; margin: 0; padding: 0; list-style: none; }
.tile { position: relative; }
.tile > button { position: relative; display: block; width: 100%; aspect-ratio: 1; padding: 0; border: 0; overflow: hidden; background: #0E0E0E; cursor: zoom-in; }
/* Each photo eases in as it arrives instead of popping into an empty square. */
.tile img { display: block; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 0.7s var(--fade); }
.tile img.in { opacity: 1; }
.tile > button:focus-visible { outline-offset: -2px; }
/* A video plays a short silent preview in its tile: while the mouse rests on it, or on a phone while it is in view. */
.tile-preview { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; pointer-events: none; transition: opacity 0.6s var(--fade); }
.tile-preview.on { opacity: 1; }
/* The ⋯ on each photo, top right: it appears with the mouse over the photo, or with the keyboard. On a phone it stays
   out of the way; the same option is in the ⋯ of the photo opened whole. */
.tile .dots {
  position: absolute; top: 6px; right: 6px; z-index: 1; display: grid; place-items: center;
  width: 30px; height: 30px; padding: 0; border: 0; border-radius: 50%;
  background: rgba(0, 0, 0, 0.45); color: rgba(255, 255, 255, 0.94); cursor: pointer;
  opacity: 0; transition: opacity 0.35s var(--fade), background-color 0.35s var(--fade);
}
.tile .dots::after { content: ""; position: absolute; inset: -7px; } /* 44 px to tap */
.tile:hover .dots, .tile:focus-within .dots, .tile .dots[aria-expanded="true"] { opacity: 1; }
.tile .dots:hover { background: rgba(0, 0, 0, 0.7); }
@media (hover: none) { .tile .dots { display: none; } }
body:not(.shown) .dots { visibility: hidden; }
/* Its menu. */
.dots-menu {
  position: fixed; inset: auto; margin: 0; min-width: 236px; padding: 6px;
  border: 1px solid var(--glass-line); border-radius: 14px; background: #141414; color: #FFF;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
}
.dots-menu:popover-open { animation: menu-in 0.3s var(--fade) both; }
@keyframes menu-in { from { opacity: 0; transform: translateY(-4px); } }
.dots-menu button { display: block; width: 100%; padding: 12px 14px; border: 0; border-radius: 9px; background: none; color: #FFF; font: 400 15px / 1.3 var(--sans); text-align: left; cursor: pointer; }
.dots-menu button:hover, .dots-menu button:focus-visible { background: rgba(255, 255, 255, 0.08); }

/* What the wall says when something is wrong: a link to a photo that has gone, or photos that didn't load. */
.wall-status { margin: 0; padding: 28px 24px; text-align: center; font-size: 15px; font-weight: 300; color: var(--soft); }
.wall-status:empty { display: none; }
.wall-status p { margin: 0; }
.failed.shown .wall-status { display: grid; place-content: center; justify-items: center; gap: 12px; min-height: 60vh; padding: 0 24px; }
.failed .wall-status .add { margin: 0; }
.failed .memory, .failed .foot { display: none; }
.linklike { padding: 0; border: 0; background: none; cursor: pointer; }

/* ---------- The end of the page: pure black, carrying on from the wall. The footer's own rules are in foot.css, shared
   with the About page. ---------- */
/* Share a memory: a section of its own, one more row of the wall: the height of a row of photos, with the button exactly
   as wide as the wall's middle column, so its edges fall under the seams. The last photos fall away into the black above
   it, the way the fund's photo does. */
.memory { container-type: inline-size; position: relative; display: grid; align-items: center; aspect-ratio: 3 / 1; min-height: 340px; width: 100%; text-align: center; background: #000; }
.memory::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: clamp(64px, 8vw, 132px); background: linear-gradient(to bottom, rgba(0, 0, 0, 0), #000); pointer-events: none; }
/* The heavy capitals of his name and the fund's title, with his name's finish and the same light crossing it once. */
.memory-title {
  margin: 0; padding-bottom: 0.04em; font: 600 min(9.6cqw, max(6.6cqw, 40px), 124px) / 1 var(--sans); letter-spacing: 0.01em; text-transform: uppercase; white-space: nowrap;
  color: transparent;
  background:
    linear-gradient(100deg, rgba(255, 255, 255, 0) 42%, rgba(255, 255, 255, 0.9) 50%, rgba(255, 255, 255, 0) 58%) 130% 0 / 300% 100% no-repeat,
    linear-gradient(to bottom, #FFF 0%, #E6E6E6 55%, #BDBDBD 100%);
  -webkit-background-clip: text; background-clip: text;
  filter: drop-shadow(0 0 min(0.08em, 9px) rgba(255, 255, 255, 0.14));
}
/* The title glows faintly and breathes, slowly, like the tip of the fund's bar. The glow stays close to the letters, so
   the black around it stays black. */
@keyframes title-glow { 50% { filter: drop-shadow(0 0 min(0.11em, 13px) rgba(255, 255, 255, 0.2)); } }
/* Its own layer, so on Windows the words get clean edges rather than coloured ones. */
.memory-in { display: grid; justify-items: center; gap: clamp(30px, 3cqw, 44px); width: 100%; padding: 64px 20px; will-change: transform; }
.memory-note { max-width: 30em; margin: 0; font-size: 15px; font-weight: 300; line-height: 1.5; color: rgba(255, 255, 255, 0.7); }
.memory-note a { color: #FFF; }
/* The button: one plain dark grey, the colour of the site's menus and fields. Under the mouse white opens out from its
   centre and the plus turns; pressed, it gives a little. */
.memory .add {
  position: relative; isolation: isolate; gap: 14px; width: max(calc((100cqw - 4px) / 3), min(340px, 100cqw - 40px)); min-height: clamp(74px, 5.8cqw, 88px); margin: 0; padding: 0 28px;
  border: 0; background: #262626; color: #FFF; font-size: clamp(14px, 1.1cqw, 16px); font-weight: 500; letter-spacing: 0.24em;
  transition: color 0.18s var(--fade), transform 0.6s var(--arrive); /* the label turns black ahead of the white reaching it */
}
.memory .add > span:last-child { margin-right: -0.24em; } /* the spacing after the last letter would pull it off centre */
.memory .add::after { content: ""; position: absolute; z-index: -1; inset: 0; border-radius: inherit; background: #FFF; clip-path: inset(0 50% round 999px); transition: clip-path 0.7s var(--arrive); }
.add-mark { display: grid; place-items: center; transition: transform 0.7s var(--arrive); }
.add-mark svg { display: block; width: 1.4em; height: 1.4em; }
@media (hover: hover) {
  .memory .add:hover { background: #262626; color: #000; transform: translateY(-2px); }
  .memory .add:hover::after { clip-path: inset(0 0 round 999px); }
  .memory .add:hover .add-mark { transform: rotate(90deg); }
}
.memory .add:active { background: #262626; color: #000; transform: scale(0.985); transition-duration: 0.15s; }
.memory .add:active::after { clip-path: inset(0 0 round 999px); transition-duration: 0.15s; }
.memory .add:focus-visible { outline-offset: 5px; }
/* The words drift in the first time it comes into view, like the fund's, and the light crosses the title. wall.js adds
   .await and .in, so without scripts it simply stands there. */
.memory.await:not(.in) .memory-in > * { opacity: 0; }
.memory.in .memory-title { animation: words-in 1.6s var(--arrive) both, sweep 3.2s var(--fade) 0.8s both, title-glow 6s var(--fade) 2.4s infinite; }
.memory.in .add { animation: words-in 1.6s var(--arrive) 0.15s backwards; }

/* No photos yet: the section fills the screen below the GoFundMe page. */
.empty .memory { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: calc(100vh - var(--foot-h)); min-height: calc(100svh - var(--foot-h)); padding-block: 30px; }
.empty .memory::before { display: none; }
@media (max-height: 700px) { .empty .memory .add { margin-top: 16px; } }
@media (max-height: 520px) { .empty .memory-title { display: none; } .empty .memory .add { margin-top: 0; } }

/* ---------- Sheets (Share a memory, Request a removal, More information): from the bottom on phones, a small box on
   larger screens. ---------- */
.sheet { width: 100%; max-width: 440px; margin: auto; padding: 0; border: 1px solid var(--glass-line); border-radius: 20px; background: #0A0A0A; color: #FFF; font-size: 16px; }
/* Behind a sheet the page goes soft and quiet, like the glass over the wall in the intro. */
.sheet::backdrop { background: rgba(0, 0, 0, 0.66); -webkit-backdrop-filter: blur(12px) saturate(0.7); backdrop-filter: blur(12px) saturate(0.7); }
.sheet[open] { animation: sheet-in 0.6s var(--arrive) both; }
.sheet[open]::backdrop { animation: rise 0.6s var(--fade) both; }
@keyframes sheet-in { from { opacity: 0; transform: translateY(16px); } }
@media (max-width: 599px) {
  .sheet { max-width: none; margin: auto 0 0; border-width: 1px 0 0; border-radius: 22px 22px 0 0; }
}
.sheet { max-height: min(92dvh, 820px); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.24) transparent; }
.sheet-body { position: relative; padding: 24px 22px calc(24px + env(safe-area-inset-bottom)); }
.sheet-body.drop { outline: 2px dashed rgba(255, 255, 255, 0.4); outline-offset: -10px; }
/* The title and × stay at the top, and Upload stays at the bottom, however long the sheet gets. */
.sheet-head { position: sticky; top: 0; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: -24px -22px 6px; padding: 12px 10px 4px 22px; background: #0A0A0A; }
.sheet h2 { margin: 0; font: 400 13.5px / 1.4 "Tenor Sans", var(--sans); letter-spacing: 0.3em; text-transform: uppercase; }
.sheet-close { flex: none; display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 0; background: none; color: rgba(255, 255, 255, 0.86); cursor: pointer; transition: color 0.35s var(--fade); }
.sheet-close:hover { color: #FFF; }
/* The short notes at the top of a sheet. */
.points { display: grid; gap: 7px; margin: 6px 0 20px; padding: 0; list-style: none; }
.points li { position: relative; padding-left: 16px; font-size: 14.5px; font-weight: 300; line-height: 1.45; color: rgba(255, 255, 255, 0.74); }
.points li::before { content: ""; position: absolute; left: 2px; top: 0.62em; width: 4px; height: 4px; border-radius: 50%; background: rgba(255, 255, 255, 0.55); }
/* The rows in More information, and the folding "Accessibility" and "Trouble uploading?". */
.more-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; width: 100%; padding: 15px 0; border: 0; border-top: 1px solid rgba(255, 255, 255, 0.1); background: none; color: #FFF; font: 400 16px / 1.4 var(--sans); text-align: left; text-decoration: none; cursor: pointer; }
.more-row > svg { flex: none; color: rgba(255, 255, 255, 0.55); transition: transform 0.35s var(--fade); }
.more-row:hover > svg { transform: translateX(3px); }
/* Each row: its name, and one quiet line saying what is there. GoFundMe's is their own green wordmark, as on their page. */
.row-text { flex: 1; min-width: 0; display: grid; justify-items: start; gap: 2px; font-size: 16px; line-height: 1.35; }
.row-text small { font-size: 13.5px; font-weight: 300; line-height: 1.4; color: rgba(255, 255, 255, 0.55); }
.row-logo { width: 74px; height: 25px; }
.more { border-top: 1px solid rgba(255, 255, 255, 0.1); border-bottom: 1px solid rgba(255, 255, 255, 0.1); }
.more summary { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 15px 0; font-size: 16px; list-style: none; cursor: pointer; }
.more summary::-webkit-details-marker { display: none; }
.more summary::after { content: "+"; flex: none; width: 18px; font-size: 19px; font-weight: 300; line-height: 1; text-align: center; color: var(--soft); }
.more[open] summary::after { content: "\2212"; }
.more-body { padding-bottom: 6px; }
.more-body p { font-size: 15px; font-weight: 300; line-height: 1.5; color: rgba(255, 255, 255, 0.74); }
/* Accessibility: short notes, each under the small spaced label the fund's page uses. */
.access-help { display: grid; gap: 16px; padding: 4px 0 20px; }
.sheet .access-help p { margin: 0; font-size: 14.5px; line-height: 1.6; color: rgba(255, 255, 255, 0.74); }
.access-help strong { display: block; margin-bottom: 3px; font-size: 11px; font-weight: 400; line-height: 1.6; letter-spacing: 0.16em; text-transform: uppercase; color: rgba(255, 255, 255, 0.52); }
#more { scroll-padding-top: 68px; }
.help { margin-top: 22px; border-bottom: 0; }
.help summary { font-size: 14.5px; color: rgba(255, 255, 255, 0.74); }
.help-mail { color: #FFF; overflow-wrap: anywhere; text-underline-offset: 0.15em; }
.help-copy { margin: 0 0 6px; font-size: 12px; letter-spacing: 0.18em; text-transform: uppercase; color: rgba(255, 255, 255, 0.62); transition: color 0.35s var(--fade); }
.help-copy:hover { color: #FFF; }
.send-bar { position: sticky; bottom: 0; z-index: 2; margin: 0 -22px; padding: 10px 22px calc(12px + env(safe-area-inset-bottom)); border-top: 1px solid rgba(255, 255, 255, 0.08); background: #0A0A0A; }
.send-fine { margin: 10px 0 0 !important; font-size: 12.5px; font-weight: 300; text-align: center; color: rgba(255, 255, 255, 0.45); }
.sheet p { margin: 0 0 12px; }
.note { text-wrap: pretty; font-size: 15px; font-weight: 300; line-height: 1.5; color: rgba(255, 255, 255, 0.74); }
.button {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 52px; padding: 0 24px; border: 1px solid #FFF; border-radius: 999px;
  background: #FFF; color: #000; font: 500 12.5px / 1 var(--sans); letter-spacing: 0.22em; text-transform: uppercase; text-decoration: none; white-space: nowrap; cursor: pointer;
  transition: background-color 0.35s var(--fade), color 0.35s var(--fade), border-color 0.35s var(--fade);
}
.button.quiet { background: transparent; color: #FFF; border-color: rgba(255, 255, 255, 0.35); }
.button.quiet:hover { border-color: rgba(255, 255, 255, 0.7); }
.button:disabled { opacity: 0.4; cursor: default; }
.button.pick { width: 100%; margin: 6px 0 14px; }
.button.send { width: 100%; margin-top: 8px; }
.send-done { display: grid; gap: 10px; margin-top: 4px; }
.send-done .button { width: 100%; margin: 0; }

/* What they chose: small squares, each with its own × and progress line, and one more square to add more. */
.picked { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin: 0 0 10px; padding: 0; list-style: none; }
.picked:empty { display: none; }
.picked li { position: relative; }
.pthumb { position: relative; aspect-ratio: 1; overflow: hidden; border-radius: 8px; background: #1A1A1A; }
.pthumb img, .pthumb video { display: block; width: 100%; height: 100%; object-fit: cover; }
.pthumb[data-label]::after { content: attr(data-label); position: absolute; inset: 0; display: grid; place-items: center; font-size: 12px; color: var(--soft); }
.add-tile { display: grid; place-items: center; width: 100%; aspect-ratio: 1; padding: 0; border: 1px dashed rgba(255, 255, 255, 0.3); border-radius: 8px; background: none; color: rgba(255, 255, 255, 0.7); cursor: pointer; transition: border-color 0.35s var(--fade), color 0.35s var(--fade); }
.add-tile:hover { border-color: rgba(255, 255, 255, 0.65); color: #FFF; }
.pbar { position: absolute; left: 6px; right: 6px; bottom: 6px; height: 3px; width: 0; max-width: calc(100% - 12px); border-radius: 2px; background: #FFF; opacity: 0; }
.sending .pbar, .sent .pbar { opacity: 1; }
/* Sent: a small tick, drawn rather than typed so it matches the other icons. */
.sent .pthumb::before { content: ""; position: absolute; z-index: 1; top: 7px; left: 8px; width: 5px; height: 10px; border: solid #FFF; border-width: 0 1.6px 1.6px 0; transform: rotate(45deg); filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.8)); }
.failed .pthumb, .bad .pthumb { outline: 2px solid #F2A69B; outline-offset: -2px; }
.premove { position: absolute; top: -6px; right: -6px; display: grid; place-items: center; width: 26px; height: 26px; padding: 0; border: 0; border-radius: 50%; background: rgba(0, 0, 0, 0.8); cursor: pointer; }
.premove::after { content: ""; position: absolute; inset: -9px; } /* 44 px to tap */
.sending .premove, .sent .premove { display: none; }
.pwhy { margin: 4px 0 0; font-size: 11px; line-height: 1.3; color: #F2A69B; }
.pwhy:empty { display: none; }

.fields { display: grid; gap: 12px; margin-top: 6px; }
.field { display: grid; gap: 6px; font-size: 14px; }
.field .opt { color: rgba(255, 255, 255, 0.5); }
.field input, .field textarea {
  width: 100%; padding: 12px 14px; border: 1px solid rgba(255, 255, 255, 0.22); border-radius: 12px;
  background: #121212; color: #FFF; font: inherit; font-size: 16px; /* 16 px: no zoom on iPhone */
  transition: border-color 0.35s var(--fade);
}
.field textarea { resize: vertical; min-height: 88px; }
.fields .field textarea { min-height: 64px; }
.field-hint { margin: -4px 0 0 !important; font-size: 13px; font-weight: 300; color: rgba(255, 255, 255, 0.55); }
.field input:focus, .field textarea:focus { border-color: rgba(255, 255, 255, 0.6); }
.send-status { margin: 12px 0 0; font-size: 15px; font-weight: 300; line-height: 1.5; }
.send-status:empty { display: none; }
.send-status a { color: #FFF; word-break: break-all; }

/* ---------- Viewer: the whole photo or video, nothing cropped. ---------- */
.viewer { width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0; background: #000; color: #FFF; }
.viewer::backdrop { background: #000; }
.viewer[open] { animation: rise 0.45s var(--fade) both; }
.v-top, .v-bottom { position: absolute; left: 0; right: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
/* Close on the left, ⋯ beside the GoFundMe icon on the right. */
.v-top { top: 0; justify-content: flex-start; height: calc(64px + env(safe-area-inset-top)); padding: env(safe-area-inset-top) calc(124px + env(safe-area-inset-right)) 0 max(6px, env(safe-area-inset-left)); }
.v-bottom { bottom: 0; min-height: calc(76px + env(safe-area-inset-bottom)); padding: 8px max(8px, env(safe-area-inset-right)) calc(8px + env(safe-area-inset-bottom)) max(8px, env(safe-area-inset-left)); }
/* Previous and next at the sides of the bottom row, who shared it and their note between them. */
.v-mid { flex: 1; min-width: 0; display: grid; justify-items: center; gap: 2px; }
.v-media { position: absolute; top: calc(64px + env(safe-area-inset-top)); bottom: calc(76px + env(safe-area-inset-bottom)); left: 0; right: 0; display: flex; align-items: center; justify-content: center; touch-action: pinch-zoom; }
.v-media.has-video { touch-action: auto; }
.v-media img, .v-media video { display: block; max-width: 100%; max-height: 100%; }
.v-media img { animation: rise 0.4s var(--fade) both; }
.v-fail { display: grid; justify-items: center; gap: 14px; padding: 24px; text-align: center; font-weight: 300; color: var(--soft); }
.v-fail .add { margin: 0; color: #000; }
.v-fail .add:hover { color: #FFF; }
/* Under the photo: the name of whoever shared it, then their note. */
.v-from { margin: 0; font: 400 11.5px / 1.6 var(--sans); letter-spacing: 0.2em; text-transform: uppercase; color: rgba(255, 255, 255, 0.62); text-align: center; }
.v-caption { max-width: 36em; max-height: 4.6em; margin: 0; overflow-y: auto; font: 300 15.5px / 1.5 var(--sans); text-align: center; text-wrap: pretty; }
.v-from:empty, .v-caption:empty { display: none; }
.v-dots { margin-left: auto; }
.v-btn { flex: none; display: grid; place-items: center; width: 44px; height: 44px; padding: 0; border: 0; background: none; color: rgba(255, 255, 255, 0.86); cursor: pointer; transition: color 0.35s var(--fade), opacity 0.35s var(--fade); }
.v-btn:hover { color: #FFF; }
.v-btn:disabled { opacity: 0.22; cursor: default; }

/* Let the fundraiser grow on short screens or when text is enlarged. */
@media (max-width: 599px) and (max-aspect-ratio: 1 / 1) {
  .fund-page {
    height: auto; min-height: 100svh;
    grid-template-rows: minmax(156px, 1fr) auto auto;
  }
  .fp-photo img { position: absolute; inset: 0; }
  .fp-text {
    margin-top: -38px;
    padding-inline: max(20px, env(safe-area-inset-left)) max(20px, env(safe-area-inset-right));
  }
  .fp-title { font-size: clamp(26px, 8.2vw, 44px); }
}
@media (min-aspect-ratio: 1 / 1) and (max-height: 560px) {
  .fund-page { height: auto; min-height: max(340px, 100svh); grid-template-rows: minmax(min-content, 1fr) auto; }
  .fp-photo img { position: absolute; inset: 0; }
  .fp-text { padding-right: max(24px, env(safe-area-inset-right)); }
}

/* Reserve real space for captions and controls so neither can cover the media. */
.viewer { height: 100dvh; }
.viewer[open] { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; }
.v-top, .v-bottom, .v-media { position: relative; inset: auto; min-width: 0; }
.v-media {
  min-height: 0;
  margin-inline: env(safe-area-inset-left) env(safe-area-inset-right);
}
.v-from, .v-caption { overflow-wrap: anywhere; }
.v-caption { max-height: min(4.6em, 22dvh); }
.sheet { scroll-padding-top: 72px; scroll-padding-bottom: 88px; }

@media (max-width: 599px) {
  .memory { aspect-ratio: auto; min-height: 0; }
  .memory-in { min-width: 0; padding-block: clamp(40px, 12vw, 64px); gap: clamp(22px, 7vw, 32px); }
  .memory-title { max-width: 100%; white-space: normal; font-size: clamp(26px, 8cqw, 44px); text-wrap: balance; }
  .memory .add { width: min(100%, 340px); min-height: 56px; gap: 10px; padding-inline: 16px; }
}

html.intro-locked { overflow: hidden; overscroll-behavior: none; }
html.intro-locked body { position: fixed; inset: 0; width: 100%; overflow: hidden; }
html.intro-locked .intro, html.intro-locked .fund-page { touch-action: pinch-zoom; }
