/* Gallery's layout: mockup B, "Mosaic" (plan D13), as structure.
 *
 * Every colour, radius, shadow, space and type size here is a token: Kit's
 * (css/kit/*-tokens.css) or one of Gallery's own, which retro.css and
 * clean.css define per theme and scheme (--glass, --glass-strong,
 * --glass-line, --glass-blur, --backdrop-dim, --kind-location,
 * --kind-species, --kind-subject, --kind-month; docs/LOOK.md has their values). No colour
 * literal, no duration, no radius in px, no backdrop-filter, transition or
 * animation: the frosted surfaces, the blur, the frames and the motion are
 * the theme sheets'. The lengths that are not tokens are the layout's own
 * measures in rem: the frame's width (94rem), the sidebar's (15.5rem) and
 * the breakpoints, 48rem (the sidebar moves beside the content), 56.25rem
 * (the related row's third column), 75rem (the grid's third column and the
 * photo page's two columns) and the short window, max-height 40rem (a phone
 * turned sideways: docs/LOOK.md, "The photo on its page"), and the masonry
 * grid's row arithmetic. The `sizes` attributes in
 * build/templates/ say the same numbers; change both.
 *
 * Text that sits straight on the backdrop (the page headings, the legend,
 * the related photos' title) is --text, never --text-muted: the veil is set
 * so --text clears 4.5:1 over the brightest and the darkest photo, and the
 * muted greys would need a veil that hides the photo (docs/LOOK.md,
 * "Contrast"). docs/BUILD.md lists every class. */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
}

body {
  margin: 0;
  min-height: 100dvh;
  font-size: var(--text-base);
  line-height: 1.5;
  overflow-wrap: break-word;
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: var(--link);
}

button {
  font: inherit;
}

:focus-visible {
  outline: 2px solid transparent;
  outline-offset: 2px;
  box-shadow: var(--ring);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Skip links: out of sight until focused. */
.skip {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  z-index: 40;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  color: var(--text);
  transform: translateY(-300%);
}

.skip:focus {
  transform: none;
}

/* The backdrop: the page photo's 32px placeholder, stretched over the
 * window behind everything; the theme sheets blur it (CSS filter: it is one
 * small image, never the full photo). Two layers for the crossfade
 * (docs/LAYOUT.md, "Phase B's names"): with no data-fade on either, the
 * first shows and the second is transparent; the script marks the layer it
 * fades to "in" and the other "out". Scaled past the window's edges so the
 * blur's soft rim stays out of sight. */
.backdrop {
  position: fixed;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  transform: scale(1.2);
}

.backdrop[data-fade="in"] {
  opacity: 1;
}

.backdrop + .backdrop:not([data-fade]),
.backdrop[data-fade="out"],
.backdrop:not([src]) {
  opacity: 0;
}

/* The veil over the backdrop, under everything else: --backdrop-dim is set
 * per scheme so --text over it clears 4.5:1 whatever the photo. Retro adds
 * its starfield to the same layer. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--backdrop-dim);
  pointer-events: none;
}

/* The frame: the top bar, then the two columns. */
.frame {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  max-width: 94rem;
  margin-inline: auto;
  padding: var(--space-3);
}

/* Above the columns, so the appearance popover opens over the page. */
.topbar {
  position: relative;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: calc(var(--control-h) + var(--space-3));
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-4);
  border-radius: var(--radius-lg);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--control-h);
  margin-inline-end: auto;
  color: var(--text);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  letter-spacing: -0.01em;
  text-decoration: none;
  white-space: nowrap;
}

.brand-mark {
  width: 1.125rem;
  height: 1.125rem;
  border-radius: var(--radius-full);
  background: conic-gradient(from 210deg, var(--kind-species), var(--kind-location), var(--kind-subject), var(--kind-species));
}

/* The top bar's two buttons: Browse (the drawer's, below 48rem) and the
 * appearance menu's. */
.browse-link,
.appearance-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-width: var(--control-h);
  min-height: var(--control-h);
  padding-inline: var(--space-3);
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-full);
  background: var(--surface-alt);
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  text-decoration: none;
  cursor: pointer;
}

/* Open (G5 sets aria-expanded on both buttons): the filled accent, as the
 * sidebar's current entry, so the ☰ reads as pressed while the drawer is
 * open and the appearance button while its panel is. */
.browse-link[aria-expanded="true"],
.appearance-toggle[aria-expanded="true"] {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--accent-text);
}

.browse-glyph {
  font-size: var(--text-lg);
  line-height: 1;
}

#appearance {
  position: relative;
  display: flex;
}

#appearance:empty {
  display: none;
}

/* The appearance popover (Kit's mountAppearance control): a raised surface,
 * not glass, so it never makes a fifth frosted surface. */
.appearance-panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: 30;
  width: max-content;
  max-width: calc(100vw - 2 * var(--space-3));
  padding: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface-raised);
  color: var(--text);
  box-shadow: var(--shadow-3);
}

/* Below 48rem the sidebar follows the content (the top bar's Browse link
 * jumps to it, or opens it as a drawer); from 48rem it sits on the left. */
.columns {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-5);
  align-items: start;
}

.content {
  min-width: 0;
  /* The masonry grid's rows are fractions of the content's width (cqi). */
  container-type: inline-size;
}

/* An in-page navigation is fetching the next page. */
body[aria-busy="true"] .content {
  opacity: 0.6;
  cursor: progress;
}

/* The sidebar */
.sidebar {
  padding: var(--space-2);
  border-radius: var(--radius-lg);
}

.side-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.side-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: calc(var(--control-h) - var(--space-1));
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  text-decoration: none;
}

.side-top .side-link {
  font-weight: var(--weight-semibold);
}

