/* Design tokens — restyle the site by changing these. */
:root {
  /* Colours: "Moss & Bark" — parchment page, bark-brown header, moss-green accent. */
  --color-bg: #f2eee3;
  --color-surface: #fbf9f3;
  --color-text: #2f2a21;
  --color-muted: #6e6555;
  --color-border: #ddd4c1;
  --color-accent: #4d6b33;
  --color-accent-contrast: #fbf9f3;
  /* A deeper moss for the "edge" under pressable buttons, and a pale one for tags and cards. */
  --color-accent-dark: #354a22;
  --color-accent-pale: #e3e9d3;
  /* A sage between the pale moss and the stitching, for a tag that sits on a card. */
  --color-accent-soft: #c5d4ab;
  /* The stitching round cards, and how far in from the edge it runs. */
  --color-stitch: #a7bb88;
  --stitch-inset: 0.5rem;
  --color-error: #a8321e;
  --color-success: #3f6e2a;
  /* A court's leader: its crown and the frame round its tile. */
  --color-leader: #b0820f;
  --color-leader-shadow: #d9c58f;
  /* A special event Fauri: the frame and tag on its tile in the pool. */
  --color-event: #7a6389;
  --color-event-frame: #b9a9c4;
  --color-event-shadow: #e0d8e5;
  /* Dims the page behind a box that opens over it. */
  --color-backdrop: rgb(47 42 33 / 0.35);
  /* The header bar and the links on it. */
  --color-header-bg: #3e2e20;
  --color-header-text: #f7efdd;
  --color-header-muted: #d6c8ae;
  --color-header-current: #c9dba3;
  --color-header-hover: rgb(255 255 255 / 0.08);
  --color-footer-bg: #ebe5d6;
  /* The dark forest green the whole site sits on, like a page on a desk. */
  --color-page-bg: #171b16;
  /* The fireflies glowing in the dark green round the page, and the page's faint glow. */
  --color-firefly: #f1e98a;
  --color-firefly-glow: rgb(241 233 138 / 0.55);
  --color-page-glow: rgb(156 196 111 / 0.12);
  /*
   * The vine trailing under the header: one 64×16 tile repeated across. Colours can't be
   * variables inside an image, so they are written in: the stem is --color-accent (#4d6b33),
   * the leaves two lighter mosses.
   */
  --vine-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='64' height='16'%3E%3Cpath d='M0 3Q16 14 32 3T64 3' fill='none' stroke='%234d6b33' stroke-width='1.6'/%3E%3Cpath d='M14 8C18 7 21 10 21 14C17 14 14 12 14 8Z' fill='%237a9a52'/%3E%3Cpath d='M46 6C50 4 54 6 55 10C51 11 47 10 46 6Z' fill='%23607f3f'/%3E%3C/svg%3E");
  --vine-band: 4px;
  --vine-height: 20px;

  /* Fonts */
  /* Nunito is a web font, loaded in views/layout.ts. */
  --font-body: 'Nunito', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-heading: var(--font-body);
  /* The site title alone is a fine serif, to go with the logo's drawing; also loaded there. */
  --font-site-title: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  /* Headings are bold; page titles one step heavier (the heaviest weight looked cartoonish). */
  --font-weight-heading: 700;
  --font-weight-title: 800;
  --font-size-base: 1rem;
  --font-size-small: 0.875rem;
  --line-height: 1.6;

  /* Spacing */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 2rem;
  --space-xl: 4rem;

  /* Shape & layout */
  --radius: 0.5rem;
  /* The big, soft corners of tiles and cards. */
  --radius-soft: 1.4rem;
  /* How wide the site's page gets (content plus its padding). */
  --content-width: 72rem;
  /* The dark green kept on each side of the page. */
  --page-gutter: var(--space-lg);
  /* The page's bottom corners, slightly rounded. */
  --page-radius: 0.75rem;
  /* The thin strip of green left under the page, so its rounded corners show. */
  --page-strip: var(--space-md);
}

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

/* Always keep room for the scrollbar, so the page is the same width and in the same place
   whether or not a page is long enough to scroll. */
html {
  scrollbar-gutter: stable;
  background: var(--color-page-bg);
}

body {
  margin: 0;
  background: var(--color-page-bg);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--font-size-base);
  line-height: var(--line-height);
}

h1,
h2,
h3 {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-heading);
  line-height: 1.2;
  margin: 0 0 var(--space-md);
}

/* Page titles: the heaviest weight. */
.site-main h1 {
  font-weight: var(--font-weight-title);
}

p {
  margin: 0 0 var(--space-md);
}

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

/* Layout */

/*
 * The site's page: header, content and footer on one sheet over the dark green, running from
 * the top of the window to just short of the bottom (a thin strip of green shows under its
 * rounded corners), so a short page still fills the screen with the footer at the bottom, and
 * growing with longer content.
 */
.site-page {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  width: min(100% - 2 * var(--page-gutter), var(--content-width));
  min-height: calc(100vh - var(--page-strip));
  margin: 0 auto var(--page-strip);
  border-radius: 0 0 var(--page-radius) var(--page-radius);
  overflow: hidden;
  background: var(--color-bg);
  box-shadow: 0 0 60px var(--color-page-glow);
}

