/* The fairy-book template's design system.
 *
 * Structure lives here; a book's palette recolours only the identity layer, and
 * arrives as custom properties on :root (light) with the same tokens' dark
 * variants swapped in under prefers-color-scheme. Every colour below is a token,
 * so a book that declares a new palette needs no CSS at all.
 *
 * Dark mode is a first-class mode, not an inversion: period line art sits on a
 * paper-tone card in BOTH modes, so dark mode dims the frame and never the
 * engraving. */

:root {
  --measure: 34rem;
  --serif: Iowan Old Style, Palatino Linotype, Palatino, Georgia, serif;
  --sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
  --bg: var(--paper);
  --fg: var(--ink);
  --muted: color-mix(in srgb, var(--ink) 62%, var(--paper));
  --edge: var(--rule);
  --tinted: var(--tint);
  --card: color-mix(in srgb, var(--paper) 88%, var(--accent));
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: var(--night);
    --fg: var(--moon);
    --muted: color-mix(in srgb, var(--moon) 62%, var(--night));
    --edge: var(--rule-dark);
    --tinted: var(--tint-dark);
    --card: var(--wash-dark);
  }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: 1.0625rem/1.65 var(--serif);
}
a { color: var(--tinted); text-underline-offset: 0.15em; }
a:focus-visible, button:focus-visible, input:focus-visible {
  outline: 3px solid var(--tinted); outline-offset: 2px;
}
.skip { position: absolute; left: -9999px; }
.skip:focus { left: 1rem; top: 1rem; background: var(--bg); padding: .5rem 1rem; z-index: 9; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

header.site, footer.site {
  display: flex; flex-wrap: wrap; gap: 1rem; align-items: baseline;
  padding: 1rem clamp(1rem, 4vw, 3rem);
  border-bottom: 1px solid var(--edge);
  font-family: var(--sans); font-size: .9375rem;
}
footer.site { border-bottom: 0; border-top: 1px solid var(--edge); margin-top: 4rem; }
.brand { font-weight: 600; text-decoration: none; letter-spacing: .01em; }
.site-nav { display: flex; gap: 1rem; align-items: baseline; margin-left: auto; flex-wrap: wrap; }
.search { position: relative; }
.search input {
  font: inherit; padding: .3rem .6rem; border: 1px solid var(--edge);
  border-radius: 999px; background: var(--bg); color: var(--fg); min-width: 9rem;
}
#results {
  position: absolute; right: 0; top: 2.2rem; z-index: 5; margin: 0; padding: .25rem;
  list-style: none; background: var(--bg); border: 1px solid var(--edge);
  border-radius: .5rem; max-width: 22rem; box-shadow: 0 6px 24px rgba(0,0,0,.12);
}
#results li a { display: block; padding: .35rem .6rem; text-decoration: none; }
#results li a:hover { background: var(--card); }

main { padding: clamp(1.5rem, 5vw, 4rem) clamp(1rem, 4vw, 3rem); }
h1, h2, h3 { line-height: 1.2; font-weight: 600; }
.shelf-title, .hub h1, .unit h1, .page h1 { max-width: var(--measure); }
.standfirst { color: var(--muted); max-width: var(--measure); }

/* --- the shelf: the rainbow
 *
 * TWO COLUMNS from the narrowest screen up, not one. `minmax(13rem, 1fr)` needs
 * 26rem plus the gap before a second card fits, so every phone got one book per
 * screen: a 2:3 cover at full width, then a scroll, then the next one. A shelf
 * that shows one book is not a shelf, and the whole point of this series is
 * seeing the colours side by side.
 *
 * An explicit 2 up to 30rem, then auto-fill takes over for real estate that can
 * actually hold a third. The gap tightens on small screens too, because 2rem
 * between two cards on a 390px viewport is most of a card. */