/* The count, and Graph view's arrow: both aria-hidden. */
.side-link > [aria-hidden="true"] {
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

.side-link[aria-current="page"],
.side-index[aria-current="page"] {
  background: var(--accent);
  color: var(--accent-text);
}

.side-link[aria-current="page"] > [aria-hidden="true"] {
  color: inherit;
}

.side-heading {
  margin: var(--space-3) 0 0;
  font-size: var(--text-xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.side-index {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: calc(var(--control-h) - var(--space-2));
  padding-inline: var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  text-decoration: none;
}

/* The four kinds' colours (G4's tokens and G13's --kind-month, which G6's
 * graph reads too): the heading dots, the chips, the legend. */
.side-index::before,
.chip::before,
.legend li::before {
  content: "";
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: var(--radius-full);
  background: var(--text-muted);
}

.side-index[data-kind="location"]::before,
.chip[data-kind="location"]::before,
.legend li[data-kind="location"]::before {
  background: var(--kind-location);
}

.side-index[data-kind="species"]::before,
.chip[data-kind="species"]::before,
.legend li[data-kind="species"]::before {
  background: var(--kind-species);
}

.side-index[data-kind="subject"]::before,
.chip[data-kind="subject"]::before,
.legend li[data-kind="subject"]::before {
  background: var(--kind-subject);
}

.side-index[data-kind="month"]::before,
.chip[data-kind="month"]::before,
.legend li[data-kind="month"]::before {
  background: var(--kind-month);
}

/* A page's heading: crumb and h1 on the left, the count or legend right.
 * Straight on the backdrop, so all of it is --text. */
.page-heading {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-2) var(--space-4);
  margin: var(--space-1) var(--space-1) var(--space-4);
  color: var(--text);
}

.page-title {
  min-width: 0;
}

.crumb {
  margin: 0;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.crumb a {
  color: inherit;
}

.page-heading h1 {
  margin: 0;
  font-size: clamp(var(--text-2xl), 4.4vw, calc(var(--text-2xl) * 1.85));
  line-height: 1.05;
  letter-spacing: -0.03em;
}

.page-sub {
  margin: var(--space-1) 0 0;
}

.page-count {
  margin: 0;
  font-weight: var(--weight-semibold);
}

/* More like this (FEAT-gallery-12): under a month or subject page's
 * heading, a label and its chips on one line that wraps, straight on the
 * backdrop as the heading is. */
.more-like {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0 var(--space-1) var(--space-4);
  color: var(--text);
}

.more-like-label {
  margin: 0;
  font-weight: var(--weight-semibold);
}

.more-like .chips {
  margin: 0;
}

/* The masonry grid, row first and with no script: a CSS grid of small
 * rows, 40 to a column's width, in which each card spans its picture's
 * rows plus two for the gap under it. build/templates/card.mjs spanOf()
 * works the span out from the photo's aspect ratio and writes it as a
 * class span-NN from the set below (NN from 12 to 102). Lengths are
 * fractions of the content's width, 100cqi: two columns and a column gap
 * of two rows make 82 rows to the width (a column is 100cqi / 2.05), three
 * make 124 (100cqi / 3.1). The span never depends on the viewport, so the
 * reading order, the tab order and the visual order are all newest first,
 * left to right.
 *
 * A card fills its area less the gap. Its picture is fitted inside with
 * object-fit: contain, so a span rounded to a whole row shows as a sliver
 * of the card's own background, at most half a row (1/80 of the column's
 * width) above and below a portrait picture, never a crop. */
.masonry {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: calc(100cqi / 82);
  column-gap: calc(100cqi / 41);
  margin: 0;
  padding: 0;
  list-style: none;
}

.masonry > .card {
  margin-bottom: calc(100cqi / 41);
}

.masonry > .span-12 { grid-row-end: span 12; --wide-14: 26; --wide-12: 24; --wide-10: 22; --wide-8: 20; --wide-6: 18; --wide-5: 17; }
.masonry > .span-13 { grid-row-end: span 13; --wide-14: 27; --wide-12: 25; --wide-10: 23; --wide-8: 21; --wide-6: 19; --wide-5: 18; }
.masonry > .span-14 { grid-row-end: span 14; --wide-14: 28; --wide-12: 26; --wide-10: 24; --wide-8: 22; --wide-6: 20; --wide-5: 19; }
.masonry > .span-15 { grid-row-end: span 15; --wide-14: 29; --wide-12: 27; --wide-10: 25; --wide-8: 23; --wide-6: 21; --wide-5: 20; }
.masonry > .span-16 { grid-row-end: span 16; --wide-14: 30; --wide-12: 28; --wide-10: 26; --wide-8: 24; --wide-6: 22; --wide-5: 21; }
.masonry > .span-17 { grid-row-end: span 17; --wide-14: 31; --wide-12: 29; --wide-10: 27; --wide-8: 25; --wide-6: 23; --wide-5: 22; }
.masonry > .span-18 { grid-row-end: span 18; --wide-14: 32; --wide-12: 30; --wide-10: 28; --wide-8: 26; --wide-6: 24; --wide-5: 23; }
.masonry > .span-19 { grid-row-end: span 19; --wide-14: 33; --wide-12: 31; --wide-10: 29; --wide-8: 27; --wide-6: 25; --wide-5: 24; }
.masonry > .span-20 { grid-row-end: span 20; --wide-14: 34; --wide-12: 32; --wide-10: 30; --wide-8: 28; --wide-6: 26; --wide-5: 25; }
.masonry > .span-21 { grid-row-end: span 21; --wide-14: 35; --wide-12: 33; --wide-10: 31; --wide-8: 29; --wide-6: 27; --wide-5: 26; }
.masonry > .span-22 { grid-row-end: span 22; --wide-14: 36; --wide-12: 34; --wide-10: 32; --wide-8: 30; --wide-6: 28; --wide-5: 27; }
.masonry > .span-23 { grid-row-end: span 23; --wide-14: 37; --wide-12: 35; --wide-10: 33; --wide-8: 31; --wide-6: 29; --wide-5: 28; }
.masonry > .span-24 { grid-row-end: span 24; --wide-14: 38; --wide-12: 36; --wide-10: 34; --wide-8: 32; --wide-6: 30; --wide-5: 29; }
.masonry > .span-25 { grid-row-end: span 25; --wide-14: 39; --wide-12: 37; --wide-10: 35; --wide-8: 33; --wide-6: 31; --wide-5: 30; }
.masonry > .span-26 { grid-row-end: span 26; --wide-14: 40; --wide-12: 38; --wide-10: 36; --wide-8: 34; --wide-6: 32; --wide-5: 31; }
.masonry > .span-27 { grid-row-end: span 27; --wide-14: 41; --wide-12: 39; --wide-10: 37; --wide-8: 35; --wide-6: 33; --wide-5: 32; }
.masonry > .span-28 { grid-row-end: span 28; --wide-14: 42; --wide-12: 40; --wide-10: 38; --wide-8: 36; --wide-6: 34; --wide-5: 33; }
.masonry > .span-29 { grid-row-end: span 29; --wide-14: 43; --wide-12: 41; --wide-10: 39; --wide-8: 37; --wide-6: 35; --wide-5: 34; }
.masonry > .span-30 { grid-row-end: span 30; --wide-14: 44; --wide-12: 42; --wide-10: 40; --wide-8: 38; --wide-6: 36; --wide-5: 35; }
.masonry > .span-31 { grid-row-end: span 31; --wide-14: 45; --wide-12: 43; --wide-10: 41; --wide-8: 39; --wide-6: 37; --wide-5: 36; }
.masonry > .span-32 { grid-row-end: span 32; --wide-14: 46; --wide-12: 44; --wide-10: 42; --wide-8: 40; --wide-6: 38; --wide-5: 37; }
.masonry > .span-33 { grid-row-end: span 33; --wide-14: 47; --wide-12: 45; --wide-10: 43; --wide-8: 41; --wide-6: 39; --wide-5: 38; }
.masonry > .span-34 { grid-row-end: span 34; --wide-14: 48; --wide-12: 46; --wide-10: 44; --wide-8: 42; --wide-6: 40; --wide-5: 39; }
.masonry > .span-35 { grid-row-end: span 35; --wide-14: 49; --wide-12: 47; --wide-10: 45; --wide-8: 43; --wide-6: 41; --wide-5: 40; }
.masonry > .span-36 { grid-row-end: span 36; --wide-14: 50; --wide-12: 48; --wide-10: 46; --wide-8: 44; --wide-6: 42; --wide-5: 41; }
.masonry > .span-37 { grid-row-end: span 37; --wide-14: 51; --wide-12: 49; --wide-10: 47; --wide-8: 45; --wide-6: 43; --wide-5: 42; }
.masonry > .span-38 { grid-row-end: span 38; --wide-14: 52; --wide-12: 50; --wide-10: 48; --wide-8: 46; --wide-6: 44; --wide-5: 43; }
.masonry > .span-39 { grid-row-end: span 39; --wide-14: 53; --wide-12: 51; --wide-10: 49; --wide-8: 47; --wide-6: 45; --wide-5: 44; }
.masonry > .span-40 { grid-row-end: span 40; --wide-14: 54; --wide-12: 52; --wide-10: 50; --wide-8: 48; --wide-6: 46; --wide-5: 45; }
.masonry > .span-41 { grid-row-end: span 41; --wide-14: 55; --wide-12: 53; --wide-10: 51; --wide-8: 49; --wide-6: 47; --wide-5: 46; }
.masonry > .span-42 { grid-row-end: span 42; --wide-14: 56; --wide-12: 54; --wide-10: 52; --wide-8: 50; --wide-6: 48; --wide-5: 47; }
.masonry > .span-43 { grid-row-end: span 43; }
.masonry > .span-44 { grid-row-end: span 44; }
.masonry > .span-45 { grid-row-end: span 45; }
.masonry > .span-46 { grid-row-end: span 46; }
.masonry > .span-47 { grid-row-end: span 47; }
.masonry > .span-48 { grid-row-end: span 48; }
.masonry > .span-49 { grid-row-end: span 49; }
.masonry > .span-50 { grid-row-end: span 50; }
.masonry > .span-51 { grid-row-end: span 51; }
.masonry > .span-52 { grid-row-end: span 52; }
.masonry > .span-53 { grid-row-end: span 53; }
.masonry > .span-54 { grid-row-end: span 54; }
.masonry > .span-55 { grid-row-end: span 55; }
.masonry > .span-56 { grid-row-end: span 56; }
.masonry > .span-57 { grid-row-end: span 57; }
.masonry > .span-58 { grid-row-end: span 58; }
.masonry > .span-59 { grid-row-end: span 59; }
.masonry > .span-60 { grid-row-end: span 60; }
.masonry > .span-61 { grid-row-end: span 61; }
.masonry > .span-62 { grid-row-end: span 62; }
.masonry > .span-63 { grid-row-end: span 63; }
.masonry > .span-64 { grid-row-end: span 64; }
.masonry > .span-65 { grid-row-end: span 65; }
.masonry > .span-66 { grid-row-end: span 66; }
.masonry > .span-67 { grid-row-end: span 67; }
.masonry > .span-68 { grid-row-end: span 68; }
.masonry > .span-69 { grid-row-end: span 69; }
.masonry > .span-70 { grid-row-end: span 70; }
.masonry > .span-71 { grid-row-end: span 71; }
.masonry > .span-72 { grid-row-end: span 72; }
.masonry > .span-73 { grid-row-end: span 73; }
.masonry > .span-74 { grid-row-end: span 74; }
.masonry > .span-75 { grid-row-end: span 75; }
.masonry > .span-76 { grid-row-end: span 76; }
.masonry > .span-77 { grid-row-end: span 77; }
.masonry > .span-78 { grid-row-end: span 78; }
.masonry > .span-79 { grid-row-end: span 79; }
.masonry > .span-80 { grid-row-end: span 80; }
.masonry > .span-81 { grid-row-end: span 81; }
.masonry > .span-82 { grid-row-end: span 82; }
.masonry > .span-83 { grid-row-end: span 83; }
.masonry > .span-84 { grid-row-end: span 84; }
.masonry > .span-85 { grid-row-end: span 85; }
.masonry > .span-86 { grid-row-end: span 86; }
.masonry > .span-87 { grid-row-end: span 87; }
.masonry > .span-88 { grid-row-end: span 88; }
.masonry > .span-89 { grid-row-end: span 89; }
.masonry > .span-90 { grid-row-end: span 90; }
.masonry > .span-91 { grid-row-end: span 91; }
.masonry > .span-92 { grid-row-end: span 92; }
.masonry > .span-93 { grid-row-end: span 93; }
.masonry > .span-94 { grid-row-end: span 94; }
.masonry > .span-95 { grid-row-end: span 95; }
.masonry > .span-96 { grid-row-end: span 96; }
.masonry > .span-97 { grid-row-end: span 97; }
.masonry > .span-98 { grid-row-end: span 98; }
.masonry > .span-99 { grid-row-end: span 99; }
.masonry > .span-100 { grid-row-end: span 100; }
.masonry > .span-101 { grid-row-end: span 101; }
.masonry > .span-102 { grid-row-end: span 102; }

/* A landscape card (card--wide) puts its strip below the picture, so it
 * spans the strip's rows too. The strip is at most 3.375rem tall (two
 * 24 px links, the 2 px gap between them and the retro border, UX-gallery-4)
 * while a row is 1/40 of the column's width, so the rows it takes depend
 * on the column: 14 below a 180px column, then 12, 10, 8, 6 and 5 from
 * 180, 216, 270, 360 and 432 px. Each span-NN above carries NN plus each
 * of those as --wide-S, and the rules below pick one by the content's
 * width. The picture is rounded up to whole rows (spanOf), so the strip
 * takes any slack: it is up to about 1.5rem taller than its two lines,
 * never shorter while the column is over 154px (a 340px window); at 320
 * retro's picture is letterboxed by about 1.5px, which nothing shows
 * (docs/BUILD.md, review RGU1-5). */
.masonry > .card--wide {
  grid-row-end: span var(--wide-14);
}

@container (min-width: 23.06rem) {
  .masonry > .card--wide {
    grid-row-end: span var(--wide-12);
  }
}

@container (min-width: 27.68rem) {
  .masonry > .card--wide {
    grid-row-end: span var(--wide-10);
  }
}

@container (min-width: 34.59rem) {
  .masonry > .card--wide {
    grid-row-end: span var(--wide-8);
  }
}

@container (min-width: 46.13rem) {
  .masonry > .card--wide {
    grid-row-end: span var(--wide-6);
  }
}

@container (min-width: 55.35rem) {
  .masonry > .card--wide {
    grid-row-end: span var(--wide-5);
  }
}

@media (min-width: 75rem) {
  .masonry {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    grid-auto-rows: calc(100cqi / 124);
    column-gap: calc(100cqi / 62);
  }

  .masonry > .card {
    margin-bottom: calc(100cqi / 62);
  }

  .masonry > .card--wide {
    grid-row-end: span var(--wide-8);
  }

  @container (min-width: 69.75rem) {
    .masonry > .card--wide {
      grid-row-end: span var(--wide-6);
    }
  }

  @container (min-width: 83.7rem) {
    .masonry > .card--wide {
      grid-row-end: span var(--wide-5);
    }
  }
}


/* Cards below the fold skip their rendering until they come near the
 * window (plan section 7). A card's box is its grid area, whose rows are
 * fixed lengths, so skipping its contents moves nothing. The first six are
 * the first two rows at any width. */
.masonry > .card:nth-child(n + 7) {
  content-visibility: auto;
  contain-intrinsic-size: auto 16rem;
}

/* A card: the picture, never cropped, and its caption strip. In the grid
 * the card has its area's height and the picture takes what the strip
 * leaves; in the related row it has the picture's own height. */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: var(--radius-lg);
  background: var(--glass-line);
  box-shadow: var(--shadow-2);
}

/* The card's frame, drawn over the picture's edge: a hairline in clean, the
 * ridge in retro (the theme sheets). It also covers the sliver a span
 * rounded to whole rows leaves beside a portrait picture (docs/LOOK.md). */
.card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  border-radius: var(--radius-lg);
  pointer-events: none;
}

.card-photo,
.card-link {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
  color: inherit;
  text-decoration: none;
}

.card picture {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}

.card picture img {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  height: auto;
  object-fit: contain;
}

.card-photo:focus-visible,
.card-link:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: -6px;
}