/*
 * Fireflies glowing on and off in the dark green round the page (`.fireflies` in layout.ts:
 * empty <i>s, placed here). They stay put while the page scrolls, behind it, so they only
 * show in the margins. They glow on and off and drift slowly; without motion they just stay
 * lit and still.
 */
.fireflies {
  /* How wide the green margin on each side of the page is. */
  --page-margin: max(var(--page-gutter), (100vw - var(--content-width)) / 2);
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.fireflies i {
  position: absolute;
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  background: var(--color-firefly);
  box-shadow: 0 0 10px 3px var(--color-firefly-glow);
  animation:
    firefly-glow 3.2s ease-in-out infinite,
    firefly-drift 11s ease-in-out infinite alternate;
}

/*
 * Each firefly hovers outside the page: `--out` is how far from the page's edge (so they
 * stay beside it however wide the screen is) and `--size` how big it is; the small ones
 * read as further away. The first eight are on the left, the rest on the right. Each has its
 * own timing for the glow and for the drift (the two values of delay and duration), so they
 * never move in step.
 */
.fireflies i:nth-child(-n + 8) {
  left: calc(var(--page-margin) - var(--out));
}

.fireflies i:nth-child(n + 9) {
  right: calc(var(--page-margin) - var(--out));
}

.fireflies i:nth-child(1) { --out: 1.9rem; --size: 6px; top: 12%; animation-delay: 0s, 0.0s; animation-duration: 3.2s, 9s; }
.fireflies i:nth-child(2) { --out: 5.9rem; --size: 4px; top: 24%; animation-delay: 1.9s, -1.3s; animation-duration: 4.4s, 10.5s; }
.fireflies i:nth-child(3) { --out: 3.75rem; --size: 7px; top: 38%; animation-delay: 1.1s, -2.6s; animation-duration: 3.4s, 12s; }
.fireflies i:nth-child(4) { --out: 8.75rem; --size: 4px; top: 50%; animation-delay: 0.4s, -3.9s; animation-duration: 5s, 13.5s; }
.fireflies i:nth-child(5) { --out: 2.25rem; --size: 5px; top: 62%; animation-delay: 2.2s, -5.2s; animation-duration: 3.2s, 15s; }
.fireflies i:nth-child(6) { --out: 6.9rem; --size: 6px; top: 74%; animation-delay: 0.9s, -6.5s; animation-duration: 3.8s, 9s; }
.fireflies i:nth-child(7) { --out: 3.25rem; --size: 6px; top: 86%; animation-delay: 0.7s, -7.8s; animation-duration: 4s, 10.5s; }
.fireflies i:nth-child(8) { --out: 9.4rem; --size: 3px; top: 93%; animation-delay: 2.8s, -9.1s; animation-duration: 4.6s, 12s; }
.fireflies i:nth-child(9) { --out: 2.75rem; --size: 6px; top: 8%; animation-delay: 1.4s, -10.4s; animation-duration: 3.6s, 13.5s; }
.fireflies i:nth-child(10) { --out: 7.5rem; --size: 4px; top: 18%; animation-delay: 0.2s, -11.7s; animation-duration: 4.8s, 15s; }
.fireflies i:nth-child(11) { --out: 4.4rem; --size: 6px; top: 30%; animation-delay: 0.6s, -13.0s; animation-duration: 3.2s, 9s; }
.fireflies i:nth-child(12) { --out: 1.75rem; --size: 5px; top: 44%; animation-delay: 2.4s, -14.3s; animation-duration: 3.6s, 10.5s; }
.fireflies i:nth-child(13) { --out: 8.4rem; --size: 3px; top: 56%; animation-delay: 1.2s, -15.6s; animation-duration: 5.2s, 12s; }
.fireflies i:nth-child(14) { --out: 3.6rem; --size: 7px; top: 68%; animation-delay: 1.7s, -16.9s; animation-duration: 4.2s, 13.5s; }
.fireflies i:nth-child(15) { --out: 6.25rem; --size: 5px; top: 80%; animation-delay: 2.6s, -18.2s; animation-duration: 3.2s, 15s; }
.fireflies i:nth-child(16) { --out: 2.1rem; --size: 6px; top: 92%; animation-delay: 0.5s, -19.5s; animation-duration: 4s, 9s; }

@keyframes firefly-glow {
  0%,
  100% {
    opacity: 0.2;
  }

  50% {
    opacity: 1;
  }
}

/* A slow wander upwards and sideways, then back again. */
@keyframes firefly-drift {
  0% {
    translate: 0 0;
  }

  33% {
    translate: 14px -18px;
  }

  66% {
    translate: -10px -30px;
  }

  100% {
    translate: 8px -44px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .fireflies i {
    animation: none;
    opacity: 0.7;
  }
}

/* The padded strip inside the page that the header, content and footer each sit in. */
.container {
  padding: 0 var(--space-lg);
}

/* On a phone: a thinner strip of green, and less padding inside the page. */
@media (max-width: 36rem) {
  :root {
    --page-gutter: var(--space-sm);
  }

  .container {
    padding: 0 var(--space-md);
  }
}

.site-header {
  position: relative;
  background: var(--color-header-bg);
  color: var(--color-header-text);
  padding: var(--space-md) 0;
}

/* A moss band with a vine trailing under it, hanging over the top of the page's padding. */
.site-header::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 1;
  height: var(--vine-height);
  border-top: var(--vine-band) solid var(--color-accent);
  background: var(--vine-image) repeat-x 0 0 / 64px 16px;
  pointer-events: none;
}

.site-header .container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm) var(--space-lg);
}