.shelf { list-style: none; padding: 0; display: grid; gap: 1rem;
  grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 30rem) {
  .shelf { gap: 2rem; grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); }
}
.shelf-card a { text-decoration: none; color: inherit; display: block; }
.shelf-name { display: block; margin-top: .75rem; font-weight: 600; }
.shelf-blurb { color: var(--muted); font-size: .9375rem; }
.cover {
  display: grid; align-content: center; gap: .75rem; aspect-ratio: 2 / 3;
  width: 100%; border-radius: 3px; padding: 1.25rem; text-align: center;
  background: var(--accent); color: var(--on-accent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--deep) 55%, transparent),
              0 2px 10px rgba(0,0,0,.18);
}
img.cover { object-fit: cover; padding: 0; }
.cover-title { font-size: 1.35rem; line-height: 1.15; }
.cover-year { font-family: var(--sans); font-size: .8125rem; letter-spacing: .12em; }
.cover-rule { height: 1px; background: currentColor; opacity: .55; }

/* --- a book hub
 *
 * The head is a TIGHT stack. It was a 1rem grid gap around a 12rem cover with an
 * empty standfirst paragraph inside it, which read as a cover marooned in the
 * middle of the page. The renderer stopped emitting the empty paragraph; this
 * closes the rest, and pulls the cover in so the title sits with it rather than
 * a screen below it. */
.hub-head { display: grid; gap: .5rem; max-width: 46rem; justify-items: start; }
.hub-head .cover { max-width: 9rem; margin-bottom: .25rem; }
.hub-head h1 { margin: 0; }
.hub-head .standfirst, .hub-head .title-page { margin: 0; }
.title-page { font-style: italic; color: var(--muted); }

/* The contents list.
 *
 * Was a browser-numbered <ol> of bare links, each with the story's full
 * bibliographic source underneath: thirty-seven underlined lines interleaved
 * with thirty-seven citations, which is a bibliography, not a table of contents.
 * The credit moved to the story page (where it already was) and the number
 * became an authored element so it can hold its own gutter.
 *
 * Rows rather than list items: a hairline between entries, the number set in the
 * book's own colour, and no underline until hover or focus, so the eye reads a
 * column of TITLES instead of a column of links. */
.toc { max-width: var(--measure); padding-left: 0; list-style: none; margin: 1rem 0 0; }
.toc li { margin: 0; border-top: 1px solid var(--edge); }
.toc li:last-child { border-bottom: 1px solid var(--edge); }
.toc li a {
  display: grid; grid-template-columns: 2.25rem 1fr; align-items: baseline;
  gap: .5rem; padding: .7rem .25rem; text-decoration: none; color: inherit;
}
.toc-n {
  font-family: var(--sans); font-size: .8125rem; font-variant-numeric: tabular-nums;
  color: var(--tinted); text-align: right; padding-top: .15em;
}
.toc-t { text-decoration: underline; text-decoration-color: transparent;
  text-underline-offset: .15em; transition: text-decoration-color .12s ease; }
.toc li a:hover .toc-t, .toc li a:focus-visible .toc-t { text-decoration-color: currentColor; }
.toc li a:hover { background: var(--card); }

.in-book, .matter-role {
  display: block; font-family: var(--sans); font-size: .8125rem; color: var(--muted);
}
.matter { max-width: var(--measure); }
.matter-role { text-transform: uppercase; letter-spacing: .1em; }

/* --- the spine: the book's colour, down the page
 *
 * Only on a page that belongs to ONE book (body.has-accent). The shelf and the
 * indexes belong to all of them and get none.
 *
 * Fixed rather than a border on the article, so it runs the whole height of the
 * viewport and stays there through a long tale, which is what "all the way down
 * the page" means for a story that is nine screens long. At left: 0 with no
 * width contribution it cannot cause horizontal overflow, and the page's own
 * padding (min 1rem) keeps text well clear of it. */
body.has-accent::before {
  content: ""; position: fixed; left: 0; top: 0; bottom: 0; width: 5px;
  /* --tinted, not --accent. The raw cover colour is mixed for a LIGHT ground, so
     on the night ground a deep navy at this width is technically present and
     visually absent: measured at rgb(29,63,143) against rgb(21,20,24), which is
     a stripe you have to be told about. --tinted is the same hue resolved per
     mode (identical to the accent in light, lightened in dark), which is exactly
     the job. */
  background: var(--tinted); z-index: 10; pointer-events: none;
}