/* The caption strip: over the picture's bottom edge, the name and the
 * place one line each, so it is two lines whatever the names. An opaque
 * surface with the frost layer (the photo's own lqip, scaled and blurred by
 * the theme sheets) over it at low opacity: the strip takes the photo's
 * colour and its text clears 4.5:1 whatever the photo, so both lines are
 * --text. */
.caption {
  position: relative;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  overflow: hidden;
  isolation: isolate;
  background: var(--surface);
  color: var(--text);
  line-height: 1.3;
}

.card:not(.card--wide, .card-hero) .caption {
  position: absolute;
  right: var(--space-2);
  bottom: var(--space-2);
  left: var(--space-2);
  border-radius: var(--radius);
}

.frost {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: bottom;
  pointer-events: none;
}

.caption-name,
.caption-place,
.caption-count {
  overflow: hidden;
  color: var(--text);
  white-space: nowrap;
  text-overflow: ellipsis;
  text-decoration: none;
}

/* Each caption link is a tap target at least 24 px tall (WCAG 2.2's
 * minimum), with a 2 px gap between the two (UX-gallery-4). The cover
 * card's span caption is one link for the whole card and keeps its size. */
.caption {
  gap: 0.125rem;
}

/* A block with a 24 px line, not a flex box, so a long name still ends in
 * an ellipsis (text-overflow needs a block container). */