/* The site title with the account links (log in, or username and log out) beside it, their
   text sitting on the same line as the title's. */
.site-brand {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-xs) var(--space-md);
}

/* Not a flex box: its baseline has to be its text's, not the logo's, for the line-up above. */
.site-title {
  display: inline-block;
  white-space: nowrap;
  font-family: var(--font-site-title);
  font-size: 1.65rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--color-header-text);
  text-decoration: none;
}

/* The favicon as a little logo beside the title, kept small enough not to make the header taller. */
.site-logo {
  width: auto;
  height: 2.1rem;
  margin-right: var(--space-xs);
  vertical-align: middle;
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Links, plus the log out button, which is styled to match them. */
.site-nav-link {
  display: block;
  padding: var(--space-xs) var(--space-sm);
  border: none;
  border-radius: 999px;
  background: none;
  color: var(--color-header-muted);
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}

.site-nav-link:hover {
  color: var(--color-header-text);
  background: var(--color-header-hover);
}

.site-nav-link[aria-current='page'] {
  color: var(--color-header-current);
  background: var(--color-header-hover);
  font-weight: bold;
}

/* An unseen bold copy of the link's text (`data-label`, set in nav.ts), so every link is
   always as wide as it is when current and the nav doesn't shift from page to page. */
.site-nav-link[data-label]::after {
  content: attr(data-label);
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: bold;
}

.site-nav-link:focus-visible {
  outline: 2px solid var(--color-header-current);
  outline-offset: 1px;
}

/*
 * The menu button (a checkbox and its label, in layout.ts; no script). Only a phone has it:
 * there the main nav is folded away until the button is pressed, then opens as a list under
 * the title.
 */
.nav-toggle,
.nav-toggle-button {
  display: none;
}

@media (max-width: 36rem) {
  .site-header .container {
    gap: var(--space-sm);
  }

  /* Takes the room beside the button, so the button stays on the title's row. */
  .site-brand {
    flex: 1;
    min-width: 0;
  }

  /* The checkbox itself is unseen but still takes keyboard focus. */
  .nav-toggle {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
  }

  .nav-toggle-button {
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 999px;
    color: var(--color-header-text);
    cursor: pointer;
  }

  .nav-toggle-button:hover,
  .nav-toggle:checked + .nav-toggle-button {
    background: var(--color-header-hover);
  }

  .nav-toggle:focus-visible + .nav-toggle-button {
    outline: 2px solid var(--color-header-current);
    outline-offset: 1px;
  }

  .site-nav-main {
    display: none;
    flex-basis: 100%;
  }

  .nav-toggle:checked ~ .site-nav-main {
    display: block;
  }

  .site-nav-main ul {
    flex-direction: column;
  }

  /* Full-width rows, tall enough to tap. */
  .site-nav-main .site-nav-link {
    width: 100%;
    padding: var(--space-sm) var(--space-md);
    text-align: left;
  }
}

/* The top padding also clears the vine hanging from the header. */
.site-main {
  flex: 1;
  padding: calc(var(--space-lg) + var(--vine-height)) 0 var(--space-lg);
}

.site-footer {
  background: var(--color-footer-bg);
  border-top: 1px solid var(--color-border);
  padding: var(--space-md) 0;
  color: var(--color-muted);
  font-size: var(--font-size-small);
}

/* Copyright on the left, links on the right; they stack when there isn't room for both. */
.site-footer .container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-xs) var(--space-md);
}

/* Components */
/*
 * A soft pale-moss card with stitching round its edge. The stitching is behind the content
 * (z-index -1 inside the card's own stacking context, from `isolation`).
 */
.card {
  position: relative;
  isolation: isolate;
  background: var(--color-accent-pale);
  border-radius: var(--radius-soft);
  padding: var(--space-lg);
}

/* A row of stitches sewn just inside the card's edge. */
.card::before {
  content: '';
  position: absolute;
  inset: var(--stitch-inset);
  z-index: -1;
  border: 2px dashed var(--color-stitch);
  border-radius: calc(var(--radius-soft) - var(--stitch-inset));
  pointer-events: none;
}

.card + .card {
  margin-top: var(--space-md);
}

/* Grid of square picture tiles with a caption underneath: owners, and an owner's folders. */
.tile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12.8rem, 1fr));
  gap: var(--space-lg) var(--space-md);
  margin: 0;
  padding: 0;
  list-style: none;
}