/* --- a unit */
.unit { max-width: var(--measure); }
.breadcrumb { font-family: var(--sans); font-size: .875rem; margin: 0 0 .5rem; }
.unit .credit { color: var(--muted); font-style: italic; margin: .35rem 0 0; }

/* The title block: a small cover, then the title. The cover is what tells a
   reader which of the coloured books they are in without making them read the
   breadcrumb. */
.unit-title { display: grid; grid-template-columns: 3.25rem 1fr; gap: .9rem; align-items: start; }
.unit-title h1 { margin: 0; }
.unit-cover .cover {
  width: 3.25rem; padding: .3rem; gap: .2rem; border-radius: 2px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--deep) 55%, transparent),
              0 1px 4px rgba(0,0,0,.22);
}
/* The composed cover's lettering does not survive being shrunk to a thumbnail:
   at this size it is a coloured spine, so the type comes out and the rules
   stay. */
.unit-cover .cover-title, .unit-cover .cover-year { display: none; }
.unit-cover .cover-rule { opacity: .8; }
.unit-cover img.cover { height: auto; aspect-ratio: 2 / 3; }
/* Not on a poem: a drop cap belongs to the opening of prose, and :first-of-type
   would otherwise hand one to a tale that opens on a rhyme. */
.text > p:not(.verse):first-of-type::first-letter {
  float: left; font-size: 3.1em; line-height: .82; padding: .06em .08em 0 0;
  color: var(--tinted);
}
.text p { margin: 0 0 1.15em; }

/* --- verse. The line is the unit, and the indent is meaning: a ballad's
   alternating indent is how the printed page says it is a ballad. */
.text p.verse { margin: 1.6em 0; font-style: italic; }
/* The <br>s are the no-CSS fallback; with the stylesheet the spans are already
   blocks and the breaks would double. */
.verse br { display: none; }
.verse .vl { display: block; text-indent: -1.4em; padding-left: 1.4em; }
.verse .vs { display: block; height: .8em; }
.verse .vl-1 { padding-left: 2.8em; }
.verse .vl-2 { padding-left: 4.2em; }
.verse .vl-3 { padding-left: 5.6em; }
.verse .vl-4 { padding-left: 7em; }
.editions { display: flex; gap: .5rem; font-family: var(--sans); font-size: .875rem; margin: 1rem 0 2rem; }
.edition { padding: .25rem .7rem; border: 1px solid var(--edge); border-radius: 999px; text-decoration: none; }
.edition.current { background: var(--accent); color: var(--on-accent); border-color: transparent; }

.plate { margin: 2rem 0; padding: .75rem; background: var(--paper); border-radius: 2px;
  box-shadow: 0 1px 6px rgba(0,0,0,.2); }
/* A vignette is the small decorative cut, not a full-page illustration, and it
   sits inside the measure rather than filling it. A hero opens the page. */