a.caption-name,
a.caption-place {
  display: block;
  min-height: 1.5rem;
  line-height: 1.5rem;
}

.caption-name {
  font-weight: var(--weight-semibold);
  letter-spacing: -0.01em;
}

.caption-place,
.caption-count {
  font-size: var(--text-xs);
}

/* A landscape card, and the hero: the strip below the picture, covering
 * none of it. */
.card--wide .card-photo,
.card--wide .card-link > picture,
.card-hero .card-photo {
  flex: 0 1 auto;
}

.card--wide .caption,
.card-hero .caption {
  flex: 1 0 auto;
  justify-content: center;
  padding-block: 0;
}

/* The hero (FEAT-gallery-1; docs/BUILD.md, "The hero"): home page 1's
 * featured photo, wide above the grid, at its own aspect ratio and never
 * cropped (contain, as every card), no taller than most of the window. Its
 * caption strip sits below the picture, as a card--wide's does (the rules
 * above), whatever the photo's shape. */
.hero {
  margin-block-end: var(--space-3);
}

.hero > .card-hero {
  margin: 0;
}

/* The picture keeps the photo's own ratio: as wide as the card allows and
 * no taller than the cap, centred, the card's own background beside it. */
.card.card-hero picture {
  align-items: center;
}

.card.card-hero picture img {
  flex: none;
  max-width: 100%;
  height: auto;
  max-height: min(70svh, 40rem);
  object-fit: contain;
}

/* The hero's ratio set (review S1-GF1-2; card.mjs HERO_RATIOS): its
 * <picture> carries hero-r-NNN, the photo's width / height rounded down to
 * 0.05, and the image takes a definite width, min(100%, cap * ratio), so
 * its width and height attributes give it its box before its bytes arrive
 * and the grid below never moves. Rounded down, the box is never taller
 * than the cap. Below 48rem the cap is 60svh. */