/*
 * Tiles for grids of Fauri pictures: a step bigger than the plain grid's.
 */
.tile-grid-large {
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}

/* The same pictures a fifth smaller, five to a row at full width: the home page's. */
.tile-grid-small {
  grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
}

/* Exactly five to a row at full width, three on a mid-width screen: the lineart's. */
.tile-grid-five {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

/* A couple of those tiles half as big again, centred: the lineart's bases. */
.tile-grid-big {
  grid-template-columns: repeat(2, minmax(0, calc((100% - 4 * var(--space-md)) / 5 * 1.5)));
  justify-content: center;
}

/*
 * The lineart's tiles at their usual size, but with a row that isn't full centred: the
 * glamours and mutations. A grid can't centre its last row, so this one is a wrapping flex
 * row, its tiles as wide as `--per-row` grid columns would be (a hair under, so rounding
 * never drops one to the next row).
 */
.tile-grid-centred {
  --per-row: 5;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.tile-grid-centred > li {
  flex: none;
  width: calc((100% - (var(--per-row) - 1) * var(--space-md)) / var(--per-row) - 0.1px);
  min-width: 0;
}

@media (max-width: 56rem) {
  .tile-grid-five {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .tile-grid-big {
    grid-template-columns: repeat(2, minmax(0, 40%));
  }

  .tile-grid-centred {
    --per-row: 3;
  }
}

/* A tile grid that follows something else on the page, with a heading of its own if any. */
.tile-group {
  margin-top: var(--space-lg);
}

/* A column as tall as its row, so the name can sit at the bottom: names in a row then line up
   even when their pictures are different heights. */
.tile {
  display: flex;
  flex-direction: column;
  height: 100%;
  color: var(--color-text);
  text-align: center;
  text-decoration: none;
  transition: transform 0.2s cubic-bezier(0.3, 1.6, 0.5, 1);
}

/* Only tiles that are links react to the pointer: they bounce up a little. */
a.tile:hover,
a.tile:focus-visible {
  transform: translateY(-5px);
}

a.tile:focus-visible {
  outline: none;
}

a.tile:focus-visible .tile-photo,
a.tile:focus-visible .tile-image {
  outline: 3px solid var(--color-accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .tile {
    transition: none;
  }
}

/* A real picture on a tile, at its own proportions, on a soft rounded card with a chunky
   shadow under it. */
.tile-photo {
  display: block;
  flex: none;
  width: 100%;
  height: auto;
  border-radius: var(--radius-soft);
  background: var(--color-surface);
  box-shadow: 0 5px 0 var(--color-border);
}

/* The gap over the name is the picture's, as the name's own top margin pushes it down. */
.tile-photo:has(+ .tile-name) {
  margin-bottom: var(--space-md);
}

/* The picture on a Fauri's own page: full size, but never wider than the page. */
.fauri-image {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
}

/* A smaller copy of a Fauri's picture, to check it's the right one. */
.preview-image {
  display: block;
  max-width: min(100%, 20rem);
  height: auto;
  margin-bottom: var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
}

.tile-image {
  display: grid;
  flex: none;
  place-items: center;
  aspect-ratio: 1;
  margin-bottom: var(--space-md);
  border-radius: var(--radius-soft);
  background: var(--color-surface);
  box-shadow: 0 5px 0 var(--color-border);
  color: var(--color-border);
  overflow: hidden;
}

/* A picture drawn for the site (e.g. a folder's icon), filling a tile's square box. */
.tile-icon {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The name under a tile, on a little moss tag. */
.tile-name {
  width: fit-content;
  max-width: 100%;
  margin: auto auto 0;
  padding: 0.1rem 0.75rem;
  border-radius: 999px;
  background: var(--color-accent-pale);
  color: var(--color-accent-dark);
  font-weight: bold;
  overflow-wrap: anywhere;
  line-height: 1.4;
}

.tile-note {
  margin-top: var(--space-xs);
  color: var(--color-muted);
  font-size: var(--font-size-small);
}

/* On a phone every tile grid is two to a row, with closer rows and smaller name tags. */
@media (max-width: 36rem) {
  .tile-grid,
  .tile-grid-large,
  .tile-grid-small,
  .tile-grid-five {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-md);
  }

  .tile-grid-centred {
    --per-row: 2;
  }

  .tile-name {
    font-size: var(--font-size-small);
  }
}

/* Buttons are round and look pressable: a darker edge underneath that they sink into. */
.button {
  display: inline-block;
  padding: var(--space-sm) var(--space-md);
  border: none;
  border-radius: 999px;
  background: var(--color-accent);
  box-shadow: 0 4px 0 var(--color-accent-dark);
  color: var(--color-accent-contrast);
  font: inherit;
  font-weight: bold;
  cursor: pointer;
}

.button:hover {
  filter: brightness(1.1);
}

.button:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--color-accent-dark);
}

/* A second button beside the main one, outlined rather than filled. A link styled this way
   can hold an icon before its text. */
.button-secondary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  background: var(--color-surface);
  box-shadow: inset 0 0 0 2px var(--color-accent);
  color: var(--color-accent);
  text-decoration: none;
}

.button-secondary:hover {
  filter: none;
  background: var(--color-bg);
}

.button-secondary:active {
  transform: none;
  box-shadow: inset 0 0 0 2px var(--color-accent);
}

/* A plainer button for the lesser choice beside a main one (e.g. Cancel). */
.button-quiet,
.button-quiet:active {
  background: none;
  box-shadow: none;
  transform: none;
  color: var(--color-muted);
  font-weight: normal;
}

.button-quiet:hover {
  color: var(--color-text);
  filter: none;
}

/* Buttons side by side at the end of a form. */
.button-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
}