.plate-vignette { max-width: 16rem; margin-inline: auto; }
.plate-hero { margin-top: 0; }
.plate img { width: 100%; height: auto; display: block; }
.plate figcaption { font-family: var(--sans); font-size: .8125rem; color: #55504a; margin-top: .5rem; }
.plate .credit { display: block; opacity: .8; }

/* A DECLARED PLATE WITH NO IMAGE YET. The text knows where every illustration
   goes and what it shows; the files arrive separately and later. So this is the
   normal state of a freshly ingested volume, not a broken one, and it reads as
   a place held open: the printed caption, on the paper, in the plate's own
   slot. It gets no drop shadow, because nothing is sitting on the page yet. */
.plate-stub { box-shadow: none; border: 1px dashed var(--edge); background: none;
  display: flex; align-items: center; justify-content: center; min-height: 5rem;
  padding: 1.25rem; }
.plate-stub figcaption { margin-top: 0; text-align: center; font-style: italic;
  max-width: 28rem; }

.footnotes { margin-top: 3rem; border-top: 1px solid var(--edge); font-size: .9375rem; }
.footnotes-head { font-family: var(--sans); font-size: .8125rem; text-transform: uppercase; letter-spacing: .1em; }
.fn-ref a { text-decoration: none; padding: 0 .15em; }
.fn-back { text-decoration: none; }

.commentary {
  margin-top: 3rem; padding: 1.25rem 1.5rem; background: var(--card);
  border-left: 3px solid var(--accent); border-radius: 0 4px 4px 0;
}
.commentary h1, .commentary h2 { font-family: var(--sans); font-size: 1rem;
  text-transform: uppercase; letter-spacing: .08em; margin-top: 0; }
.colophon-block { margin-top: 4rem; font-size: .875rem; color: var(--muted); max-width: var(--measure); }

.tags { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: 2.5rem; }
.tag { font-family: var(--sans); font-size: .8125rem; padding: .2rem .6rem;
  border: 1px solid var(--edge); border-radius: 999px; text-decoration: none; }
/* The pager: three fixed cells, so backwards is always on the left and forwards
 * is always on the right whether or not either exists. A flex row that dropped
 * an absent cell used to slide the remaining ones sideways, which is how "next"
 * ended up wherever there was room. */
.unit-nav {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: .75rem;
  margin-top: 3rem; padding-top: 1.25rem; border-top: 1px solid var(--edge);
  font-family: var(--sans); align-items: start;
}
.pager { display: flex; flex-direction: column; gap: .15rem; text-decoration: none;
  color: inherit; padding: .5rem .25rem; border-radius: 4px; min-width: 0; }
.pager.empty { padding: 0; }
.pager.prev { text-align: left; }
.pager.random { text-align: center; align-items: center; }
.pager.next { text-align: right; align-items: flex-end; }
.pager-role {
  font-size: .8125rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--tinted); white-space: nowrap;
}
/* The chevrons live in CSS, not in the text, so a screen reader reads
   "Previous" and not "left angle bracket Previous". */
.pager.prev .pager-role::before { content: "\2039\00a0"; }
.pager.next .pager-role::after { content: "\00a0\203a"; }
.pager-title {
  font-size: .9375rem; color: var(--muted); line-height: 1.3;
  overflow-wrap: anywhere;
}
.pager:hover { background: var(--card); }
.pager:hover .pager-title { color: var(--fg); }
/* The shuffle is the one cell that is an ACTION rather than a place, so it is
   drawn as a control instead of a signpost. */
.pager.random { border: 1px solid var(--edge); }
.pager.random .pager-role { color: var(--fg); }

/* --- indexes */
.a-to-z, .tag-list, .tag-cards { list-style: none; padding: 0; }
.a-to-z li { margin: .3rem 0; }
.a-to-z .letter { font-family: var(--sans); font-size: .8125rem; letter-spacing: .12em;
  color: var(--muted); margin-top: 1.5rem; }
.tag-list { display: flex; flex-wrap: wrap; gap: .5rem; }
.tag-list a { display: inline-flex; gap: .4rem; align-items: baseline;
  border: 1px solid var(--edge); border-radius: 999px; padding: .25rem .75rem;
  text-decoration: none; font-family: var(--sans); font-size: .875rem; }
.tag-list .count { color: var(--muted); font-size: .75rem; }
.tag-cards { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
.tag-card { border-left: 4px solid var(--accent); padding-left: .9rem; }
.tag-card a { text-decoration: none; color: inherit; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* --- touch targets
 *
 * A tale's title in the contents list renders as a 19px inline link, and a book
 * hub is 37 of them stacked. On a phone that is a column of neighbours a thumb
 * cannot separate, and mis-tapping into the wrong story is the one navigation
 * failure a reader cannot work around: they do not know which story they meant
 * to open until they have lost it. Measured at 390px before this existed: 35
 * targets under 24px on the hub, 36 on the A-to-Z.
 *
 * Widened only where the pointer is COARSE. A mouse is precise at 19px and a
 * dense list is easier to scan, so paying for touch on every device would make
 * the desktop worse to fix something desktop does not have.
 */
@media (pointer: coarse) {
  /* .toc is deliberately absent. Its rows are already a full-width grid with
   * .7rem of padding, and the `display: block` this block used to apply to them
   * flattened that grid back into a stack, collapsing the number gutter on
   * exactly the devices the rule exists to serve. A rule that only fires on
   * touch is a rule nobody sees break. */
  .a-to-z li a,
  .matter a { display: block; padding: .5rem 0; }
  .a-to-z li { margin: 0; }
}