.card-hero .hero-r-020 img { width: min(100%, calc(min(70svh, 40rem) * 0.20)); }
.card-hero .hero-r-025 img { width: min(100%, calc(min(70svh, 40rem) * 0.25)); }
.card-hero .hero-r-030 img { width: min(100%, calc(min(70svh, 40rem) * 0.30)); }
.card-hero .hero-r-035 img { width: min(100%, calc(min(70svh, 40rem) * 0.35)); }
.card-hero .hero-r-040 img { width: min(100%, calc(min(70svh, 40rem) * 0.40)); }
.card-hero .hero-r-045 img { width: min(100%, calc(min(70svh, 40rem) * 0.45)); }
.card-hero .hero-r-050 img { width: min(100%, calc(min(70svh, 40rem) * 0.50)); }
.card-hero .hero-r-055 img { width: min(100%, calc(min(70svh, 40rem) * 0.55)); }
.card-hero .hero-r-060 img { width: min(100%, calc(min(70svh, 40rem) * 0.60)); }
.card-hero .hero-r-065 img { width: min(100%, calc(min(70svh, 40rem) * 0.65)); }
.card-hero .hero-r-070 img { width: min(100%, calc(min(70svh, 40rem) * 0.70)); }
.card-hero .hero-r-075 img { width: min(100%, calc(min(70svh, 40rem) * 0.75)); }
.card-hero .hero-r-080 img { width: min(100%, calc(min(70svh, 40rem) * 0.80)); }
.card-hero .hero-r-085 img { width: min(100%, calc(min(70svh, 40rem) * 0.85)); }
.card-hero .hero-r-090 img { width: min(100%, calc(min(70svh, 40rem) * 0.90)); }
.card-hero .hero-r-095 img { width: min(100%, calc(min(70svh, 40rem) * 0.95)); }
.card-hero .hero-r-100 img { width: min(100%, calc(min(70svh, 40rem) * 1.00)); }
.card-hero .hero-r-105 img { width: min(100%, calc(min(70svh, 40rem) * 1.05)); }
.card-hero .hero-r-110 img { width: min(100%, calc(min(70svh, 40rem) * 1.10)); }
.card-hero .hero-r-115 img { width: min(100%, calc(min(70svh, 40rem) * 1.15)); }
.card-hero .hero-r-120 img { width: min(100%, calc(min(70svh, 40rem) * 1.20)); }
.card-hero .hero-r-125 img { width: min(100%, calc(min(70svh, 40rem) * 1.25)); }
.card-hero .hero-r-130 img { width: min(100%, calc(min(70svh, 40rem) * 1.30)); }
.card-hero .hero-r-135 img { width: min(100%, calc(min(70svh, 40rem) * 1.35)); }
.card-hero .hero-r-140 img { width: min(100%, calc(min(70svh, 40rem) * 1.40)); }
.card-hero .hero-r-145 img { width: min(100%, calc(min(70svh, 40rem) * 1.45)); }
.card-hero .hero-r-150 img { width: min(100%, calc(min(70svh, 40rem) * 1.50)); }
.card-hero .hero-r-155 img { width: min(100%, calc(min(70svh, 40rem) * 1.55)); }
.card-hero .hero-r-160 img { width: min(100%, calc(min(70svh, 40rem) * 1.60)); }
.card-hero .hero-r-165 img { width: min(100%, calc(min(70svh, 40rem) * 1.65)); }
.card-hero .hero-r-170 img { width: min(100%, calc(min(70svh, 40rem) * 1.70)); }
.card-hero .hero-r-175 img { width: min(100%, calc(min(70svh, 40rem) * 1.75)); }
.card-hero .hero-r-180 img { width: min(100%, calc(min(70svh, 40rem) * 1.80)); }
.card-hero .hero-r-185 img { width: min(100%, calc(min(70svh, 40rem) * 1.85)); }
.card-hero .hero-r-190 img { width: min(100%, calc(min(70svh, 40rem) * 1.90)); }
.card-hero .hero-r-195 img { width: min(100%, calc(min(70svh, 40rem) * 1.95)); }
.card-hero .hero-r-200 img { width: min(100%, calc(min(70svh, 40rem) * 2.00)); }
.card-hero .hero-r-205 img { width: min(100%, calc(min(70svh, 40rem) * 2.05)); }
.card-hero .hero-r-210 img { width: min(100%, calc(min(70svh, 40rem) * 2.10)); }
.card-hero .hero-r-215 img { width: min(100%, calc(min(70svh, 40rem) * 2.15)); }
.card-hero .hero-r-220 img { width: min(100%, calc(min(70svh, 40rem) * 2.20)); }
.card-hero .hero-r-225 img { width: min(100%, calc(min(70svh, 40rem) * 2.25)); }
.card-hero .hero-r-230 img { width: min(100%, calc(min(70svh, 40rem) * 2.30)); }
.card-hero .hero-r-235 img { width: min(100%, calc(min(70svh, 40rem) * 2.35)); }
.card-hero .hero-r-240 img { width: min(100%, calc(min(70svh, 40rem) * 2.40)); }
.card-hero .hero-r-245 img { width: min(100%, calc(min(70svh, 40rem) * 2.45)); }
.card-hero .hero-r-250 img { width: min(100%, calc(min(70svh, 40rem) * 2.50)); }
.card-hero .hero-r-255 img { width: min(100%, calc(min(70svh, 40rem) * 2.55)); }
.card-hero .hero-r-260 img { width: min(100%, calc(min(70svh, 40rem) * 2.60)); }
.card-hero .hero-r-265 img { width: min(100%, calc(min(70svh, 40rem) * 2.65)); }
.card-hero .hero-r-270 img { width: min(100%, calc(min(70svh, 40rem) * 2.70)); }
.card-hero .hero-r-275 img { width: min(100%, calc(min(70svh, 40rem) * 2.75)); }
.card-hero .hero-r-280 img { width: min(100%, calc(min(70svh, 40rem) * 2.80)); }
.card-hero .hero-r-285 img { width: min(100%, calc(min(70svh, 40rem) * 2.85)); }
.card-hero .hero-r-290 img { width: min(100%, calc(min(70svh, 40rem) * 2.90)); }
.card-hero .hero-r-295 img { width: min(100%, calc(min(70svh, 40rem) * 2.95)); }
.card-hero .hero-r-300 img { width: min(100%, calc(min(70svh, 40rem) * 3.00)); }