/* A small button that is just an icon (e.g. the edit pencil). */
.icon-button {
  display: grid;
  place-items: center;
  flex: none;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: none;
  border-radius: var(--radius);
  background: none;
  color: var(--color-muted);
  cursor: pointer;
}

.icon-button:hover {
  background: var(--color-surface);
  color: var(--color-text);
}

/* An icon button with nothing to do (e.g. the next arrow on the last Fauri of a folder). */
.icon-button-off,
.icon-button-off:hover {
  background: none;
  color: var(--color-border);
  cursor: default;
}

/* A text field with an icon button at its end (e.g. the dice in the rename box). */
.input-with-button {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin-top: var(--space-xs);
}

.field .input-with-button input {
  flex: 1;
  min-width: 0;
  margin-top: 0;
}

/* The row above a Fauri's name: the link back to its folder, and the arrows through it. */
.back-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  min-height: 2rem;
  margin-bottom: var(--space-md);
}

.fauri-arrows {
  display: flex;
  gap: var(--space-xs);
}

/* Home: its sections. */
/*
 * The home page's last section sits at the bottom of the page, just above the footer, however
 * short the welcome is: the page's content becomes a column as tall as the space it has, and
 * the section's auto margin takes up the slack.
 */
.site-main:has(.home-section) {
  display: flex;
  flex-direction: column;
}

.site-main > .container:has(> .home-section) {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.home-section {
  margin-top: auto;
  padding-top: var(--space-lg);
}

/* An owner's page: what they wrote about their court, under its name. */
.court-owner {
  margin-top: calc(-1 * var(--space-sm));
  color: var(--color-muted);
}

.court-motto {
  font-style: italic;
  font-weight: bold;
  color: var(--color-accent-dark);
  overflow-wrap: anywhere;
}

/* Keeps the line breaks the owner typed. */
.court-blurb {
  max-width: 44rem;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* A court's badges, under its folders: each on a moss tag with a gold star. */
.court-badges,
.achievements {
  margin-top: var(--space-lg);
}

.court-badges h2,
.achievements h2 {
  font-size: 1.25rem;
}

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

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0.1rem 0.75rem;
  border-radius: 999px;
  background: var(--color-accent-pale);
  color: var(--color-accent-dark);
  font-weight: bold;
}

.badge svg {
  flex: none;
  color: var(--color-leader);
}

/* The badges a court hasn't earned yet, shown to its owner: as many columns as fit, each
   with its name, how far along it is and a bar. */
.achievement-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: var(--space-md) var(--space-lg);
  margin: 0;
  padding: 0;
  list-style: none;
}

.achievement-name {
  display: flex;
  justify-content: space-between;
  gap: var(--space-sm);
}

.achievement-list progress {
  display: block;
  width: 100%;
  margin-top: var(--space-xs);
  accent-color: var(--color-accent);
}

/*
 * A certed Fauri's page: the picture with its profile in a card just to its right, as on
 * Dream Gen. The card drops under the picture when the window is too narrow for both.
 */
.fauri-with-profile {
  display: grid;
  grid-template-columns: minmax(0, auto) clamp(17rem, 34%, 23rem);
  justify-content: center;
  gap: var(--space-lg);
}

@media (max-width: 48rem) {
  .fauri-with-profile {
    grid-template-columns: 1fr;
  }
}

/* The card is as tall as the picture beside it (or taller, if it holds more), with the court
   part at the bottom. */
.fauri-profile {
  display: flex;
  flex-direction: column;
  padding: calc(var(--space-md) + var(--stitch-inset));
}

.fauri-profile h2 {
  margin-bottom: var(--space-sm);
  font-size: 1.25rem;
}

/* A group's heading: a soft sage tag sitting in the middle of the row of stitches over its
   group (see .profile-group). */
.fauri-profile h3 {
  width: fit-content;
  margin: -0.8rem auto var(--space-xs);
  padding: 0 0.85rem;
  border-radius: 999px;
  background: var(--color-accent-soft);
  color: var(--color-accent-dark);
  font-size: var(--font-size-small);
  line-height: 1.5;
}

/* A certed Fauri's bio, in a card under its picture and profile: what its owner wrote and,
   for them, the box to write it in under a row of formatting buttons. */
/* Set in from the page's edges by as much again as the page's own padding, and twice a card's
   padding inside. */
.fauri-bio {
  margin: var(--space-lg) var(--space-lg) 0;
  padding-inline: calc(2 * var(--space-lg));
}