@media (max-width: 47.9375rem) {
  .card.card-hero picture img {
    max-height: 60svh;
  }

  .card-hero .hero-r-020 img { width: min(100%, calc(60svh * 0.20)); }
  .card-hero .hero-r-025 img { width: min(100%, calc(60svh * 0.25)); }
  .card-hero .hero-r-030 img { width: min(100%, calc(60svh * 0.30)); }
  .card-hero .hero-r-035 img { width: min(100%, calc(60svh * 0.35)); }
  .card-hero .hero-r-040 img { width: min(100%, calc(60svh * 0.40)); }
  .card-hero .hero-r-045 img { width: min(100%, calc(60svh * 0.45)); }
  .card-hero .hero-r-050 img { width: min(100%, calc(60svh * 0.50)); }
  .card-hero .hero-r-055 img { width: min(100%, calc(60svh * 0.55)); }
  .card-hero .hero-r-060 img { width: min(100%, calc(60svh * 0.60)); }
  .card-hero .hero-r-065 img { width: min(100%, calc(60svh * 0.65)); }
  .card-hero .hero-r-070 img { width: min(100%, calc(60svh * 0.70)); }
  .card-hero .hero-r-075 img { width: min(100%, calc(60svh * 0.75)); }
  .card-hero .hero-r-080 img { width: min(100%, calc(60svh * 0.80)); }
  .card-hero .hero-r-085 img { width: min(100%, calc(60svh * 0.85)); }
  .card-hero .hero-r-090 img { width: min(100%, calc(60svh * 0.90)); }
  .card-hero .hero-r-095 img { width: min(100%, calc(60svh * 0.95)); }
  .card-hero .hero-r-100 img { width: min(100%, calc(60svh * 1.00)); }
  .card-hero .hero-r-105 img { width: min(100%, calc(60svh * 1.05)); }
  .card-hero .hero-r-110 img { width: min(100%, calc(60svh * 1.10)); }
  .card-hero .hero-r-115 img { width: min(100%, calc(60svh * 1.15)); }
  .card-hero .hero-r-120 img { width: min(100%, calc(60svh * 1.20)); }
  .card-hero .hero-r-125 img { width: min(100%, calc(60svh * 1.25)); }
  .card-hero .hero-r-130 img { width: min(100%, calc(60svh * 1.30)); }
  .card-hero .hero-r-135 img { width: min(100%, calc(60svh * 1.35)); }
  .card-hero .hero-r-140 img { width: min(100%, calc(60svh * 1.40)); }
  .card-hero .hero-r-145 img { width: min(100%, calc(60svh * 1.45)); }
  .card-hero .hero-r-150 img { width: min(100%, calc(60svh * 1.50)); }
  .card-hero .hero-r-155 img { width: min(100%, calc(60svh * 1.55)); }
  .card-hero .hero-r-160 img { width: min(100%, calc(60svh * 1.60)); }
  .card-hero .hero-r-165 img { width: min(100%, calc(60svh * 1.65)); }
  .card-hero .hero-r-170 img { width: min(100%, calc(60svh * 1.70)); }
  .card-hero .hero-r-175 img { width: min(100%, calc(60svh * 1.75)); }
  .card-hero .hero-r-180 img { width: min(100%, calc(60svh * 1.80)); }
  .card-hero .hero-r-185 img { width: min(100%, calc(60svh * 1.85)); }
  .card-hero .hero-r-190 img { width: min(100%, calc(60svh * 1.90)); }
  .card-hero .hero-r-195 img { width: min(100%, calc(60svh * 1.95)); }
  .card-hero .hero-r-200 img { width: min(100%, calc(60svh * 2.00)); }
  .card-hero .hero-r-205 img { width: min(100%, calc(60svh * 2.05)); }
  .card-hero .hero-r-210 img { width: min(100%, calc(60svh * 2.10)); }
  .card-hero .hero-r-215 img { width: min(100%, calc(60svh * 2.15)); }
  .card-hero .hero-r-220 img { width: min(100%, calc(60svh * 2.20)); }
  .card-hero .hero-r-225 img { width: min(100%, calc(60svh * 2.25)); }
  .card-hero .hero-r-230 img { width: min(100%, calc(60svh * 2.30)); }
  .card-hero .hero-r-235 img { width: min(100%, calc(60svh * 2.35)); }
  .card-hero .hero-r-240 img { width: min(100%, calc(60svh * 2.40)); }
  .card-hero .hero-r-245 img { width: min(100%, calc(60svh * 2.45)); }
  .card-hero .hero-r-250 img { width: min(100%, calc(60svh * 2.50)); }
  .card-hero .hero-r-255 img { width: min(100%, calc(60svh * 2.55)); }
  .card-hero .hero-r-260 img { width: min(100%, calc(60svh * 2.60)); }
  .card-hero .hero-r-265 img { width: min(100%, calc(60svh * 2.65)); }
  .card-hero .hero-r-270 img { width: min(100%, calc(60svh * 2.70)); }
  .card-hero .hero-r-275 img { width: min(100%, calc(60svh * 2.75)); }
  .card-hero .hero-r-280 img { width: min(100%, calc(60svh * 2.80)); }
  .card-hero .hero-r-285 img { width: min(100%, calc(60svh * 2.85)); }
  .card-hero .hero-r-290 img { width: min(100%, calc(60svh * 2.90)); }
  .card-hero .hero-r-295 img { width: min(100%, calc(60svh * 2.95)); }
  .card-hero .hero-r-300 img { width: min(100%, calc(60svh * 3.00)); }
}

/* The photo page */
.back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--control-h);
  margin-bottom: var(--space-4);
  padding-inline: var(--space-4);
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-full);
  background: var(--surface-alt);
  color: var(--text);
  font-weight: var(--weight-semibold);
  text-decoration: none;
}

/* The photo bar: Back, and at its end the top pager (UX-gallery-1), the
 * same previous and next as the pager below the related row, each a 44 px
 * round button showing only its glyph. */
.photo-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.photo-bar .back {
  margin-bottom: 0;
}

.pager-top {
  display: flex;
  gap: var(--space-2);
  margin-inline-start: auto;
}

.pager-top a {
  display: grid;
  place-items: center;
  min-width: var(--control-h);
  min-height: var(--control-h);
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-full);
  background: var(--surface-alt);
  color: var(--text);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  line-height: 1;
  text-decoration: none;
}

.detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-4);
  align-items: start;
}

.shot {
  margin: 0;
}

/* Never cropped: both sides auto, capped by the column and the window, so
 * the picture keeps its own aspect ratio whichever cap holds. In a tall
 * window the cap leaves room for what is above the photo, so all of it is in
 * view on arrival; in a short one (below, max-height 40rem) it is the window
 * less the frame's padding, so one scroll brings all of it into view
 * (docs/LOOK.md, "The photo on its page"). */
.shot img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: max(18rem, calc(100dvh - var(--wp-site-nav-h, 0px) - 9rem));
  margin-inline: auto;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-3);
}

.rail {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-width: 0;
}

.info {
  padding: var(--space-5);
  border-radius: var(--radius-lg);
}

.connected {
  padding: var(--space-4) var(--space-4) var(--space-3);
  border-radius: var(--radius-lg);
}

.eyebrow {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* The month over the photo's name links to the month's page: the eyebrow's
 * own colour with a dotted underline at rest, so it reads as a link on a
 * phone (no hover there; G13 review nit 1), solid on hover and focus,
 * 24 px tall (WCAG 2.5.8). */
.month-link {
  display: inline-block;
  min-height: 1.5rem;
  line-height: 1.5rem;
  color: inherit;
  text-decoration: underline dotted;
  text-underline-offset: 0.2em;
}

.month-link:hover,
.month-link:focus-visible {
  text-decoration-style: solid;
}

.info .name {
  margin: var(--space-1) 0 0;
  font-size: calc(var(--text-2xl) * 1.05);
  line-height: 1.12;
  letter-spacing: -0.02em;
}

.info .name a {
  color: inherit;
  text-decoration: none;
}

.scientific {
  margin: var(--space-1) 0 0;
  color: var(--text-muted);
}

/* On a species page the scientific name sits on the backdrop. */
.page-heading .scientific {
  color: inherit;
}

.status {
  margin: var(--space-2) 0 0;
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
}

.status[data-status="confirmed"] {
  color: var(--success-soft-text);
}

.status[data-status="suggested"] {
  color: var(--warning-soft-text);
}

/* The ✓ or ?, aria-hidden: the words say it. */
.status > [aria-hidden="true"] {
  margin-inline-end: 0.35em;
}

/* The place block: a ring in the location colour, then the place (a link
 * to its location page: a point of interest's name over its region, or a
 * city's one line) and, for a point of interest, the map link under it
 * (docs/CATALOG.md, "Place kinds"). A solid --surface-alt, so --link holds
 * on it in every look. */
.place {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--space-3);
  align-items: start;
  margin-top: var(--space-4);
  padding: var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  color: var(--text-muted);
  font-size: var(--text-sm);
  line-height: 1.35;
}