/* On a phone there isn't the room for either. */
@media (max-width: 36rem) {
  .fauri-bio {
    margin-inline: 0;
    padding-inline: var(--space-lg);
  }
}

.fauri-bio h2 {
  margin-bottom: var(--space-sm);
  font-size: 1.25rem;
}

.bio-text {
  overflow-wrap: anywhere;
}

.bio-text h3 {
  margin: var(--space-md) 0 var(--space-xs);
  color: var(--color-accent-dark);
  font-size: 1.1rem;
}

.bio-text p,
.bio-text ul {
  margin: 0 0 var(--space-sm);
}

.bio-text > :first-child {
  margin-top: 0;
}

.bio-text > :last-child {
  margin-bottom: 0;
}

/* The Edit button under a bio: opening it swaps the bio for the box, and it becomes Cancel. */
.bio-edit {
  margin-top: var(--space-md);
}

.bio-edit summary {
  width: fit-content;
  list-style: none;
  cursor: pointer;
}

.bio-edit summary::-webkit-details-marker {
  display: none;
}

.bio-edit[open] summary {
  margin-bottom: var(--space-md);
}

.bio-edit[open] .bio-edit-open,
.bio-edit:not([open]) .bio-edit-close,
.fauri-bio:has(.bio-edit[open]) > .bio-text {
  display: none;
}

.bio-tools {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}

.bio-editor [hidden] {
  display: none;
}

/* The box the bio is written in, showing it as it will look. */
.bio-rich {
  min-height: 9rem;
  margin: var(--space-xs) 0 var(--space-sm);
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
}

.bio-rich:empty::before {
  content: attr(data-placeholder);
  color: var(--color-muted);
}

/* A formatting button: a small pill with its name on it. */
.bio-tool {
  padding: 0.1rem 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  font-size: var(--font-size-small);
  cursor: pointer;
}

.bio-tool:hover {
  background: var(--color-bg);
}

/* The formatting that is on where the cursor is. */
.bio-tool[aria-pressed='true'] {
  border-color: var(--color-accent);
  background: var(--color-accent-soft);
}

.bio-editor .field {
  margin-bottom: var(--space-sm);
}

.bio-editor textarea {
  resize: vertical;
}

.bio-editor .field-hint {
  margin: 0 0 var(--space-sm);
}

/* Cells two to a row (three for the hair), each a small label over what it holds. A long
   value wraps inside its own cell. */
.profile-cells {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-sm) var(--space-md);
  margin: 0;
}

.profile-cells-three {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.profile-cells dt,
.profile-label {
  color: var(--color-muted);
  font-size: var(--font-size-small);
  line-height: 1.3;
}

.profile-cells dd {
  margin: 0;
  font-weight: bold;
  line-height: 1.3;
  overflow-wrap: anywhere;
}

/* The personalities' label, with the pencil that edits them at the far end of its row. */
.profile-label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 2rem;
  margin-top: var(--space-sm);
}

/* The personalities, a tag each, wrapping onto as many lines as they need. */
.profile-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

.profile-tag {
  max-width: 100%;
  padding: 0.1rem 0.75rem;
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-accent-dark);
  font-weight: bold;
  line-height: 1.4;
  overflow-wrap: anywhere;
}

/* Hair, mutations, parents: each set apart from what is above it by a row of stitches, which
   its heading sits on. */
.profile-group {
  margin-top: calc(var(--space-md) + var(--space-sm));
  border-top: 2px dashed var(--color-stitch);
}

.profile-note {
  margin: 0;
  font-weight: bold;
}

/* The court part of the profile, set apart by a row of stitches. */
.profile-court {
  margin-top: auto;
}

.profile-court::before {
  content: '';
  display: block;
  margin: var(--space-md) 0;
  border-top: 2px dashed var(--color-stitch);
}

/* The site's leader picture, in the promote button and beside "Court leader". */
.promoted-icon {
  flex: none;
  width: 6rem;
  height: 6rem;
  object-fit: contain;
}

/* The promote button, and the demote one that takes its place on a leader: the full width of
   the card. */
.court-button {
  justify-content: center;
  gap: var(--space-sm);
  width: 100%;
}

/* The promote button holds the leader picture over its words, so it is a soft box rather
   than a pill. */
.promote-button {
  flex-direction: column;
  gap: 0;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-soft);
}

/* What a leader's profile says in place of the promote button: gold, like every leader's crown. */
.leader-status {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin: 0;
  color: var(--color-leader);
  font-weight: bold;
}

.leader-status + form {
  margin-top: var(--space-sm);
}

/*
 * A pinned tile (a court's leader, first in its folder): a gold frame round the picture (a
 * thin gap, then a gold band) and a crown in its corner. Both are drawn over the tile (shadows
 * and an absolutely placed badge), so it is exactly the size of its neighbours.
 */
.tile-pinned {
  position: relative;
}

.tile-pinned .tile-photo,
.tile-pinned .tile-image {
  box-shadow:
    0 0 0 3px var(--color-leader),
    0 5px 0 3px var(--color-leader-shadow);
}