.place::before {
  content: "";
  grid-row: 1 / span 2;
  width: 1.125rem;
  height: 1.125rem;
  margin-top: 0.1rem;
  border: 2px solid var(--kind-location);
  border-radius: var(--radius-full);
  box-shadow: inset 0 0 0 3px var(--surface-alt), inset 0 0 0 9px var(--kind-location);
}

.place-link {
  display: flex;
  flex-direction: column;
  grid-column: 2;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}

.place-name {
  color: var(--text);
  font-weight: var(--weight-semibold);
}

/* "Open in Maps" and "Wikipedia" (G14): plain underlined links, 24 px
 * tall at least (WCAG 2.5.8). The map link sits under the place on the
 * photo page and under the region on a point of interest's location page;
 * the Wikipedia link after the scientific name on the photo page and under
 * it on a species page. */
.map-link,
.wiki-link {
  display: inline-block;
  min-height: 1.5rem;
  line-height: 1.5rem;
  font-weight: var(--weight-semibold);
  text-underline-offset: 0.2em;
}

.scientific .wiki-link {
  color: var(--link);
  font-size: var(--text-sm);
}

.place .map-link {
  grid-column: 2;
  justify-self: start;
  margin-top: var(--space-1);
  color: var(--link);
}

/* On the location page the link sits straight on the backdrop, where only
 * --text holds 4.5:1 (docs/LOOK.md, "Contrast"). */
.page-map,
.page-wiki {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
}

.page-map .map-link,
.page-wiki .wiki-link {
  color: inherit;
}

/* The note is optional (Will's own words, when he writes one), so the
 * chips keep their own space above them and nothing leans on a note. */
.note {
  margin: var(--space-4) 0 0;
  line-height: 1.55;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-4) 0 0;
  padding: 0;
  list-style: none;
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 2rem;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  background: var(--surface-alt);
  color: var(--text);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  text-decoration: none;
}

.panel-title {
  margin: 0 0 var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

/* The related photos' title sits on the backdrop. */
.related > .panel-title {
  margin-inline: var(--space-1);
  color: var(--text);
}

/* The graph's mount: G6's canvas goes in it, the relations list stays under
 * it (for no script and for screen readers). Sized for the canvas: about
 * 300px in the Connected panel, most of the window on the graph page. */
.graph {
  position: relative;
  font-size: var(--text-sm);
}

.connected .graph {
  min-height: 18.75rem;
}

.graph-panel .graph {
  min-height: min(64dvh, 42rem);
}

/* G6's canvas (public/js/graph.js, docs/GRAPH.md) fills the mount. On a
 * photo's page a vertical drag over it still scrolls the page; on the graph
 * page every gesture is the graph's. The cursor is the script's data
 * attribute. With the canvas in place the script marks the relations list
 * .visually-hidden; while focus is in it, it shows again under the canvas,
 * so a keyboard user sees the link they are on. */
.graph-canvas {
  display: block;
  width: 100%;
  height: 18.75rem;
  touch-action: pan-y;
}

.graph-panel .graph-canvas {
  height: min(64dvh, 42rem);
  touch-action: none;
}

/* With the canvas drawn, it fills the graph page's panel to its edges
 * (QA-V nit 4): the panel drops its padding and clips the canvas at its
 * rounded corners, so a node zoomed past the edge leaves at the panel's
 * edge, not at a line inside it. The panel's own min-height is the
 * canvas's height. The list, shown again while focused, keeps the padding. */
.graph-panel:has(> .graph > .graph-canvas) {
  padding: 0;
  overflow: hidden;
}

.graph-panel:has(> .graph > .graph-canvas) .graph {
  min-height: min(72dvh, 47.5rem);
}

.graph-panel:has(> .graph > .graph-canvas) .graph-canvas {
  height: min(72dvh, 47.5rem);
}

.graph-panel .graph > .relations.visually-hidden:focus-within {
  margin: var(--space-3) var(--space-5) var(--space-5);
}

.graph-canvas[data-cursor="pointer"] {
  cursor: pointer;
}

.graph-canvas[data-cursor="grab"] {
  cursor: grab;
}

.graph-canvas[data-cursor="grabbing"] {
  cursor: grabbing;
}

.graph > .relations.visually-hidden:focus-within {
  position: static;
  width: auto;
  height: auto;
  margin: var(--space-3) 0 0;
  overflow: visible;
  clip-path: none;
  white-space: normal;
}

.relations,
.relations ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.relations ul {
  padding-inline-start: var(--space-4);
}

.relations li {
  margin-block: var(--space-1);
}

.rel-term-link {
  font-weight: var(--weight-semibold);
}

.relations .count {
  color: var(--text-muted);
  font-size: var(--text-xs);
}

.related {
  margin-top: var(--space-6);
}

.card-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  align-items: start;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pager {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

.pager a,
.grid-pager a {
  display: inline-flex;
  align-items: center;
  min-height: var(--control-h);
  max-width: 100%;
  padding-inline: var(--space-4);
  border: 1px solid var(--glass-line);
  border-radius: var(--radius-full);
  background: var(--surface-alt);
  color: var(--text);
  text-decoration: none;
}

.pager-next {
  margin-inline-start: auto;
}

/* The home grid's pager (B1, BUILD.md "Paging"): "Page n of N" on its own
   line, straight on the backdrop so in --text; under it the newer page on
   the left and the older on the right, the pills of the photo page's
   pager. */
.grid-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

.grid-pager-status {
  flex: 1 0 100%;
  margin: 0;
  color: var(--text);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  text-align: center;
}

.grid-pager-older {
  margin-inline-start: auto;
}

/* The graph page */
.legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--text);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
}