.crown-badge {
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border: 2px solid var(--color-leader);
  border-radius: 50%;
  background: var(--color-surface);
  color: var(--color-leader);
}

/* A photo tile that matches the square placeholder tiles beside it, the image fitted inside. */
.tile-photo-square {
  aspect-ratio: 1;
  object-fit: contain;
}

/* A page heading with something beside it (e.g. an edit button). */
.title-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

.title-row h1 {
  margin: 0;
  overflow-wrap: anywhere;
}

/*
 * A little box that opens over the page (an element with the popover attribute; the browser
 * centres it and closes it on Escape or a click outside). Don't set display here: that would
 * show it while closed.
 */
.popover-box {
  width: min(22rem, calc(100vw - 2 * var(--space-md)));
  padding: var(--space-md);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  color: var(--color-text);
}

/* A roomier one, for a box with more to fill in. */
.popover-box-wide {
  width: min(30rem, calc(100vw - 2 * var(--space-md)));
}

.popover-box textarea {
  resize: vertical;
}

.popover-box::backdrop {
  background: var(--color-backdrop);
}

.popover-box .field {
  margin-bottom: var(--space-sm);
}

.popover-box form > p:first-child {
  margin-top: 0;
}

/* Small print under a form, set apart by a rule. */
.form-notes {
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-border);
  color: var(--color-muted);
  font-size: var(--font-size-small);
}

.form-notes p:last-child {
  margin-bottom: 0;
}

.card-narrow {
  max-width: 24rem;
  margin: 0 auto;
}

.field {
  display: block;
  margin: 0 0 var(--space-md);
}

.field-hint {
  display: block;
  color: var(--color-muted);
  font-size: var(--font-size-small);
}

.field input,
.field select,
.field textarea {
  display: block;
  width: 100%;
  margin-top: var(--space-xs);
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
}

/* A tick box with its label beside it. */
.checkbox-field {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  margin: 0 0 var(--space-md);
}

/* The admin's buttons under a Fauri's picture. */
.admin-actions {
  margin-top: var(--space-md);
}

/* The badges under a Fauri's picture (glamour, special event): round gold buttons that say
   what they stand for when rolled over, tapped or tabbed to. */
.trait-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-md);
}

.trait-badge {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border: 2px solid var(--color-leader);
  border-radius: 50%;
  background: var(--color-surface);
  box-shadow: 0 3px 0 var(--color-leader-shadow);
  color: var(--color-leader);
  cursor: help;
  transition: transform 0.15s ease;
}

.trait-badge:hover,
.trait-badge:focus {
  transform: translateY(-3px);
}

.trait-badge::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + var(--space-sm));
  left: 0;
  z-index: 1;
  width: max-content;
  max-width: min(18rem, 80vw);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius);
  background: var(--color-header-bg);
  color: var(--color-header-text);
  font-size: var(--font-size-small);
  font-weight: bold;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.trait-badge:hover::after,
.trait-badge:focus::after {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .trait-badge,
  .trait-badge::after {
    transition: none;
  }

  .trait-badge:hover,
  .trait-badge:focus {
    transform: none;
  }
}

/* Rows of three compact fields, for forms with a lot of them. */
.form-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-sm) var(--space-md);
  align-items: start;
  margin-bottom: var(--space-md);
}

.form-grid .field {
  margin: 0;
  font-size: var(--font-size-small);
}

.form-grid .field input,
.form-grid .field select,
.form-grid .field textarea {
  margin-top: 0;
  padding: var(--space-xs) var(--space-sm);
}

/* As many narrow fields per row as fit (six at full width), for a form that should stay
   short (the search). */
.form-grid-wide {
  grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
}

/* The search form sits in a card above what it shows. */
.search-form {
  margin-bottom: var(--space-lg);
  padding: calc(var(--space-md) + var(--stitch-inset));
}

/* Dream Gen: the picture with its options in a narrow card just to its right (narrower in a
   small window), the pair centred on the page so they stay together when the picture is
   narrower than the page. Only on a phone do the options drop under the picture. */
.dream-gen {
  display: grid;
  grid-template-columns: minmax(0, auto) clamp(11rem, 28%, 16rem);
  justify-content: center;
  gap: var(--space-md);
  align-items: start;
}

.dream-gen-form {
  padding: calc(var(--space-md) + var(--stitch-inset));
}

/* The options stacked one per row. */
.dream-gen-form .form-grid {
  grid-template-columns: 1fr;
}

.dream-gen-form .button-row {
  gap: var(--space-md) var(--space-sm);
}

@media (max-width: 36rem) {
  .dream-gen {
    grid-template-columns: 1fr;
  }
}

/* A question that opens to show its answer (a <details> element). */
.disclosure {
  margin-top: var(--space-lg);
}

.disclosure summary {
  width: fit-content;
  font-weight: bold;
  cursor: pointer;
}

.disclosure p {
  max-width: 40rem;
  margin: var(--space-sm) 0 0;
  color: var(--color-muted);
}

/*
 * Dream Gen's picture: transparent lineart layers stacked in one grid cell, in source order
 * (bottom first). They all share one canvas size, so the box takes that size, capped at the
 * page width.
 */