.legend li {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.graph-panel {
  min-height: min(72dvh, 47.5rem);
  padding: var(--space-5);
  border-radius: var(--radius-lg);
}

/* The 404 page (docs/BUILD.md, "The 404 page"): one glass panel. */
.not-found {
  max-width: 40rem;
  padding: var(--space-5);
  border-radius: var(--radius-lg);
}

.not-found h1 {
  margin: 0 0 var(--space-3);
  color: var(--text);
  font-size: var(--text-2xl);
}

.not-found-text {
  margin: 0 0 var(--space-5);
  color: var(--text);
}

.not-found .back {
  margin-bottom: 0;
}

/* Phone widths: the top bar's Browse link is the ☰ button (the word stays
 * for screen readers), the strip is tighter so a long name fits one line
 * at 390 (docs/LOOK.md, "The name at phone width"), and with scripts on the
 * sidebar can open as a drawer over the page, behind a scrim. */
@media (max-width: 47.9375rem) {
  .browse-link {
    padding-inline: 0;
  }

  .browse-text {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .card:not(.card--wide, .card-hero) .caption {
    right: var(--space-1);
    bottom: var(--space-1);
    left: var(--space-1);
    border-radius: var(--radius-sm);
  }

  .caption {
    padding: var(--space-1) var(--space-2);
  }

  .caption-name {
    font-size: var(--text-sm);
  }
}

@media (min-width: 48rem) {
  .topbar {
    border-radius: var(--radius-full);
  }

  .browse-link {
    display: none;
  }

  .columns {
    grid-template-columns: 15.5rem minmax(0, 1fr);
  }

  .content {
    grid-row: 1;
    grid-column: 2;
  }

  .sidebar {
    position: sticky;
    top: var(--space-3);
    grid-row: 1;
    grid-column: 1;
    max-height: calc(100dvh - var(--space-5));
    overflow-y: auto;
  }
}

@media (min-width: 56.25rem) {
  .card-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* The photo page's two columns start here, not at 56.25rem (QA-V nit 6): at
 * 1024 the rail beside it left a landscape photo 40% of the width; one
 * column gives it the whole content's width. */
@media (min-width: 75rem) {
  .detail {
    grid-template-columns: minmax(0, 1.25fr) minmax(18rem, 0.9fr);
  }

  .card-row {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* A short window (a phone turned sideways, a small laptop window): the
 * photo is capped by the window alone, less the frame's padding, so one
 * scroll brings all of it into view whatever sits above it (the site nav
 * wraps to two rows here), and the related row has four columns at any
 * width, so no related card is larger than the photo (QA-V finding 2;
 * docs/LOOK.md, "The photo on its page"). */
@media (max-height: 40rem) {
  .shot img {
    max-height: calc(100dvh - 2 * var(--space-3));
  }

  .card-row {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* A phone turned sideways (UX-gallery-3): from 48rem wide the sidebar would
 * sit beside the photo and leave it a sliver of a 390 px window. On the
 * photo page only, the columns are one again, the sidebar follows the
 * content, and the photo is capped so that, once the photo bar is
 * scrolled to the top, all of it is in view under the bar: the window less
 * the bar, the gap under it and the frame's bottom padding. The top bar's
 * Browse link is the drawer's button here too (drawer.js, review RGU1-2),
 * so the open drawer rules below hold here too; with scripts off
 * the link jumps to the sidebar, as it does on a phone. */
@media (min-width: 48rem) and (max-height: 30rem) and (orientation: landscape) {
  body[data-page="photo"] .columns {
    grid-template-columns: minmax(0, 1fr);
  }

  body[data-page="photo"] .content,
  body[data-page="photo"] .sidebar {
    grid-row: auto;
    grid-column: 1;
  }

  body[data-page="photo"] .sidebar {
    position: static;
    max-height: none;
    overflow-y: visible;
  }

  body[data-page="photo"] .browse-link {
    display: inline-flex;
  }

  body[data-page="photo"] .shot img {
    max-height: calc(100svh - var(--control-h) - var(--space-4) - var(--space-3));
  }
}

/* The open drawer, below 48rem and on a photo page in a phone turned
 * sideways (the query above): one rule list for both (review RGU1c-7).
 * Only drawer.js sets body[data-drawer="open"], and only while the drawer
 * is in use (from 48rem it closes it, except on that photo page), so the
 * landscape arm needs no page scope of its own. It comes after the
 * landscape block, whose static sidebar it overrides at equal
 * specificity. */
@media (max-width: 47.9375rem), (min-width: 48rem) and (max-height: 30rem) and (orientation: landscape) {
  body[data-drawer="open"] {
    overflow: hidden;
  }

  /* The open drawer is modal, so it stacks over the site nav (nav.css puts
   * the bar at z-index 900): the scrim covers the bar, the drawer sits on
   * the scrim, and the top bar sits on both, so the ☰ button that opened
   * the drawer stays the button that closes it. The drawer starts under
   * the top bar where it lies at scroll 0 (the site nav's height, the
   * frame's padding, the bar's own height and a gap), which is where the
   * button is whenever it can be tapped. */
  body[data-drawer="open"]::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 1000;
    background: var(--scrim);
  }

  body[data-drawer="open"] .sidebar {
    position: fixed;
    top: calc(var(--wp-site-nav-h, 0px) + var(--space-3) + var(--control-h) + var(--space-3) + var(--space-3));
    bottom: var(--space-2);
    left: var(--space-2);
    z-index: 1001;
    width: min(20rem, calc(100vw - 3rem));
    max-height: none;
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  body[data-drawer="open"] .topbar {
    z-index: 1002;
  }
}

/* The life list (FEAT-gallery-4; docs/BUILD.md, "The life list"): a grid
 * of tiles in the checklist's order, two a row on a phone and as many
 * 11rem columns as fit from 48rem. A tile is solid, not glass (the glass
 * budget is the frame's). Its figure is a square: a seen bird's photo
 * fitted whole, never cropped; an unseen one's silhouette, which the theme
 * sheets tint. The heading and the credits sit on the backdrop, so they
 * are --text. */
.dex {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  margin: 0 0 var(--space-6);
  padding: 0;
  list-style: none;
}

.dex > li {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--text);
  overflow: hidden;
}

.dex-link {
  display: flex;
  flex: 1;
  flex-direction: column;
  color: inherit;
  text-decoration: none;
}

.dex-figure {
  display: block;
  aspect-ratio: 1;
  margin-bottom: var(--space-2);
  overflow: hidden;
  background: var(--surface-alt);
}

.dex-figure picture,
.dex-figure img {
  width: 100%;
  height: 100%;
}

.dex-figure img {
  object-fit: contain;
}

.dex-unseen .dex-figure {
  background: var(--dex-unseen-figure);
}

.dex-silhouette {
  padding: var(--space-4);
  filter: var(--dex-silhouette-filter);
  opacity: var(--dex-silhouette-opacity);
}

.dex-n,
.dex-name,
.dex-sci,
.dex-meta {
  display: block;
  padding: 0 var(--space-3);
}

.dex-n {
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: var(--text-xs);
}

.dex-name {
  font-weight: var(--weight-semibold);
  line-height: 1.25;
}

.dex-link .dex-name {
  color: var(--link);
}

.dex-sci {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.dex-meta {
  margin: var(--space-1) 0 var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-xs);
}

.dex-heading {
  margin: var(--space-6) 0 var(--space-3);
  color: var(--text);
  font-size: var(--text-xl);
}

.dex-credits {
  margin: 0 0 var(--space-6);
  color: var(--text);
  font-size: var(--text-xs);
}

.dex-credits p {
  margin: 0 0 var(--space-1);
}

.lifelist-link {
  color: var(--text);
}

.dex-credits ul {
  margin: 0;
  padding-left: var(--space-5);
}

@media (min-width: 48rem) {
  .dex {
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  }
}