.dream-preview {
  display: grid;
  width: fit-content;
  max-width: 100%;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  overflow: hidden;
}

.dream-preview img {
  grid-area: 1 / 1;
  display: block;
  max-width: 100%;
  height: auto;
}

/* Starts one line tall, like the inputs beside it; drag to see more. */
.form-grid .field textarea {
  resize: vertical;
}

@media (max-width: 36rem) {
  .form-grid {
    grid-template-columns: 1fr;
  }
}

/* A titled set of related fields (a fieldset). */
.field-group {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: none;
}

.field-group legend {
  margin-bottom: var(--space-xs);
  padding: 0;
  font-weight: bold;
}

/* A short form on one line: its field, then its button. */
.inline-form {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

.inline-form .field {
  flex: 0 1 16rem;
  margin: 0;
}

/* Tabs that are links to separate pages; the current one is marked aria-current. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin: 0 0 var(--space-md);
  padding: 0;
  border-bottom: 1px solid var(--color-border);
  list-style: none;
}

.tab {
  display: block;
  margin-bottom: -1px;
  padding: var(--space-sm) var(--space-md);
  border-bottom: 2px solid transparent;
  color: var(--color-muted);
  text-decoration: none;
}

.tab:hover {
  color: var(--color-text);
}

.tab[aria-current='page'] {
  border-bottom-color: var(--color-accent);
  color: var(--color-accent);
  font-weight: bold;
}

/* A Lore section with a picture: its card and the picture side by side, each at its own
   height and centred on the other, so the picture never stretches the card. The picture is on
   the right in one row and the left in the next. */
.lore-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 18rem);
  gap: var(--space-lg);
  align-items: center;
  margin: var(--space-md) 0;
}

.lore-row:nth-of-type(even) {
  grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
}

.lore-row:nth-of-type(even) .lore-figure {
  order: -1;
}

.lore-figure img {
  display: block;
  width: 100%;
  border-radius: var(--radius-soft);
  background: var(--color-surface);
  box-shadow: 0 5px 0 var(--color-border);
}

/* Too narrow for two columns: the picture goes above its card, no wider than it was. */
@media (max-width: 48rem) {
  .lore-row,
  .lore-row:nth-of-type(even) {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-md);
  }

  .lore-figure {
    order: -1;
    width: min(100%, 18rem);
    margin: 0 auto;
  }
}

/* Tabs within a tab: a row of small pills under the tab's intro, the current one filled in. */
.subtabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin: calc(var(--space-lg) * 0.75) 0 var(--space-md);
  padding: 0;
  list-style: none;
}

/* On a highlighted tile in the pool (framed like a pinned one): a gold tag over the picture's
   top corner, "Glamour" or "Special theme". */
.tile-tag {
  position: absolute;
  top: var(--space-sm);
  left: var(--space-sm);
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 0.1rem 0.6rem;
  border: 2px solid var(--color-leader);
  border-radius: 999px;
  background: var(--color-surface);
  color: var(--color-leader);
  font-size: var(--font-size-small);
  font-weight: bold;
}

/* A special event Fauri's tile in the pool: framed like a pinned one, but in a soft lavender, with a
   tag to match. */
.tile-themed {
  position: relative;
}

.tile-themed .tile-photo,
.tile-themed .tile-image {
  box-shadow:
    0 0 0 3px var(--color-event-frame),
    0 5px 0 3px var(--color-event-shadow);
}

.tile-tag-event {
  border-color: var(--color-event-frame);
  color: var(--color-event);
}

/* A tag with more to say (the theme) shows it just under itself while its tile is rolled
   over or focused. */
.tile-tag[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + var(--space-xs));
  left: 0;
  z-index: 1;
  width: max-content;
  max-width: 12rem;
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius);
  background: var(--color-header-bg);
  color: var(--color-header-text);
  text-align: left;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.tile:hover .tile-tag[data-tip]::after,
.tile:focus-visible .tile-tag[data-tip]::after {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .tile-tag[data-tip]::after {
    transition: none;
  }
}

.subtab {
  display: block;
  padding: 0.15rem 0.9rem;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  color: var(--color-muted);
  font-size: var(--font-size-small);
  font-weight: bold;
  text-decoration: none;
}

.subtab:hover {
  color: var(--color-text);
}

.subtab[aria-current='page'] {
  border-color: var(--color-accent);
  background: var(--color-accent);
  color: var(--color-accent-contrast);
}

/* The admin's stats: a label column, then right-aligned numbers. */
.stats-table {
  margin-bottom: var(--space-md);
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.stats-table th,
.stats-table td {
  padding: var(--space-xs) var(--space-md);
  border-bottom: 1px solid var(--color-border);
  text-align: right;
}

.stats-table th:first-child {
  padding-left: 0;
  text-align: left;
  overflow-wrap: anywhere;
}

.stats-table tbody th {
  font-weight: normal;
}

.error {
  color: var(--color-error);
}

.success {
  color: var(--color-success);
}

.muted {
  color: var(--color-muted);
}
