/* Generated by tools/brand/fetch-fonts.mjs. Do not edit by hand.
   Regenerate with: node tools/brand/fetch-fonts.mjs */

@font-face {
  font-family: 'Playfair';
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url('/fonts/playfair-latin-ext-italic.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Playfair';
  font-style: italic;
  font-weight: 400 500;
  font-display: swap;
  src: url('/fonts/playfair-latin-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Playfair';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/playfair-latin-ext-normal.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Playfair';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/playfair-latin-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Source Serif 4';
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url('/fonts/source-serif-4-latin-ext-italic.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Source Serif 4';
  font-style: italic;
  font-weight: 300 700;
  font-display: swap;
  src: url('/fonts/source-serif-4-latin-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/fonts/source-serif-4-latin-ext-normal.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/fonts/source-serif-4-latin-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/inter-latin-ext-normal.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('/fonts/inter-latin-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Literata';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/literata-latin-ext-italic.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Literata';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/literata-latin-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/literata-latin-ext-normal.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Literata';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/literata-latin-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'EB Garamond';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/eb-garamond-latin-ext-italic.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'EB Garamond';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/eb-garamond-latin-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/eb-garamond-latin-ext-normal.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/fonts/eb-garamond-latin-normal.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Scheherazade New';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/scheherazade-new-arabic-normal-400.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}

@font-face {
  font-family: 'Noto Nastaliq Urdu';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/noto-nastaliq-urdu-arabic-normal-400.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}

@font-face {
  font-family: 'Honorifics';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/honorifics.woff2') format('woff2');
}

@font-face {
  font-family: 'OpenDyslexic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/opendyslexic-normal.woff2') format('woff2');
}

@font-face {
  font-family: 'OpenDyslexic';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/opendyslexic-italic.woff2') format('woff2');
}

@font-face {
  font-family: 'OpenDyslexic';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/opendyslexic-bold.woff2') format('woff2');
}

/* ==========================================================================
   site.css - the public side of The Muslim Page.
   ==========================================================================

   THIS IS THE SECOND REDESIGN OF 19 AUGUST 2026, and it is a rewrite rather
   than a revision. The first one kept the live site's kit and gave it new
   motion; Aamir said so, and lifted the constraint that had been holding the
   kit in place, in these words: do not follow any of the previous site's CSS
   guidelines, use new colours, new format, full redesign as you wish.

   So nothing below is carried across. There are no gold ruled labels, no pill
   buttons and no bands of alternating ground colour. Sections are numbered and
   ruled off with a single hairline, and the type does the work that ornament
   used to do.

   THE PALETTE WAS CHANGED AGAIN LATER THE SAME DAY, and only the palette:
   Aamir said the colours were not it and the design was fine. It was a near
   black with a trace of green; it is now a deep midnight blue with warm ivory
   on it and one soft amber accent. Every rule below was already reading a
   token, so the whole change is the block that follows plus the four places
   that had a value written out rather than referenced, and those four are now
   tokens as well so the next one is smaller still.

   THE COVERS ARE THE COLOUR. That is the one idea the whole scheme rests on.
   Nine pieces of commissioned artwork in nine different palettes cannot all be
   framed by a site with a loud palette of its own without something clashing
   somewhere. A ground this dark and this unsaturated has almost nothing to
   clash with, and dark rather than light because a cover is a lit object: on
   this ground it reads as an illuminated thing on a shelf, and on parchment it
   reads as a sticker.

   This is NOT the reading stylesheet. Chapter content gets its own scoped
   rules when it arrives, because the reader's font scale must apply to the
   content region and to nothing else, and because a chapter is read on a light
   ground whatever the shell around it does. On the live site that separation is
   made by excluding four page identifiers from every rule, which is fragile;
   here the two never meet in the first place.

   Every colour is a token and every rule uses the token. That has not changed
   and must not, and this file is now the proof of it twice over: the loading
   animation has been repointed through two complete palettes, forest and gold
   to ivory and amber, without one line of the drawing being touched.

   MOTION, AND THE THREE KINDS OF IT. This stylesheet moves things in three
   ways, they fail in three different ways, and so they are kept apart:

     1. TRANSITIONS on hover and focus. Universal, and nothing depends on them.
     2. REVEAL ON SCROLL, driven by a class the script adds. Section 14. If the
        script never runs, nothing is ever hidden, so there is nothing to
        reveal and the page is simply there.
     3. SCROLL DRIVEN ANIMATION, driven by the browser's own scroll timeline
        with no script at all. Section 15. Every one of these sits behind a
        support query AND behind a reduced-motion query, and every one has a
        resting state that is correct on its own.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* THE GROUND. Deep midnight blue. Three steps only: the page, a raised
     surface, and the box a cover sits in before its image arrives. A dark
     scheme goes wrong when it has eight greys, because none of them is far
     enough from the next to read as a decision.

     IT IS A COLOUR AND NOT A GREY, and that is the change of 19 August 2026:
     the ground was a near black with a trace of green in it, and Aamir said
     the colours were not it while the design was. A blue this deep still does
     everything the near black did for the covers, and it reads as a night
     rather than as an absence, which is the whole difference. */
  --ink: #0e1424;
  --ink-raised: #151d31;
  --ink-hover: #1f2942;

  /* The ground again at two alpha values, so the header can arrive over the
     hero without either of them being written out as a literal in a keyframe
     where nothing would ever find it again. */
  --ink-veil: rgba(14, 20, 36, 0.72);
  --ink-veil-0: rgba(14, 20, 36, 0);

  /* THE INK ON IT. A warm ivory rather than white, for two reasons. Pure
     white on a very dark ground reliably produces halation, where the type
     looks as though it is vibrating. And a warm ivory against a cool ground is
     the pairing that makes both of them look deliberate: two neutrals of the
     same temperature read as a printing accident.

     The secondary tone leans the other way, into the blue, so that it recedes
     into the ground rather than sitting on top of it as a grey would.

     Measured on --ink: --bone is 16.0:1 and --bone-dim is 7.9:1, so both clear
     AA at any size and AAA as well. */
  --bone: #f2efe6;
  --bone-dim: #a3aab6;
  --bone-faint: rgba(242, 239, 230, 0.45);

  /* THE ONE ACCENT, for section names, categories, numerals and the reading
     progress line. A soft amber: warm, low enough in saturation to sit beside
     nine different cover palettes without picking a side, and far enough from
     the brand gold it succeeds that it does not read as the old scheme in a
     new place. On a blue ground a warm accent is the only one that does any
     work; a cool one disappears into the page. 8.9:1 on the ground, so it
     carries small text with no qualification at all. */
  --warm: #e0a878;

  /* THE ONE STATE COLOUR, and it is added grudgingly. Everything else on this
     site is ink, bone and the one amber, and the rule in DESIGN-CONSISTENCY.md
     section 5.1 is that a strong colour does not go back into the shell.

     A form is the exception the rule did not cover. A field that has been
     refused has to be findable at a glance among five others, and marking it
     in the same amber that already carries every section name and every
     category means the mark says nothing: the reader has been trained by four
     screens of it that amber is decoration. A soft rose is far enough from the
     amber to read as different at a glance and desaturated enough not to shout
     on this ground.

     It appears in exactly two places, both on the contact form: the border of
     a refused field and the sentence under it. THE COLOUR IS NEVER THE MESSAGE
     on its own, because about one man in twelve cannot tell these two hues
     apart; the sentence under the field is what says what is wrong.

     9.4:1 on the ground and 8.6:1 on a raised section, so it carries small
     text with no qualification. */
  --warn: #eda9a2;

  /* Hairlines. Two weights, both drawn in the page's own light rather than in
     a grey, so they sit ON the ground instead of being a stripe of a different
     colour laid over it. The third is the same line at nothing, for the header
     arriving over the hero. */
  --line: rgba(242, 239, 230, 0.14);
  --line-soft: rgba(242, 239, 230, 0.07);
  --line-0: rgba(242, 239, 230, 0);

  /* THE FACES, AND THE ONE RULE ABOUT THEM.

     Inter carries the whole site: the wordmark, the menu, the headlines, the
     titles, the metadata, the actions. Source Serif carries THE WORDS OF THE
     BOOKS and nothing else, which on this page means the quoted passage and
     the one transliterated book title, and which will mean the chapters when
     chapters exist.

     That is a different rule from the one this site used to keep, and a
     sharper one. The old rule was 'serif for anything read, sans for anything
     operated', which put the site's own headings in a display serif and left
     the line somewhere in the middle of the page. This one puts the line
     exactly where the AUTHORSHIP changes: the serif means you are reading a
     book, and everything the site says about itself is set in the other face.
     A serif heading is now as much a bug as a sans passage, and the shell
     tests assert both halves of it.

     Playfair is no longer used by any of this. It stays declared in fonts.css
     and stays the intended face for a chapter title, which is a book's words
     rather than the site's. It is no longer preloaded, because a preloaded
     file the page does not use costs more than it saves. */
  --ui: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --read: 'Source Serif 4', Georgia, 'Times New Roman', serif;

  /* Layout. --wrap is the outer measure for site furniture; --column is the
     narrow measure for anything meant to be read as prose. */
  --wrap: 78rem;
  --column: 34rem;
  --gutter: clamp(1.25rem, 5vw, 3.5rem);

  /* One vertical rhythm for every section, so the page has a beat. */
  --band: clamp(4.5rem, 10vw, 9rem);

  /* The header's height, stated once. Two separate things have to clear it:
     the hero is laid out under a header that floats over it, and anything
     sticky stops below it. A height written down in one place and cleared in
     another drifts apart the first time either is touched. */
  --head: 4.5rem;

  /* One curve for anything that ARRIVES, one for anything that merely
     responds. The first decelerates hard at the end, which is what makes a
     reveal read as considered rather than as a fade. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);

  /* On ink a cover needs a LIFT rather than a drop shadow: a dark shadow on a
     dark ground is nothing at all, so what separates the artwork from the page
     is a hairline of its own light, with a real shadow appearing only once it
     moves. */
  --cover-edge: 0 0 0 1px rgba(242, 239, 230, 0.09);
  --cover-lift: 0 0 0 1px rgba(242, 239, 230, 0.16), 0 30px 60px rgba(0, 0, 0, 0.6);
}

/* --------------------------------------------------------------------------
   2. Reset and base
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  background: var(--ink);
  /* An in-page jump lands softly rather than teleporting. The reduced-motion
     block in section 14 turns it off, which is what makes it safe to set. */
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--bone);
  font-family: var(--ui);
  font-size: 16px;
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Several things here are deliberately allowed to tilt or drift out of their
     own column. None of them may turn into a sideways scrollbar.

     CLIP, NOT HIDDEN, and the difference is the whole reason this needs a
     comment. `overflow-x: hidden` would make the body a scroll container, and
     a scroll container is exactly what `position: sticky` measures itself
     against: everything sticky on the site would quietly stop sticking, on
     every page, with nothing to say why. `clip` cuts the overflow off without
     creating one. A browser that does not know the value ignores the line,
     which leaves things as they were before it was added. */
  overflow-x: clip;
}

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

a {
  color: inherit;
}

h1,
h2,
h3,
h4 {
  font-family: var(--ui);
  margin: 0;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

p {
  margin: 0;
}

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

/* A visible focus ring on every interactive thing. Bone, because on this
   ground the browser's default blue all but disappears, and because bone is
   the one colour on the site that is never used decoratively. */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 4px;
  border-radius: 1px;
}

.tmp-skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--bone);
  color: var(--ink);
  padding: 0.75rem 1.25rem;
  font-size: 0.875rem;
  font-weight: 600;
}

.tmp-skip:focus {
  left: 0.5rem;
  top: 0.5rem;
}

/* Present for screen readers, absent on screen. */
.tmp-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   3. Measure

   The banded grounds are gone. There is ONE ground for the whole site, so a
   section no longer declares a colour, only its padding and its measure.
   `.tmp-raised` is the single exception and lifts a section by one step, which
   is used once, for the passage.
   -------------------------------------------------------------------------- */

.tmp-band {
  padding-inline: var(--gutter);
}

.tmp-wrap {
  max-width: var(--wrap);
  margin-inline: auto;
}

.tmp-pad {
  padding-block: var(--band);
}

.tmp-raised {
  background: var(--ink-raised);
}

/* --------------------------------------------------------------------------
   4. Type

   Three sizes carry the whole page: the headline, the section heading and the
   work title. Everything else is metadata at one of two small sizes. A near
   monochrome scheme has no colour to make hierarchy with, so the hierarchy has
   to be in the size and the weight, and the steps between them are large on
   purpose.
   -------------------------------------------------------------------------- */

/* THE HEADLINE IS SIZED FROM ITS COLUMN, NOT FROM THE VIEWPORT, and that is
   what keeps it on the two lines it is written as.

   The break after 'Islamic works' is in the markup because Aamir wrote it as
   two lines. The second line, 'Translated into English', is the longer of the
   two, and whether it fits on one line depends on how wide its COLUMN is, not
   on how wide the window is. Those two part company at 74rem, where the hero
   becomes two columns and the text loses 24rem of its width to the covers in
   one step: sized in vw the headline would be at its largest exactly where its
   column had just become narrowest, and the second line would break in a
   different place at every width.

   `cqi` is one per cent of the container's inline size, so this tracks the
   column itself and the break stays where it was put. 8.6 is the largest value
   at which the longest line still fits, measured rather than guessed, with a
   little left over for a face that renders a shade wider.

   The vw line above it is not a leftover. A browser that does not understand
   `cqi` throws the whole declaration away, so the first one is what it keeps,
   and it is deliberately a size at which the line wraps tidily rather than one
   that is nearly right. On a phone the wrap is unavoidable at any readable
   size and the floor takes over: three lines there is correct. */
.tmp-h1 {
  font-size: clamp(2.5rem, 7.4vw, 4.25rem);
  font-size: clamp(2.5rem, 8.6cqi, 5.75rem);
  font-weight: 700;
  line-height: 0.96;
  letter-spacing: -0.038em;
}

.tmp-h2 {
  font-size: clamp(2rem, 5.2vw, 3.5rem);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: -0.03em;
}

/* The small caption over a headline. It replaces the old gold label with its
   leading rule: no rule, no gold, and set in the warm taupe at a size and a
   tracking that read as a caption rather than as a heading that failed. */
.tmp-eyebrow {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--warm);
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}

/* --------------------------------------------------------------------------
   5. Section heads

   Every section opens the same way: one hairline across the full measure, the
   section's number and name under it in the warm taupe, and the heading below.
   The number is what makes them read as a sequence rather than as three
   unrelated blocks. It is an aria-hidden span in the markup rather than a list
   counter, because these are sections and not a list.
   -------------------------------------------------------------------------- */

.tmp-sec {
  border-top: 1px solid var(--line);
  padding-top: 1.25rem;
  margin-bottom: clamp(2.5rem, 6vw, 4.5rem);
  display: grid;
  gap: 1.25rem 3rem;
}

@media (min-width: 48rem) {
  .tmp-sec {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
  }

  .tmp-sec-name {
    grid-column: 1 / -1;
  }
}

.tmp-sec-name {
  display: flex;
  align-items: baseline;
  gap: 0.875rem;
  font-family: var(--ui);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--warm);
  margin: 0;
}

.tmp-sec-n {
  font-variant-numeric: tabular-nums;
  color: var(--bone-faint);
  letter-spacing: 0.1em;
}

/* --------------------------------------------------------------------------
   6. Actions and links

   The pill buttons are gone. The primary action is a hard edged bone slab that
   inverts on hover; the secondary is a label in a hairline box. Both are
   square, because a corner radius is the single strongest signal of the kit
   this design replaces.
   -------------------------------------------------------------------------- */

.tmp-act {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1;
  padding: 1.125rem 1.75rem;
  text-decoration: none;
  border: 1px solid transparent;
  transition:
    background-color 0.3s var(--ease-soft),
    color 0.3s var(--ease-soft),
    border-color 0.3s var(--ease-soft);
}

.tmp-act svg {
  flex: none;
  transition: transform 0.4s var(--ease);
}

.tmp-act:hover svg {
  transform: translateX(5px);
}

.tmp-act-solid {
  background: var(--bone);
  color: var(--ink);
}

.tmp-act-solid:hover {
  background: transparent;
  color: var(--bone);
  border-color: var(--bone);
}

.tmp-act-plain {
  color: var(--bone-dim);
  border-color: var(--line);
}

.tmp-act-plain:hover {
  color: var(--bone);
  border-color: var(--bone-faint);
}

/* An onward text link: a label over a hairline that a bone one grows across,
   and an arrow that steps forward. */
.tmp-onward {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--bone-dim);
  text-decoration: none;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--line);
  transition: color 0.3s var(--ease-soft);
}

.tmp-onward::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -1px;
  width: 100%;
  height: 1px;
  background: var(--bone);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.5s var(--ease);
}

.tmp-onward:hover {
  color: var(--bone);
}

.tmp-onward:hover::after,
.tmp-onward:focus-visible::after {
  transform: scaleX(1);
}

.tmp-onward svg {
  flex: none;
  transition: transform 0.4s var(--ease);
}

.tmp-onward:hover svg {
  transform: translateX(5px);
}

/* --------------------------------------------------------------------------
   7. Site header

   In view at all times, because the library is the thing every visitor is
   looking for and it should never be more than one glance away.

   IT HAS TWO MODES. Everywhere it is a bar in the normal flow with a hairline
   under it. On the front page it FLOATS OVER the hero, so nothing cuts across
   the opening screen, and it settles into the bar as the reader scrolls off.
   That settle is the browser's own scroll timeline, in section 15, with no
   script involved.

   It is far simpler than the version it replaces, and that is the dividend of
   one ground for the whole site. The old header had to cross fade four
   registered colour properties from a light-on-dark set to a dark-on-light
   set, because it floated over a green hero and landed on parchment. Here the
   type is bone in both states and only the ground arrives behind it, so it is
   two ordinary properties and no @property at all.
   -------------------------------------------------------------------------- */

.tmp-header {
  position: sticky;
  top: 0;
  z-index: 50;
  padding-inline: var(--gutter);
  background: var(--ink);
  border-bottom: 1px solid var(--line);
}

@supports (backdrop-filter: blur(1px)) {
  .tmp-header {
    background: var(--ink-veil);
    backdrop-filter: blur(16px) saturate(1.3);
  }
}

/* The floating mode. Fixed rather than sticky, because a sticky element still
   occupies its space in the flow and the hero has to start at the very top of
   the page. */
.tmp-header-overlay {
  position: fixed;
  inset-inline: 0;
  top: 0;
  background: transparent;
  border-bottom-color: transparent;
  backdrop-filter: none;
}

.tmp-header-inner {
  position: relative;
  max-width: var(--wrap);
  margin-inline: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  min-height: var(--head);
}

/* The reading progress line along the header's bottom edge. It rests at zero
   width, so a browser that cannot drive it from the scroll position never
   draws it and there is nothing to see.

   It is a child of the HEADER and not of the inner row, so that it runs the
   full width of the screen: the containing block of an absolutely positioned
   child is the padding box of its positioned ancestor, and the header's
   padding box is the whole bar. Inside the inner row it would stop at the
   measure and read as a bar that had broken. */
.tmp-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: var(--warm);
  transform: scaleX(0);
  transform-origin: left center;
  pointer-events: none;
}

.tmp-brand {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  text-decoration: none;
  color: var(--bone);
  flex: none;
  min-width: 0;
}

/* `width: auto` is doing real work and must not be dropped. The mark carries
   width and height attributes so the header cannot jump while it loads, and
   those attributes map to the CSS width and height properties. Setting only
   the height leaves the width at the attribute's value, and because an SVG
   letterboxes inside its box rather than stretching, the mark then draws small
   and off centre in a box the wrong shape. */
.tmp-brand-mark {
  height: 1.875rem;
  width: auto;
  flex: none;
  transition: transform 0.5s var(--ease);
}

.tmp-brand:hover .tmp-brand-mark {
  transform: translateY(-2px);
}

/* The name as text. The negative tracking is not decoration: Inter at this
   weight sets loosely for a logo, and closing it up is the difference between
   three words and one name. */
.tmp-brand-name {
  font-family: var(--ui);
  font-weight: 700;
  font-size: clamp(1.125rem, 0.95rem + 0.9vw, 1.375rem);
  letter-spacing: -0.028em;
  line-height: 1;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   The menu button

   Hidden outright until the script that drives it has marked the document
   (see NAV_ARM in shell.js), and hidden again as soon as there is room for the
   links themselves. So it is only ever on screen when it both works and is
   needed.
   -------------------------------------------------------------------------- */

.tmp-nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  margin-inline-end: -0.625rem;
  padding: 0;
  background: transparent;
  border: 0;
  color: var(--bone);
  cursor: pointer;
  transition: color 0.3s var(--ease-soft);
}

.tmp-nav-toggle:hover {
  color: var(--warm);
}

/* One glyph at a time. Both are in the markup, so the swap costs nothing and
   the button never changes width. */
.tmp-nav-shut { display: none; }
.tmp-nav-toggle[aria-expanded='true'] .tmp-nav-open { display: none; }
.tmp-nav-toggle[aria-expanded='true'] .tmp-nav-shut { display: block; }

.tmp-nav {
  display: flex;
  align-items: center;
  gap: clamp(1.5rem, 4vw, 2.5rem);
}

.tmp-nav a {
  position: relative;
  font-family: var(--ui);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--bone-dim);
  text-decoration: none;
  padding-block: 0.375rem;
  transition: color 0.3s var(--ease-soft);
}

.tmp-nav a:hover,
.tmp-nav a[aria-current='page'] {
  color: var(--bone);
}

/* The current page is marked with a dot rather than an underline. An underline
   under a menu item is the pattern this design is getting away from, and a dot
   is unmistakable at a glance without adding a second line to a bar that
   already has one. */
.tmp-nav a[aria-current='page']::before {
  content: '';
  position: absolute;
  left: 50%;
  /* UNDER the label, not over it. Above, it clears the link's padding and ends
     up nearer the top edge of the bar than the word it belongs to, where it
     reads as a speck of dust on the screen rather than as a marker. */
  bottom: -0.375rem;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--warm);
  transform: translateX(-50%);
}

/* Below 48rem the wordmark and the button are the whole header bar, and the
   links drop into a panel underneath it.

   Every rule here is behind `.js-nav`, which only exists once the script has
   run. With no script the button stays hidden and the links stay in the flow,
   wrapping onto a second line under the wordmark: smaller than the design
   intends, and completely usable, which is the right way round. */
@media (max-width: 47.999rem) {
  .js-nav .tmp-nav-toggle {
    display: inline-flex;
  }

  .js-nav .tmp-nav {
    display: none;
    /* Out of the header bar's flex row and across the full width of the band,
       so the panel's edges line up with the header's own border rather than
       with the wordmark. */
    position: absolute;
    inset-inline: calc(var(--gutter) * -1);
    top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding-inline: var(--gutter);
    /* Opaque, and one step up from the ground rather than transparent: on the
       front page the bar above it may still be transparent over the hero, and
       a panel that let the covers show through would be a menu nobody could
       read. */
    background: var(--ink-raised);
    border-bottom: 1px solid var(--line);
  }

  .js-nav .tmp-nav.is-open {
    display: flex;
  }

  .js-nav .tmp-nav a {
    padding-block: 1.0625rem;
    font-size: 1.0625rem;
    border-bottom: 1px solid var(--line-soft);
  }

  .js-nav .tmp-nav a:last-child {
    border-bottom: 0;
  }

  /* In the panel the dot would sit above the link's own text with a full width
     divider under it, which reads as a stray speck. Colour alone marks it. */
  .js-nav .tmp-nav a[aria-current='page']::before {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   8. Hero

   Full height, so the front page opens on one statement and the reader has to
   move to find the library.

   The ground carries DEPTH, NOT ORNAMENT, and the distinction is load bearing:
   a tessellated star was asked off this page on 19 August 2026 and stays off.
   What is here is one broad soft light behind the covers, a second much
   fainter one low on the left, and ink that lifts very slightly towards the
   top. Nothing repeats, nothing has an edge, and there is no motif.
   -------------------------------------------------------------------------- */

.tmp-hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  padding-inline: var(--gutter);
  /* svh, not vh: on a phone vh is the height with the browser's own bars
     retracted, so a full height hero measured in vh is always a little taller
     than the screen actually is, and the foot of it is cut off on arrival. */
  min-height: 100vh;
  min-height: 100svh;
  background:
    radial-gradient(52% 46% at 76% 30%, rgba(224, 168, 120, 0.1), transparent 70%),
    radial-gradient(46% 44% at 4% 96%, rgba(120, 160, 235, 0.08), transparent 72%),
    linear-gradient(180deg, #141d33 0%, var(--ink) 62%);
}

/* The hairline that closes the hero, and the same one that opens the footer,
   so the page shuts on the mark it opened with. It fades out at both ends
   rather than butting into the gutters. */
.tmp-hero::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 3;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line) 22%, var(--line) 78%, transparent);
}

.tmp-hero-inner {
  width: 100%;
  /* The header floats over this, so the top padding has to clear it. */
  padding-block: calc(var(--head) + clamp(3rem, 8vw, 6rem)) clamp(4.5rem, 10vw, 8rem);
  display: grid;
  gap: clamp(3rem, 7vw, 5rem);
  align-items: center;
}

/* The container the headline measures itself against. Nothing else on the page
   needs one, and this one exists only so that .tmp-h1 can be sized in `cqi`. */
.tmp-hero-text {
  container-type: inline-size;
  max-width: 52rem;
}

.tmp-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 0.75rem;
  margin-top: clamp(2.5rem, 6vw, 3.75rem);
}

/* The scroll cue: a hairline with a bead falling down it. It says nothing, so
   it is hidden from assistive technology, and it is not drawn on a short or
   narrow screen, where a full height hero is obviously scrollable already and
   the cue would only be in the way. */
.tmp-cue {
  display: none;
  position: absolute;
  left: 50%;
  bottom: 2.25rem;
  z-index: 2;
  width: 1px;
  height: 3.5rem;
  background: var(--line);
  overflow: hidden;
}

@media (min-height: 40rem) and (min-width: 30rem) {
  .tmp-cue { display: block; }
}

.tmp-cue::after {
  content: '';
  position: absolute;
  left: -1px;
  width: 3px;
  height: 1.25rem;
  background: linear-gradient(180deg, transparent, var(--warm));
  animation: tmp-cue-fall 2.8s var(--ease-soft) infinite;
}

@keyframes tmp-cue-fall {
  0%   { transform: translateY(-130%); opacity: 0; }
  25%  { opacity: 1; }
  75%  { opacity: 1; }
  100% { transform: translateY(340%); opacity: 0; }
}

/* --------------------------------------------------------------------------
   The fanned covers

   Three real covers in real perspective rather than three flat rectangles
   overlapping. Decoration made of real artwork rather than of stock shapes,
   and the one part of the first redesign Aamir asked to keep.

   NOT RENDERED BELOW 74rem, WHICH IS THE WIDTH AT WHICH IT GETS A COLUMN OF
   ITS OWN, and the two numbers have to be the same one. Stacked under the
   headline instead, the fan pushes the hero past the height of the screen, so
   a page that promises to open on one screen opens on one and a bit. Above
   74rem it sits beside the words and the promise holds.

   Every cover in it is lazy, so a phone pays nothing at all for it: a browser
   will not fetch a lazy image inside a display:none element. A fan also needs
   room to read as a fan; squeezed onto a phone it is three overlapping
   rectangles.
   -------------------------------------------------------------------------- */

.tmp-shelf { display: none; }

@media (min-width: 74rem) {
  .tmp-shelf {
    display: block;
    perspective: 1700px;
    perspective-origin: 50% 45%;
  }

  .tmp-shelf-stage {
    position: relative;
    /* The front cover is 15rem wide at the artwork's 1060:1484 ratio, so 21rem
       tall, and the fan lifts it a little further. Stated here rather than
       left to the absolutely positioned children, which contribute no height
       of their own. */
    height: 23rem;
    transform-style: preserve-3d;
  }

  .tmp-shelf-item {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 15rem;
    transform-style: preserve-3d;
    transition: transform 0.7s var(--ease), filter 0.7s var(--ease);
  }

  /* The two behind are turned away from the reader and pushed back in depth.
     They are dimmed with a filter rather than with opacity, because opacity
     would let the ground show through the artwork and grey the covers out
     rather than sitting them back. */
  .tmp-shelf-1 {
    transform: translate3d(-95%, -47%, -5rem) rotateY(26deg) rotateX(2deg) rotate(-4deg);
    filter: brightness(0.62) saturate(0.85);
    z-index: 1;
  }

  .tmp-shelf-3 {
    transform: translate3d(-5%, -47%, -5rem) rotateY(-26deg) rotateX(2deg) rotate(4deg);
    filter: brightness(0.62) saturate(0.85);
    z-index: 1;
  }

  .tmp-shelf-2 {
    transform: translate3d(-50%, -52%, 0) rotateY(-9deg) rotateX(3deg);
    z-index: 2;
  }

  .tmp-shelf:hover .tmp-shelf-1 {
    transform: translate3d(-104%, -48%, -3.5rem) rotateY(22deg) rotateX(2deg) rotate(-6deg);
    filter: brightness(0.78) saturate(0.92);
  }

  .tmp-shelf:hover .tmp-shelf-3 {
    transform: translate3d(4%, -48%, -3.5rem) rotateY(-22deg) rotateX(2deg) rotate(6deg);
    filter: brightness(0.78) saturate(0.92);
  }

  .tmp-shelf:hover .tmp-shelf-2 {
    transform: translate3d(-50%, -55%, 1.5rem) rotateY(-5deg) rotateX(2deg);
  }

  /* The floor. A pool of light under the stack rather than a shadow, because a
     shadow on ink is nothing at all.

     IT IS PUSHED BACK IN Z RATHER THAN GIVEN A z-index, and it has to be. In a
     preserve-3d parent the painting order is the 3D sort and z-index is not
     consulted: at a depth of zero this would be coplanar with the front cover,
     lose the tie on document order, and draw across the bottom of it. */
  .tmp-shelf-stage::after {
    content: '';
    position: absolute;
    left: 50%;
    bottom: -2rem;
    width: 26rem;
    height: 4rem;
    transform: translateX(-50%) translateZ(-8rem);
    background: radial-gradient(50% 50% at 50% 50%, rgba(224, 168, 120, 0.16), transparent 70%);
  }
}

/* The same breakpoint as the fan itself, and deliberately so: the fan appears
   and gets its column in the same step, so there is no width at which one has
   happened and the other has not. */
@media (min-width: 74rem) {
  .tmp-hero-inner {
    grid-template-columns: minmax(0, 1fr) 24rem;
  }
}

/* --------------------------------------------------------------------------
   9. Covers, and the library

   TWO WORKS, at size, in alternating rows. The grid of nine thumbnails is
   gone: Aamir asked for two on 19 August 2026, and two at thumbnail size would
   have been a grid with a hole in it.
   -------------------------------------------------------------------------- */

/* A cover, drawn as an object rather than as a picture of one.

   On ink the EDGE is what does that. A dark drop shadow is invisible on a dark
   ground, so what separates the artwork from the page is a hairline of its own
   light around it, with a real shadow appearing only once it lifts. Over the
   face go two gradients: a narrow dark band with a paler one just inside it
   along the leading edge, which is the shadow in the gutter and the light
   catching the first millimetre of the board, and one wide diagonal sheen.
   Neither is strong enough to notice alone, and together they are the
   difference between a cover and a thumbnail. */
.tmp-cover {
  position: relative;
  display: block;
  /* The supplied artwork is 1060 by 1484, so the ratio is stated rather than
     approximated with a padding hack, and the box is reserved before the image
     arrives. Nothing on the page jumps as the covers load. */
  aspect-ratio: 1060 / 1484;
  background: var(--ink-hover);
  overflow: hidden;
  box-shadow: var(--cover-edge);
  transition: transform 0.6s var(--ease), box-shadow 0.6s var(--ease);
}

.tmp-cover::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(
      90deg,
      rgba(0, 0, 0, 0.42) 0,
      rgba(0, 0, 0, 0.08) 1.4%,
      rgba(255, 255, 255, 0.18) 3.2%,
      rgba(255, 255, 255, 0) 8%
    ),
    linear-gradient(112deg, rgba(255, 255, 255, 0.12) 0, rgba(255, 255, 255, 0) 46%);
}

.tmp-cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.tmp-works {
  display: grid;
  gap: clamp(3.5rem, 9vw, 7.5rem);
  /* The counter is on the text and not in the markup. The works are an ordered
     list, so the position is already carried semantically, and a typed numeral
     would be the same fact said twice. */
  counter-reset: tmp-work;
}

.tmp-work {
  display: grid;
  gap: clamp(1.75rem, 5vw, 4rem);
  align-items: center;
  text-decoration: none;
  color: inherit;
}

.tmp-work-cover {
  max-width: 20rem;
}

@media (min-width: 52rem) {
  .tmp-work {
    grid-template-columns: minmax(0, 24rem) minmax(0, 1fr);
  }

  .tmp-work-cover { max-width: none; }

  /* Even rows are mirrored, so a list of several does not read as one column
     repeated. The front page shows a single work, so this costs it nothing and
     is here for any surface that shows more. */
  .tmp-works > li:nth-child(even) .tmp-work {
    grid-template-columns: minmax(0, 1fr) minmax(0, 24rem);
  }

  .tmp-works > li:nth-child(even) .tmp-work-cover { order: 2; }
}

.tmp-work:hover .tmp-cover {
  transform: translateY(-10px);
  box-shadow: var(--cover-lift);
}

.tmp-work-text::before {
  counter-increment: tmp-work;
  content: counter(tmp-work, decimal-leading-zero);
  display: block;
  font-size: 0.75rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2em;
  color: var(--bone-faint);
  margin-bottom: 1.5rem;
}

.tmp-work-cat {
  display: block;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--warm);
  margin-bottom: 0.75rem;
}

.tmp-work-title {
  font-size: clamp(1.5rem, 3.4vw, 2.25rem);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.028em;
}

.tmp-work-by {
  display: block;
  font-size: 0.9375rem;
  color: var(--bone-dim);
  margin-top: 0.75rem;
}

.tmp-work-blurb {
  max-width: var(--column);
  margin-top: 1.5rem;
  font-size: 1rem;
  line-height: 1.65;
  color: var(--bone-dim);
}

/* Not a link of its own: the whole row is the link, and this is the part of it
   that says so. It is a span in the markup for exactly that reason, because a
   link inside a link is not a thing that can be built. */
.tmp-work-go {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  margin-top: 2rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--bone);
}

.tmp-work-go svg {
  transition: transform 0.4s var(--ease);
}

.tmp-work:hover .tmp-work-go svg {
  transform: translateX(6px);
}

/* --------------------------------------------------------------------------
   10. The passage

   The one place on the front page set in the reading serif, and the one place
   a visitor is asked to read rather than scan. Those are the same fact: the
   serif means the words are a book's rather than the site's.

   Nothing is drawn around it. Every other section is carried by a cover, so
   this one is carried by the type, on a ground one step up from the page.
   -------------------------------------------------------------------------- */

.tmp-passage {
  margin: 0;
  max-width: 46rem;
}

.tmp-passage-text {
  margin: 0;
  font-family: var(--read);
  font-size: clamp(1.5rem, 3.6vw, 2.625rem);
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: -0.014em;
  color: var(--bone);
  text-wrap: pretty;
}

.tmp-passage-by {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem;
  margin-top: clamp(2.5rem, 6vw, 3.5rem);
  padding-top: 1.75rem;
  border-top: 1px solid var(--line);
}

.tmp-passage-cover {
  display: block;
  width: 5.5rem;
  flex: none;
}

.tmp-passage-meta {
  display: block;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--bone-dim);
  margin-inline-end: auto;
}

.tmp-passage-meta b {
  display: block;
  font-weight: 600;
  font-size: 1rem;
  color: var(--bone);
  margin-bottom: 0.1875rem;
}

/* --------------------------------------------------------------------------
   11. The featured book

   The card is gone. This was a white panel with a border and a shadow, which
   is furniture drawn around a book rather than a book. The cover now stands on
   the page at size with the words beside it: the same treatment the library
   gets, one step larger.
   -------------------------------------------------------------------------- */

.tmp-feature {
  display: grid;
  gap: clamp(2rem, 6vw, 4.5rem);
  align-items: center;
}

.tmp-feature-cover {
  max-width: 18rem;
}

@media (min-width: 52rem) {
  .tmp-feature {
    grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
  }

  .tmp-feature-cover { max-width: none; }
}

.tmp-feature-cat {
  display: block;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--warm);
  margin-bottom: 0.875rem;
}

.tmp-feature-title {
  font-size: clamp(1.875rem, 4.4vw, 3rem);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.032em;
}

/* The transliterated original title. Italic, and in the READING serif, because
   it is the book's own name in its own language rather than a label the site
   has put on it. It is one of exactly two places the serif appears on this
   page, and both of them are for the same reason. */
.tmp-feature-sub {
  font-family: var(--read);
  font-style: italic;
  font-size: 1.125rem;
  color: var(--bone-dim);
  margin-top: 0.625rem;
}

.tmp-feature-by {
  font-size: 0.9375rem;
  color: var(--bone-dim);
  margin-top: 1rem;
}

.tmp-feature-blurb {
  max-width: var(--column);
  margin-top: 1.75rem;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--bone-dim);
}

.tmp-feature .tmp-act {
  margin-top: 2.25rem;
}

/* --------------------------------------------------------------------------
   12. Footer
   -------------------------------------------------------------------------- */

.tmp-footer {
  position: relative;
  padding-inline: var(--gutter);
  border-top: 1px solid var(--line);
}

.tmp-footer-inner {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-block: clamp(3.5rem, 8vw, 6rem);
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
}

@media (min-width: 46rem) {
  .tmp-footer-inner {
    grid-template-columns: minmax(0, 1.6fr) repeat(2, minmax(0, 1fr));
  }
}

/* The mark is inline SVG in the current colour, so on this ground it is simply
   told to be bone. It used to be an image put through a stack of four filters
   pretending forest green was parchment. */
.tmp-footer-brand {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1.5rem;
  color: var(--bone);
}

.tmp-footer-mark {
  height: 2.5rem;
  flex: none;
}

.tmp-footer-mark svg {
  height: 100%;
  width: auto;
}

.tmp-footer-name {
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: -0.028em;
  line-height: 1;
}

.tmp-footer-about {
  font-size: 0.9375rem;
  line-height: 1.7;
  color: var(--bone-dim);
  max-width: 24rem;
}

.tmp-footer h2 {
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--warm);
  margin: 0 0 1.25rem;
}

.tmp-footer ul {
  display: grid;
  gap: 0.75rem;
}

.tmp-footer a {
  font-family: var(--ui);
  color: var(--bone-dim);
  text-decoration: none;
  font-size: 0.9375rem;
  display: inline-block;
  transition: color 0.3s var(--ease-soft), transform 0.4s var(--ease);
}

.tmp-footer a:hover {
  color: var(--bone);
  transform: translateX(5px);
}

.tmp-footer-base {
  max-width: var(--wrap);
  margin-inline: auto;
  border-top: 1px solid var(--line-soft);
  padding-block: 1.5rem 2.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  justify-content: space-between;
  font-size: 0.75rem;
  letter-spacing: 0.02em;
  color: var(--bone-faint);
}

/* --------------------------------------------------------------------------
   13. Interior pages

   The shell used by anything that is not the front page, and, in 13b to 13e,
   the parts the About and Contact pages are built from. Those two are BUILT
   pages and not holding ones since 19 August 2026: they open on their own hero
   rather than on the plain heading below, so they use the shell in 13b instead
   of `.tmp-page`. What is left using `.tmp-page` is the holding Library page,
   the per-book pages and the not-found page.
   -------------------------------------------------------------------------- */

.tmp-page {
  max-width: 44rem;
  margin-inline: auto;
  padding-block: calc(var(--band) * 0.8) var(--band);
  padding-inline: var(--gutter);
}

.tmp-page h1 {
  font-size: clamp(2.25rem, 6vw, 3.75rem);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.034em;
}

.tmp-page h1::after {
  content: '';
  display: block;
  width: 100%;
  height: 1px;
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
  background: var(--line);
}

.tmp-page > * + * {
  margin-top: 1.25rem;
}

.tmp-page p {
  color: var(--bone-dim);
  line-height: 1.7;
}

.tmp-page a {
  color: var(--bone);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.tmp-lede {
  font-size: clamp(1.125rem, 2.4vw, 1.375rem);
  line-height: 1.55;
  letter-spacing: -0.014em;
  color: var(--bone);
  max-width: var(--column);
}

.tmp-page .tmp-lede {
  margin-top: 2rem;
  color: var(--bone);
}

.tmp-note {
  color: var(--bone-faint);
}

/* The holding library list. Deliberately plain: it is a list of what is
   coming, not the Library page, and it should not be mistaken for it. */
.tmp-page-books {
  margin: 2.5rem 0 0;
}

.tmp-page-books li {
  padding-block: 1.125rem;
  border-bottom: 1px solid var(--line-soft);
}

.tmp-page-books strong {
  display: block;
  font-family: var(--ui);
  font-weight: 600;
  font-size: 1.0625rem;
  line-height: 1.25;
  letter-spacing: -0.018em;
  color: var(--bone);
}

.tmp-page-books span {
  font-size: 0.8125rem;
  color: var(--bone-dim);
}

/* The separator is spaced with a MARGIN and not with spaces inside the
   generated content. A trailing space at the end of an inline box is collapsed
   away, so a content of space, dot, space renders as a space and a dot and
   then nothing, which sets the dot hard against the word after it. */
.tmp-page-books span + span::before {
  content: '\00B7';
  margin-inline: 0.45em;
  color: var(--bone-faint);
}

/* --------------------------------------------------------------------------
   13b. The interior hero

   The front page's hero in every respect but height, and it is shared code
   rather than a resemblance: the eyebrow, the word by word rise, the actions
   and the accent all come out of the same rules.

   IT IS NOT FULL HEIGHT, and that is the one deliberate difference. The front
   page takes the whole screen because it has one statement to make and the
   reader should have to move to find the library. An interior page has the
   thing the visitor came for under the fold, and holding a form or an account
   of the project a full screen away is theatre at their expense. It opens on
   about two thirds of the screen, which is enough to be a beginning and not
   enough to be a wait.

   The header floats over it, as on the front page, so the ground reaches the
   top edge of the screen. Every page that opens on this passes `overlay`, and
   a page that does NOT open on one must not: a floating header over a heading
   on the flat ground is a header over nothing.

   The ground carries depth and no motif, the same rule as the front page. It
   is lit from the other side, so the two pages are recognisably the same site
   without being the same picture: the broad light sits high on the left here
   and behind the covers on the front page.
   -------------------------------------------------------------------------- */

.tmp-phero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  padding-inline: var(--gutter);
  min-height: 62vh;
  min-height: 62svh;
  background:
    radial-gradient(48% 52% at 14% 12%, rgba(224, 168, 120, 0.09), transparent 72%),
    radial-gradient(44% 46% at 92% 88%, rgba(120, 160, 235, 0.07), transparent 74%),
    linear-gradient(180deg, #141d33 0%, var(--ink) 74%);
}

/* The hairline that closes it, the same one that closes the front page's hero
   and opens the footer, fading out at both ends rather than butting into the
   gutters. */
.tmp-phero::after {
  content: '';
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 3;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--line) 22%, var(--line) 78%, transparent);
}

/* The container the headline measures itself against. Same reason as the front
   page's: a headline sized from the viewport is at its largest exactly where
   its column is narrowest. */
.tmp-phero-inner {
  container-type: inline-size;
  width: 100%;
  padding-block: calc(var(--head) + clamp(3rem, 8vw, 5.5rem)) clamp(3.5rem, 8vw, 6rem);
}

/* Smaller than the front page's headline, because these are SENTENCES and that
   one is a title. 'An effort to make classical Islamic knowledge accessible in
   English' at 5.75rem would be five lines of display type before the page has
   said anything. The floor is still large enough to be the biggest thing on
   the screen. */
.tmp-ph1 {
  font-size: clamp(2.125rem, 5.6vw, 3.75rem);
  font-size: clamp(2.125rem, 6.4cqi, 4.25rem);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.032em;
  max-width: 22ch;
}

/* A phrase lifted out of a headline, in the one accent the site has.

   The old About page set its equivalent in italic. Italic in the interface
   face at headline size reads as a slip rather than as emphasis, and the site
   already has a colour that means 'this is the important part': it is on every
   section name and every category on the front page. */
.tmp-hl {
  color: var(--warm);
}

.tmp-phero-lede {
  max-width: var(--column);
  margin-top: clamp(1.75rem, 4vw, 2.5rem);
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  line-height: 1.65;
  color: var(--bone-dim);
}

/* --------------------------------------------------------------------------
   13c. A numbered list of things

   The objectives on the About page and the reasons to write on the Contact
   page. Both were bordered white cards on the live site, and a card is the
   single strongest signal of the kit this design replaces.

   So: rows, ruled off from each other with one hairline, with the roman
   numeral set large in the accent out to the left. The numeral is what makes
   the rows read as a sequence, which is the one thing the cards were doing
   that was worth keeping.
   -------------------------------------------------------------------------- */

/* NO OPENING HAIRLINE. Every numbered list on this site is drawn directly
   under a section head or under a disclosure bar, and both of those have just
   ruled a line across the same measure. The rows are told apart by the rule
   BETWEEN them, and the last row's rule closes the list off, so the list needs
   no line of its own to start on. */
.tmp-list {
  border-top: 0;
}

.tmp-list-item {
  display: grid;
  gap: 0.5rem 2rem;
  padding-block: clamp(1.75rem, 4vw, 2.75rem);
  border-bottom: 1px solid var(--line-soft);
}

@media (min-width: 42rem) {
  .tmp-list-item {
    grid-template-columns: 4.5rem minmax(0, 1fr);
  }
}

/* Tabular figures so I, II and III sit on the same left edge as IV and V. The
   numeral is aria-hidden in the markup: it is a position on a page, not a
   piece of the sentence. */
.tmp-list-n {
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  font-variant-numeric: tabular-nums;
  color: var(--warm);
  line-height: 1.5;
}

.tmp-list-title {
  font-size: clamp(1.1875rem, 2.4vw, 1.5rem);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.022em;
}

.tmp-list-body {
  max-width: var(--column);
  margin-top: 0.75rem;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--bone-dim);
}

/* One paragraph set at statement size rather than at body size, for a section
   whose whole content is that paragraph. At body size it reads as a footnote
   under its own heading. */
.tmp-statement {
  max-width: 42rem;
  font-size: clamp(1.125rem, 2.4vw, 1.5rem);
  line-height: 1.6;
  letter-spacing: -0.014em;
  color: var(--bone-dim);
  text-wrap: pretty;
}

/* The closing line about Ṣadaqah Jāriyah, under the reasons to write.

   IT WAS SET AT STATEMENT SIZE and it read as though the page had started a
   new section rather than finished one: a line half again the size of the
   five paragraphs above it, in the same face, with nothing between them but
   air. It is a closing note on a list, so it is drawn as one: a short rule in
   the accent above it, the text one step up from the list's own body rather
   than three, and the same measure as the rows it closes. The rule is what
   separates it, which is what the size was being asked to do and could not. */
.tmp-sadaqah {
  max-width: var(--column);
  margin-top: clamp(2rem, 4vw, 2.75rem);
  padding-top: 1.5rem;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: var(--bone);
  text-wrap: pretty;
  position: relative;
}

.tmp-sadaqah::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 2.5rem;
  height: 1px;
  background: var(--warm);
}

/* --------------------------------------------------------------------------
   13d. A disclosure

   A section folded away behind a bar the visitor can open. Used once, for the
   reasons to write on the Contact page: the form is what that page is for, so
   the reasons sit above it as one closed line rather than as a section the
   visitor has to scroll past to reach the box.

   IT IS A REAL `details`, so it opens with no script at all, and the bar is a
   `summary`, which is a control the keyboard reaches and which a screen reader
   announces as collapsed or expanded without being told to. All this file does
   is take the browser's own triangle off and draw the site's own bar in its
   place.

   THE BAR IS A SECTION HEAD THAT HAPPENS TO OPEN: the same hairline, the same
   numeral in the accent, the same small capitals. A folded section still reads
   as one of the page's sections rather than as a widget dropped on top of the
   design.
   -------------------------------------------------------------------------- */

/* Tighter than a full band. The whole point of folding this away is that the
   form stays on the first screen, and a band of the usual rhythm round one
   closed line would spend most of what the fold saved. */
.tmp-open-band {
  padding-block: clamp(2rem, 4vw, 3rem);
}

.tmp-open {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line-soft);
}

.tmp-open-bar {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.375rem 1.5rem;
  padding-block: 1.125rem;
  cursor: pointer;
  /* Both halves of taking the browser's own marker off: Firefox and the
     standard property draw it from the list style, Safari and Chrome from
     their own pseudo-element. */
  list-style: none;
}

.tmp-open-bar::-webkit-details-marker {
  display: none;
}

.tmp-open-bar:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 4px;
}

/* Placed from the bar rather than on their own, because the bar's heading also
   carries `.tmp-sec-name`, which a section head stretches across its own grid
   at 48rem. Two rules of equal weight in different places is the sort of thing
   that comes apart the next time either is touched. */
.tmp-open-bar .tmp-open-name {
  grid-column: 1;
  grid-row: 1;
}

/* Not a heading and not a label: one line saying what is behind the bar, so
   the visitor can decide whether to open it without opening it. */
.tmp-open-bar .tmp-open-hint {
  grid-column: 1;
  grid-row: 2;
  max-width: var(--column);
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--bone-dim);
}

.tmp-open-bar .tmp-open-mark {
  grid-column: 2;
  grid-row: 1 / -1;
  color: var(--warm);
  transition: transform 0.35s var(--ease-soft);
}

.tmp-open[open] .tmp-open-mark {
  transform: rotate(180deg);
}

.tmp-open-bar:hover .tmp-open-mark {
  color: var(--bone);
}

/* The panel cannot animate its height without a script, and it does not try
   to. What it does is arrive rather than appear: the content rises the same
   short distance a revealed section does, so opening the bar reads as part of
   the same design. Under reduced motion the blanket rule at the foot of
   section 14 cuts the duration to nothing and it is simply there. */
.tmp-open-body {
  padding-block: 0.5rem clamp(1.5rem, 4vw, 2.5rem);
  animation: tmp-unfold 0.45s var(--ease) both;
}

@keyframes tmp-unfold {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}

/* Inside a panel the last row does not rule itself off either: the disclosure
   has its own hairline along the bottom, a panel's padding away, and the two
   together were a pair of rules with nothing between them. */
.tmp-open-body .tmp-list-item:last-child {
  border-bottom: 0;
}

/* --------------------------------------------------------------------------
   13e. The duʿā

   The one moment on the site that asks the reader for something rather than
   offering them something, so it is the one place given a whole screen with
   nothing else on it.

   THE MARK IS DRAWN BEHIND IT, large and very faint. It is the same inline
   drawing the header and the footer use, in the current colour, so it needs no
   file and cannot be the wrong colour when the palette next changes. It sits
   behind the words rather than above them because it is a watermark and not an
   illustration: at this opacity it is texture, and the moment it is strong
   enough to look at, it is competing with the sentence.
   -------------------------------------------------------------------------- */

.tmp-dua {
  position: relative;
  isolation: isolate;
  display: grid;
  place-items: center;
  padding-block: clamp(2rem, 6vw, 4rem);
  text-align: center;
}

.tmp-dua-mark {
  position: absolute;
  z-index: -1;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: clamp(14rem, 34vw, 22rem);
  color: var(--bone);
  opacity: 0.05;
}

.tmp-dua-mark svg {
  width: 100%;
  /* The mark carries width and height attributes so the header cannot jump
     while it loads, and those attributes map to the CSS properties. Setting
     only the width would letterbox it inside a box the wrong shape. */
  height: auto;
}

.tmp-dua-text {
  max-width: 40rem;
  font-size: clamp(1.1875rem, 2.8vw, 1.75rem);
  line-height: 1.6;
  letter-spacing: -0.016em;
  color: var(--bone);
  text-wrap: pretty;
}

/* The band that hands a page on to another one. No section number: it is the
   page finishing rather than another part of what it had to say. */
.tmp-cta {
  border-top: 1px solid var(--line);
  padding-top: clamp(2.5rem, 6vw, 4rem);
}

.tmp-cta-text {
  max-width: 30rem;
  font-size: clamp(1.375rem, 3.4vw, 2.25rem);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.028em;
  color: var(--bone);
}

.tmp-cta .tmp-actions {
  margin-top: clamp(2rem, 5vw, 2.75rem);
}

/* --------------------------------------------------------------------------
   13f. The contact form

   Two columns above 62rem: the form, and beside it the address and what to
   expect. The second column is NOT a card. It is a column with a hairline down
   its inner edge, which separates it without drawing a box round it, and the
   rule moves to the top of the block when the two stack.

   THE CONTROLS ARE DRAWN THE WAY THE REST OF THE SITE IS: square, on the
   ground rather than on white, with a hairline that becomes bone on focus. A
   dark form is the part of a dark design most often left as the browser drew
   it, and a white box with a blue focus ring in the middle of this page would
   undo the whole scheme.
   -------------------------------------------------------------------------- */

.tmp-contact {
  display: grid;
  gap: clamp(2.5rem, 6vw, 4rem);
  align-items: start;
}

@media (min-width: 62rem) {
  .tmp-contact {
    grid-template-columns: minmax(0, 1fr) 18rem;
    gap: clamp(3rem, 6vw, 5rem);
  }
}

.tmp-form {
  display: grid;
  gap: 1.5rem;
  max-width: 40rem;
}

.tmp-field-pair {
  display: grid;
  gap: 1.5rem;
}

@media (min-width: 34rem) {
  .tmp-field-pair {
    grid-template-columns: 1fr 1fr;
  }
}

.tmp-field label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-dim);
  margin-bottom: 0.625rem;
}

/* The controls. The face and the size are STATED and not inherited, because a
   form control inherits neither: left alone it is drawn at the browser's own
   13px in the system interface face, and a form set in a different typeface
   from the page around it is the commonest way a dark design comes apart. */
.tmp-input {
  font-family: var(--ui);
  font-size: 1rem;
  line-height: 1.5;
  display: block;
  width: 100%;
  padding: 0.875rem 1rem;
  color: var(--bone);
  background: var(--ink-raised);
  border: 1px solid var(--line);
  border-radius: 0;
  /* Safari draws its own inner shadow and its own corner radius on an input
     whatever the border says. */
  appearance: none;
  transition:
    border-color 0.3s var(--ease-soft),
    background-color 0.3s var(--ease-soft);
}


.tmp-input::placeholder {
  color: var(--bone-faint);
}

.tmp-input:hover {
  border-color: var(--bone-faint);
}

/* The focus ring is drawn on the control's own border rather than as an
   outline offset away from it, because these boxes sit close together and an
   offset ring on a two column pair overlaps its neighbour. It is bone, which
   is the site's focus colour everywhere else, and it is a border change AND a
   shadow so that it is still visible in forced colours. */
.tmp-input:focus-visible {
  outline: none;
  border-color: var(--bone);
  box-shadow: 0 0 0 1px var(--bone);
}

textarea.tmp-input {
  resize: vertical;
  min-height: 10rem;
}

/* The select's own arrow, drawn rather than left to the browser. `appearance:
   none` above takes the native one away, and without a replacement there is
   nothing to say the field opens. It is inline SVG in a data URI, so it costs
   no request, and it is drawn in the secondary ink so it sits back. */
select.tmp-input {
  padding-right: 2.75rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23a3aab6' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
}

/* The option list is drawn by the operating system and takes almost nothing
   from this stylesheet. What it does take is these two, without which the list
   is white text on white on some platforms. */
select.tmp-input option {
  background: var(--ink-raised);
  color: var(--bone);
}

.tmp-field.is-bad .tmp-input {
  border-color: var(--warn);
}

.tmp-field-error {
  margin-top: 0.5rem;
  font-size: 0.875rem;
  color: var(--warn);
}

.tmp-field-hint {
  margin-bottom: 0.625rem;
  font-size: 0.875rem;
  color: var(--bone-dim);
}

/* What happened to the last attempt. It is the first thing in the form rather
   than the last thing after the button, so an answer cannot arrive below the
   fold on a phone, and it is a live region in the markup so the enhanced
   version, which never reloads the page, still announces it. */
.tmp-form-status {
  padding: 1rem 1.25rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  border-left: 2px solid var(--line);
  background: var(--ink-raised);
}


/* THE TWO OUTCOMES ARE TOLD APART BY MORE THAN THE COLOUR, because a colour on
   its own is not a message: about one man in twelve cannot read the difference
   between these two hues, and the sentence inside is what actually says what
   happened. The colour is the confirmation, not the statement. */
.tmp-form-status.is-ok {
  border-left-color: var(--warm);
  color: var(--bone);
}

.tmp-form-status.is-bad {
  border-left-color: var(--warn);
  color: var(--bone);
}

/* The sentence explaining a form that arrived already filled in, from a link
   somewhere else on the site. */
.tmp-form-prefill {
  padding: 1rem 1.25rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--bone-dim);
  border-left: 2px solid var(--line);
}

.tmp-form-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem 1.75rem;
  margin-top: 0.5rem;
}

.tmp-form-note {
  font-size: 0.875rem;
  color: var(--bone-dim);
}

.tmp-form button[disabled] {
  opacity: 0.55;
  cursor: progress;
}

/* THE HONEYPOT, and why it is positioned away rather than hidden.

   A field set to `display: none`, or carrying the `hidden` attribute, is
   invisible to the crude bots as well, and they know to skip it. This one is
   in the layout and in the markup exactly like a real field, and simply
   painted off the page. The markup keeps a keyboard visitor out of it with
   `tabindex="-1"` and a screen reader out of it with `aria-hidden`, so the one
   thing that ever fills it in is something filling in every field it finds.

   `position: absolute` and not a negative margin: a negative margin would
   still take part in the layout and could drag the field below it upwards. */
.tmp-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* The column beside the form. A hairline and not a box: on a phone the rule
   moves to the top, where it separates the two without pretending to be a
   panel. */
.tmp-contact-side {
  border-top: 1px solid var(--line);
  padding-top: 1.75rem;
}

@media (min-width: 62rem) {
  .tmp-contact-side {
    border-top: 0;
    border-left: 1px solid var(--line);
    padding-top: 0;
    padding-left: clamp(2rem, 4vw, 3rem);
  }
}

.tmp-contact-side h3 {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--warm);
}

.tmp-contact-side h3 + p {
  margin-top: 0.75rem;
}

.tmp-contact-side p {
  font-size: 0.9375rem;
  line-height: 1.65;
  color: var(--bone-dim);
}

.tmp-contact-side p + h3 {
  margin-top: 2.25rem;
}

.tmp-contact-side a {
  color: var(--bone);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   13g. The Library page

   The bar, and the works.

   THERE ARE NO CARDS IN THE GRID, in the sense the live page meant by a card:
   no white panel, no border, no corner radius, no shadow drawn round the
   words. Nine pieces of commissioned artwork are the only colour on this site
   and a box drawn around each of them is a box competing with the thing it
   contains. A work is its cover standing on the ground, with its own edge and
   its own sheen from section 9, and three lines of type under it.

   THE BAR IS ONE ROW OF THREE BUTTONS. A round glass on the left that opens
   a search field the width of the bar, and on the right one button holding
   everything that narrows the library and one holding everything that orders
   it. Both are `details` elements, so they open with nothing running, and both
   panels are lifted out of the flow so that opening one does not push the nine
   covers down the page.

   The glass is THE ONE ROUND THING IN THIS DESIGN and it is round on purpose.
   Everywhere else a corner radius is refused, because it is the strongest
   single signal of the kit this design replaces; an icon on its own with no
   word beside it is the one shape that is read faster as a circle, and it is
   the shape the same control has in the other project. Everything with a word
   in it stays square.

   The rest is built out of the page's existing vocabulary rather than out of
   new furniture: the label above a group of chips is the numbered list's label
   column in 13c, the chosen chip is the primary action in section 6 at small
   size, and the count sits in the secondary ink like every other piece of
   metadata.
   -------------------------------------------------------------------------- */

/* NO HAIRLINE OF ITS OWN. The section head directly above has already ruled
   this section off, and a second line a heading's height below it was the
   first of the three stacked rules Aamir asked to be thinned out on 19 August
   2026. The bar's own line is the one UNDER it, which is the line that does
   real work: it separates the controls from the covers. */
.tmp-lib-filters {
  /* TWO NUMBERS NAMED ONCE, because two rules have to agree about them. The
     first is the gap between the bar and the covers. The second is the exact
     height the search field occupies when it is open, and the rule under this
     one and the field itself are the two places it is used: the field is drawn
     exactly this tall, and the space under the bar grows by exactly this much
     to hold it. Written out twice they would drift, and the way that failure
     shows is text on top of artwork. */
  --tmp-lib-gap: clamp(2.5rem, 6vw, 4rem);
  --tmp-find-h: 3.5rem;
  margin-bottom: var(--tmp-lib-gap);
}

/* THE SEARCH FIELD IS OUT OF THE FLOW, LIKE THE TWO MENUS, AND UNLIKE THEM IT
   HAS NO BOX OF ITS OWN, so on a phone it was being drawn straight over the
   first row of covers: the word SEARCH and the placeholder sat on the artwork
   with nothing between them and it. The menus get away with hanging over the
   page because each is an opaque panel a reader has this moment opened and
   will shut again in a moment. The field is meant to read as part of the bar,
   and part of the bar cannot float over the page.

   So while it is open the gap under the bar grows by exactly the height of the
   field, and the covers move down out of its way. The covers move, which the
   note on the panels above says they must not: that rule is about a menu
   opening over a list somebody is reading, and this is the bar getting taller
   by one row of itself, which is the one case where the list should move. */
.tmp-lib-filters:has(.tmp-lib-find[open]) {
  margin-bottom: calc(var(--tmp-lib-gap) + var(--tmp-find-h));
}

/* The bar. It is the containing block for the panels as well as for itself,
   which is what lets the search field span the whole width of the bar while
   the two menus on the right hang under their own buttons.

   THE THREE CONTROLS SHARE ONE ROW AT EVERY WIDTH, which is Aamir's decision
   of 19 August 2026 after seeing the bar break in two on a phone. It used to
   be one wrapping flex row, and on a narrow screen the count in the middle
   pushed Filter and Sort by onto a second line, so the three controls that are
   meant to read as one bar read as two rows of furniture.

   So below 52rem it is a two row grid and the ROW IS DECIDED rather than left
   to wrapping: the glass and the two menus take the first row, the count and
   the reset link take the second. From 52rem, which is the width at which the
   grid below goes to three columns, everything fits on one line and it goes
   back to being a single flex row. */
.tmp-lib-bar {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.625rem 1rem;
  padding-block: 0.875rem;
  border-bottom: 1px solid var(--line-soft);
}

/* Stretched, not sat at the left: the whole point of the pill is that the gap
   between the search and the two menus is closed. */
.tmp-lib-bar .tmp-lib-find {
  grid-area: 1 / 1;
  justify-self: stretch;
  min-width: 0;
}

.tmp-lib-bar .tmp-lib-menus {
  grid-area: 1 / 2;
}

.tmp-lib-bar .tmp-lib-count {
  grid-area: 2 / 1;
}

.tmp-lib-bar .tmp-lib-reset {
  grid-area: 2 / 2;
  justify-self: end;
}

/* While the search is open, the field's own hairline is sitting a row below
   this one, so the bar stops drawing a second rule across the middle of the
   two. The colour goes rather than the border, so nothing moves. A browser
   without `:has` simply keeps both lines, which is a line too many and not a
   control that does nothing: that is the difference between using `:has` here
   and refusing it on the chips in the panel. */
.tmp-lib-bar:has(.tmp-lib-find[open]) {
  border-bottom-color: transparent;
}

/* Pinned right, which is where Aamir asked for them. They never wrap against
   each other: two buttons stacked in a corner is the same break in miniature. */
.tmp-lib-menus {
  display: flex;
  flex-wrap: nowrap;
  gap: 0.5rem;
  min-width: 0;
}

@media (min-width: 52rem) {
  .tmp-lib-bar {
    display: flex;
    flex-wrap: nowrap;
  }

  /* It takes the slack on this layout too, but not all of it: a pill running
     the whole way to the count would leave the count hard against the Filter
     button, and the search would be the largest thing on a bar whose subject
     is nine covers. */
  .tmp-lib-find {
    flex: 1 1 auto;
    min-width: 0;
    max-width: 22rem;
  }

  .tmp-lib-menus {
    margin-left: auto;
  }
}

/* Only the two on the right are positioned, so their panels hang under their
   own buttons. The search deliberately is NOT, so its panel resolves against
   the bar above and comes out the full width of it. */
.tmp-lib-menus .tmp-lib-menu {
  position: relative;
}

/* --- the search ---
   The one round thing in the design, and the reason is at the head of this
   section. It began as a glass with nothing in it and is a PILL as of 19
   August 2026, on Aamir's word, so that it fills the space left over beside
   the two menus instead of leaving a gap in the middle of the bar. Stretching
   a circle is the one change that keeps the reason the shape was allowed:
   round because it carries a drawing rather than a label, and it is still the
   only thing on the site with a radius on it.

   Which of the two icons inside it is showing is decided by the open state, in
   this stylesheet, so the button says what pressing it will do whether or not
   anything is running. */
.tmp-lib-glass {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-width: 2.375rem;
  min-height: 2.375rem;
  padding-inline: 0.8125rem;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.4;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--bone-dim);
  cursor: pointer;
  /* Both halves of taking the browser's own marker off, as on the disclosure
     bars in section 12. */
  list-style: none;
  transition:
    background-color 0.3s var(--ease-soft),
    color 0.3s var(--ease-soft),
    border-color 0.3s var(--ease-soft);
}

.tmp-lib-glass::-webkit-details-marker {
  display: none;
}

.tmp-lib-glass:hover {
  color: var(--bone);
  border-color: var(--bone-faint);
}

.tmp-lib-glass:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 3px;
}

/* Open, or holding a search that is narrowing the library. Solid, because
   this is the one control on the bar with no words on it to carry a state. */
.tmp-lib-find[open] .tmp-lib-glass,
.tmp-lib-find.is-on .tmp-lib-glass {
  background: var(--bone);
  border-color: var(--bone);
  color: var(--ink);
}

.tmp-lib-find:not([open]) .tmp-i-close,
.tmp-lib-find[open] .tmp-i-search {
  display: none;
}

/* The word beside the drawing, and the first thing to go when the pill has no
   room for it: under 23rem the two menus leave a gap the width of the glass
   itself, and a pill that has to cut a five letter word in half is worse than
   the glass it came from. */
.tmp-lib-glass-word {
  display: none;
}

@media (min-width: 23rem) {
  .tmp-lib-glass-word {
    display: inline;
  }
}

/* --- the two buttons ---
   The chip's own shape at the size of a control rather than of a choice: same
   hairline, same weight, same square corners. Open is a raised fill and a
   brighter edge rather than the chip's solid inversion, because these two
   carry a warm tally and a warm order name inside them and neither reads on a
   bone ground. */
.tmp-lib-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5625rem 0.875rem;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.4;
  color: var(--bone-dim);
  border: 1px solid var(--line);
  cursor: pointer;
  white-space: nowrap;
  list-style: none;
  transition:
    background-color 0.3s var(--ease-soft),
    color 0.3s var(--ease-soft),
    border-color 0.3s var(--ease-soft);
}

.tmp-lib-btn::-webkit-details-marker {
  display: none;
}

.tmp-lib-btn:hover {
  color: var(--bone);
  border-color: var(--bone-faint);
}

.tmp-lib-btn:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 3px;
}

.tmp-lib-menu[open] .tmp-lib-btn {
  background: var(--ink-raised);
  color: var(--bone);
  border-color: var(--bone-faint);
}

/* The chevron is the same one the About page's folded sections use, so it has
   to be turned here as well: the rule that turns it there is scoped to that
   bar, and two rules of equal weight in different places is the sort of thing
   that comes apart the next time either is touched. */
.tmp-lib-btn .tmp-open-mark {
  color: var(--warm);
  transition: transform 0.35s var(--ease-soft);
}

.tmp-lib-menu[open] .tmp-lib-btn .tmp-open-mark {
  transform: rotate(180deg);
}

/* How many choices are narrowing the library, on the face of the Filter
   button. It is there so that a library quietly showing four of nine can never
   look like a library of four. */
.tmp-lib-tally {
  min-width: 1.25rem;
  padding-inline: 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.25rem;
  text-align: center;
  color: var(--ink);
  background: var(--warm);
  font-variant-numeric: tabular-nums;
}

/* And the order the library is in, on the face of the Sort by button, so the
   one thing a closed menu ought to say is the one thing it says.

   It is the one piece of the bar that stands down on a phone, and it is the
   right piece to lose: the order is the only thing behind either button that
   changes nothing about WHICH works are on the page, so a reader who cannot
   see it is not being kept from anything. The tally on Filter never stands
   down for exactly that reason, because a library quietly showing four of nine
   must always say so. */
.tmp-lib-chosen {
  display: none;
  color: var(--warm);
}

@media (min-width: 52rem) {
  .tmp-lib-chosen {
    display: inline;
  }
}

.tmp-lib-tally[hidden] {
  display: none;
}

/* --- the panels ---
   Lifted out of the flow, over the grid rather than pushing it down: nine
   covers moving half a screen because somebody opened a menu is the page
   losing the reader's place to answer a question they have not asked yet. */
.tmp-lib-panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 0.6rem);
  min-width: 14rem;
  max-width: min(22rem, calc(100vw - 2 * var(--gutter)));
  padding: 1.125rem 1.25rem 1.25rem;
  background: var(--ink-raised);
  border: 1px solid var(--line);
  box-shadow: 0 24px 48px rgba(0, 0, 0, 0.55);
  display: grid;
  gap: 1.125rem;
  animation: tmp-unfold 0.3s var(--ease) both;
}

/* Anchored to the right edge of its own button, because these two are pinned
   right and a panel opening rightwards would open off the screen. */
.tmp-lib-panel-end {
  right: 0;
}

/* The search is not a panel hanging off a button, it is the bar's second row:
   flush under it, the full width of it, and reading as part of the bar rather
   than as something floating over the page. */
.tmp-lib-panel-find {
  left: 0;
  right: 0;
  top: 100%;
  /* Drawn to the height the rule above reserves for it, rather than to
     whatever its contents come out at, so that the two can never disagree. */
  height: var(--tmp-find-h);
  max-width: none;
  padding: 0.875rem 0 1rem;
  /* The page's own ground rather than nothing at all. It looks the same,
     because the ground under it is the same colour, and it is what stands
     between the field and a cover if the space above is ever not reserved. */
  background: var(--ink);
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  box-shadow: none;
}

.tmp-lib-field {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  /* The line the label and the field sit on, fixed, because the panel's height
     is fixed and the two are the same fact stated at both ends. */
  min-height: 1.625rem;
}

/* The field carries no box of its own. The bar's own hairline under it is the
   line it sits on, which is one piece of furniture instead of two. */
.tmp-lib-field input {
  flex: 1;
  min-width: 0;
  /* A search input is a box with a shape of its own on iOS, taller than the
     line it holds and carrying its own clear button. The design already has a
     way to clear and close, on the pill, and a box here would be the second
     piece of furniture the note above refuses. */
  appearance: none;
  -webkit-appearance: none;
  padding: 0;
  background: none;
  border: 0;
  color: var(--bone);
  font-family: var(--ui);
  font-size: 1.0625rem;
  line-height: 1.5;
}

.tmp-lib-field input::placeholder {
  color: var(--bone-faint);
}

.tmp-lib-field input:focus {
  outline: none;
}

/* One question inside a panel: its label, then its choices. */
.tmp-lib-group {
  display: grid;
  gap: 0.625rem;
}

.tmp-lib-label {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--warm);
}

.tmp-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* A CHOICE IS A REAL RADIO BUTTON with the box drawn on the span beside it,
   and that is worth being exact about, because the obvious way to write this
   is wrong in two directions.

   Styling the label on `:has(input:checked)` reads better and is what the live
   page does. A browser without `:has` then draws no chosen state at all, so
   the bar works and shows nothing about what it is showing. The adjacent
   sibling has worked everywhere since forever.

   And the input is moved off the box rather than given `display: none`, which
   would take it out of the tab order and leave the bar unreachable from a
   keyboard. It stays in the layout at one pixel with no opacity, so it is
   focusable, and the focus ring is drawn on the span next to it. */
.tmp-chip {
  position: relative;
  display: inline-flex;
}

.tmp-chip input {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
}

.tmp-chip span {
  display: inline-block;
  padding: 0.5rem 0.9375rem;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.4;
  color: var(--bone-dim);
  border: 1px solid var(--line);
  cursor: pointer;
  transition:
    background-color 0.3s var(--ease-soft),
    color 0.3s var(--ease-soft),
    border-color 0.3s var(--ease-soft);
}

.tmp-chip:hover span {
  color: var(--bone);
  border-color: var(--bone-faint);
}

.tmp-chip input:checked + span {
  background: var(--bone);
  color: var(--ink);
  border-color: var(--bone);
}

.tmp-chip input:focus-visible + span {
  outline: 2px solid var(--bone);
  outline-offset: 3px;
}

/* Counted at the moment the page is drawn, never typed into a sentence.
   Tabular figures so the number does not jog the words beside it as the
   filters change. */
.tmp-lib-count {
  font-size: 0.8125rem;
  color: var(--bone-dim);
  font-variant-numeric: tabular-nums;
}

/* The whole foot is the submit button, and it goes only once the script has
   said it is running. It is the control a visitor with no script uses, so
   hiding it in the stylesheet and revealing it by script would be the one
   arrangement that leaves a broken page: the same bargain the reveal system
   makes in section 14. */
.tmp-lib-foot {
  padding-top: 1.25rem;
}

.js-lib .tmp-lib-foot {
  display: none;
}

.tmp-lib-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(2.25rem, 5vw, 3.5rem) clamp(1.25rem, 3vw, 2.5rem);
}

/* Three columns from 52rem and no more than three at any width. The library
   is nine works, so three columns is three full rows: a fourth column would
   leave a row of one, and the covers would be small enough to be thumbnails,
   which is the format this design took the front page out of. */
@media (min-width: 52rem) {
  .tmp-lib-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* A filtered row is hidden with the attribute rather than a class, so the
   browser does it and nothing has to run. This says the same thing the user
   agent already says, and is here because a grid item with a display of its
   own would otherwise ignore it. */
.tmp-lib-item[hidden],
.tmp-lib-reset[hidden],
.tmp-lib-empty[hidden] {
  display: none;
}

.tmp-lib-card {
  display: block;
  text-decoration: none;
  color: inherit;
}

.tmp-lib-cover {
  display: block;
  margin-bottom: 1.125rem;
}

.tmp-lib-card:hover .tmp-cover {
  transform: translateY(-10px);
  box-shadow: var(--cover-lift);
}

.tmp-lib-cat {
  display: block;
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--warm);
  margin-bottom: 0.5rem;
}

.tmp-lib-title {
  font-size: clamp(1rem, 1.7vw, 1.1875rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.02em;
  text-wrap: pretty;
  transition: color 0.3s var(--ease-soft);
}

.tmp-lib-by {
  display: block;
  margin-top: 0.4375rem;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--bone-dim);
}

/* The empty state. It happens: every work in the library is a translation
   today, so asking for the original works is a fair question with nothing to
   answer it. The words are the live page's own. */
.tmp-lib-empty {
  padding-block: clamp(3rem, 8vw, 5rem);
  text-align: center;
  font-size: 1.125rem;
  color: var(--bone-faint);
}

/* --------------------------------------------------------------------------
   14. Reveal on scroll

   Progressive enhancement in the strict sense: the rule that hides an element
   only applies once the script has marked the document, so with JavaScript
   off, or if the script fails, every element is simply visible. Hiding first
   and revealing with script is how a broken script turns into a blank page.

   THE STAGGER IS A NUMBER ON THE ELEMENT, --d, and not a class per step.
   -------------------------------------------------------------------------- */

.js-reveal .tmp-reveal {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity 0.9s var(--ease),
    transform 0.9s var(--ease);
  transition-delay: calc(var(--d, 0) * 70ms);
}

.js-reveal .tmp-reveal.is-in {
  opacity: 1;
  transform: none;
}

/* THE QUICK LANE, added 20 August 2026.

   Aamir asked for it about the contents pages: people scroll a list of
   chapters fast, and the reveal above is nine tenths of a second with a stagger
   on top of it, so a fast scroller meets rows that are still on their way in.
   A row that is not there yet is worse than no animation at all.

   So a long list takes this instead: opacity only, a fifth of a second, no
   movement and no stagger. It is small enough to be felt rather than watched,
   and the observer in shell.js starts it well before the row reaches the screen
   rather than as it crosses the edge, so by the time a row is under the eye it
   has almost always finished.

   It is a modifier rather than a second system: the hiding rule, the marking
   class and the reduced-motion escape are all the ones above, so a page whose
   script never ran is still simply visible. */
.js-reveal .tmp-reveal.is-quick {
  transform: none;
  transition: opacity 0.2s linear;
  transition-delay: 0s;
}

/* THE HEADLINE, WORD BY WORD.

   Each word sits in its own box with the overflow taken away and rises from
   under its own baseline. The box carries a little padding at the foot and an
   equal negative margin, so the mask clears the descenders without moving the
   line. The words are separate elements, but the text nodes are unchanged and
   the spaces between them are real spaces, so the heading reads, wraps and
   selects exactly as one line of prose. */
.tmp-w {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}

.tmp-w > span {
  display: inline-block;
}

/* The headline is observed like everything else, but it is the WORDS that
   move rather than the block, so the block's own fade is turned back off. */
.js-reveal .tmp-headline {
  opacity: 1;
  transform: none;
}

.js-reveal .tmp-headline .tmp-w > span {
  transform: translateY(105%);
  transition: transform 1.05s var(--ease);
  transition-delay: calc(var(--d, 0) * 80ms);
}

.js-reveal .tmp-headline.is-in .tmp-w > span {
  transform: none;
}

/* Motion is a preference the reader has already expressed to their operating
   system. Honour it: no reveal, no lift, no drift, no cue, no smooth scroll. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .js-reveal .tmp-reveal,
  .js-reveal .tmp-headline .tmp-w > span {
    opacity: 1;
    transform: none;
  }

  .tmp-cue { display: none; }
}

/* --------------------------------------------------------------------------
   15. Scroll driven animation

   No script at all: these are driven by the browser's own scroll and view
   timelines. Every one is behind BOTH a support query and a reduced-motion
   query, and every one has a resting state that is correct by itself, so a
   browser without them and a reader who has asked for less motion both get a
   page that is finished rather than one with something missing.

   The reduced-motion block above cannot be relied on to switch these off, and
   that is why they sit inside a no-preference query rather than merely being
   overridden later: a scroll driven animation takes its progress from the
   scroll position rather than from a clock, so the 0.01ms that stops every
   other animation would leave these running at whatever fraction it implies.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    /* The reading progress line, across the whole document. */
    .tmp-progress {
      animation: tmp-progress linear both;
      animation-timeline: scroll(root block);
    }

    /* The floating header settling into the bar. The range is the first few
       centimetres of scroll, so it has finished long before the hero has left
       the screen. Only the ground and the hairline arrive: the type is bone in
       both states, which is what makes this two ordinary properties rather
       than four registered colours cross fading. */
    .tmp-header-overlay {
      animation: tmp-head linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 5rem;
    }
  }
}

@keyframes tmp-progress {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@keyframes tmp-head {
  from {
    background-color: var(--ink-veil-0);
    border-bottom-color: var(--line-0);
  }
  to {
    background-color: var(--ink-veil);
    border-bottom-color: var(--line);
  }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* The fanned covers drift against the page as the hero leaves, each at its
       own rate, which is what gives the group depth once it is moving as well
       as while it is still.

       They are moved with the `translate` PROPERTY rather than with a
       transform, and that is the whole trick: translate is applied before
       transform in the same matrix, so the drift composes with the fan's own
       3D placement instead of replacing it. */
    .tmp-shelf-item {
      animation: tmp-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }

    .tmp-shelf-1 { --drift: 2.5rem; }
    .tmp-shelf-2 { --drift: 5rem; }
    .tmp-shelf-3 { --drift: 3.5rem; }

    /* Each cover in the library drifts a little against its own words as the
       row crosses the screen, so the two columns do not move as one slab. */
    .tmp-work-cover {
      animation: tmp-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
      --drift: 1.75rem;
    }
  }
}

@keyframes tmp-drift {
  from { translate: 0 var(--drift, 2rem); }
  to   { translate: 0 calc(var(--drift, 2rem) * -1); }
}

/* --------------------------------------------------------------------------
   16. The brand loader, and the cover over a cold arrival

   The drawing lives in server/src/brand-loader.js, which is where the reasons
   for every phase of it are written down. Nothing here moves anything: this is
   the palette the drawing takes, plus the cover it sits on. Every colour is a
   token, which is exactly why the whole scheme could be repointed from
   parchment and forest to ink and bone without the drawing being touched.
   -------------------------------------------------------------------------- */

.tmp-loader {
  /* The nib's glow and its sparks fly outside the mark's own box. */
  overflow: visible;
}

/* The mark itself is drawn in the page's own ivory, not in forest green: it is
   being drawn on a very dark ground. */
.tmp-loader-ink { fill: var(--bone); }
.tmp-loader-bloom { stroke: var(--warm); }

/* The nib. It reads as a hot bead: a warm halo round a pale core, which is
   what a light source looks like. On a dark ground the order runs the other
   way round from a light one, so the core is the bone and not the gold. */
.tmp-loader-halo { fill: var(--warm); }
.tmp-loader-nib { fill: var(--warm); }
.tmp-loader-core { fill: var(--bone); }
.tmp-loader-spark { fill: var(--warm); }
.tmp-loader-spark-hot { fill: var(--bone); }

/* One pass of light across the finished mark, with nothing showing at either
   edge so the band has no hard end. */
.tmp-loader-light-edge { stop-color: var(--bone); stop-opacity: 0; }
.tmp-loader-light-mid { stop-color: var(--bone); stop-opacity: 0.5; }

/* The cover is DISPLAY NONE until its script has marked the document, which is
   the same bargain the menu and the reveal make: a visitor with JavaScript
   off, and a crawler, never meet it, and a script that failed to run cannot
   leave anybody behind a cover that nothing removes. */
.tmp-splash {
  display: none;
}

.js-splash .tmp-splash {
  display: flex;
  position: fixed;
  inset: 0;
  /* Above the header, which is the highest thing on the page. */
  z-index: 200;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  opacity: 1;
  /* This duration and the one the script waits before it removes the cover are
     the same number in two places. Move them together. */
  transition: opacity 420ms ease;
}

/* Nothing scrolls underneath a cover. */
.js-splash,
.js-splash body {
  overflow: hidden;
}

.js-splash .tmp-splash.is-leaving {
  opacity: 0;
}

.tmp-splash-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* NO GAP HERE, AND THE NAME IS PULLED UP INSTEAD. The mark's own artwork
     carries about a seventh of its height as empty space below the book,
     because the nib's glow and its sparks fly outside the mark and a filter
     region clipped at the edge shows a hard corner. So a gap measured between
     the two boxes reads as roughly twice itself between the two things a
     reader actually sees. Any retuning of BOX in brand-loader.js changes what
     this has to cancel. */
  gap: 0;
  /* The name is set nowrap and starts the animation spaced wide apart, so it
     is briefly at its widest on the narrowest screen. */
  padding-inline: var(--gutter);
  /* A shade above the middle. Dead centre reads as low, because the eye puts
     the optical centre of a page above its geometric one. */
  transform: translateY(-4%);
}

.tmp-splash-mark {
  width: clamp(8rem, 34vw, 11.5rem);
  height: auto;
}

/* The name, larger than anywhere else on the site: this is the one place where
   it is the whole page rather than a label in a bar. */
.tmp-splash-name {
  /* Up into the mark's own bottom padding, not into the mark. Scaled off the
     mark's width so it tracks the artwork on every screen. */
  margin: calc(clamp(8rem, 34vw, 11.5rem) * -0.04) 0 0;
  font-family: var(--ui);
  font-weight: 700;
  font-size: clamp(1.5rem, 7vw, 2.5rem);
  letter-spacing: -0.028em;
  line-height: 1.1;
  color: var(--bone);
  white-space: nowrap;
}

/* It ARRIVES WITH THE DOME rather than sitting there from the first frame: the
   letters come up from under the mark, spaced apart and closing to their set
   width, so the mark is drawn and then named. The delay is set on the element
   itself, in brand-loader.js, because the drawing is what decides when the
   name is due.

   `both` matters twice over. It holds the first frame through the delay, so
   the name is not visible before its turn, and it holds the last one after, so
   it does not snap back when the animation ends. And this is qualified by
   .js-splash like everything else on the cover: the name on a page whose
   script never ran is simply there. */
.js-splash .tmp-splash-name {
  animation: tmp-name-in 900ms var(--ease) both;
}

@keyframes tmp-name-in {
  from {
    opacity: 0;
    transform: translateY(0.6rem);
    letter-spacing: 0.14em;
  }
  to {
    opacity: 1;
    transform: none;
    letter-spacing: -0.028em;
  }
}

/* --------------------------------------------------------------------------
   17. Print

   Someone printing a page wants the words, not a black page that empties a
   cartridge. The whole site is dark now, so print is a full inversion rather
   than the two exceptions it used to be.
   -------------------------------------------------------------------------- */

@media print {
  .tmp-header,
  .tmp-footer,
  .tmp-cue,
  .tmp-shelf,
  .tmp-progress {
    display: none;
  }

  html,
  body,
  .tmp-hero,
  .tmp-raised {
    background: #fff;
    color: #000;
  }

  .tmp-hero {
    min-height: 0;
  }

  .tmp-passage-text,
  .tmp-work-title,
  .tmp-feature-title,
  .tmp-page h1,
  .tmp-lede {
    color: #000;
  }

  .tmp-work-blurb,
  .tmp-feature-blurb,
  .tmp-page p,
  .tmp-work-by,
  .tmp-feature-by {
    color: #333;
  }
}

/* ==========================================================================
   read.css - the reading surface, and the pages that lead to it.
   ==========================================================================

   THE LIGHT SHEET ON THE DARK SITE. Aamir asked for this on 19 August 2026, in
   these words: the actual reading part of it is light, and then the text is
   dark. So the site keeps its midnight ground everywhere, and a chapter's words
   sit on paper in the middle of it.

   This is not a compromise between two schemes, it is the point of the design.
   A cover is a lit object and reads as an illuminated thing on a dark shelf,
   which is why site.css is dark. A page of a book is a printed object, and dark
   type on light paper is what four centuries of setting long-form text has
   settled on: it is what the eye tracks best over an hour, it is what every
   printed edition of these same works looks like, and it is what the PDF and
   the paperback of this text will be. A reader who moves between the web
   chapter and the printed one should not be moving between two different books.

   THE SEPARATION IS BY SCOPE, not by exception. Everything in section 3 below
   lives inside `.tmp-read`, which wraps the formatted chapter and nothing else.
   That matters for the reader's font scale when it is built: DESIGN-CONSISTENCY
   section 2.3 records that the live site achieves the same separation by
   excluding four page identifiers from every rule, which breaks the day a fifth
   page is added. Here the two never meet: no rule in site.css reaches inside
   `.tmp-read`, and no rule here reaches outside it.

   WHERE THE VALUES COME FROM. Sections 1 to 4 of DESIGN-CONSISTENCY.md are the
   specification for this surface, extracted from the live site's own
   stylesheet, and every proportion below is theirs. ONE THING IS DELIBERATELY
   DIFFERENT and it is recorded there: the live site sets its body text at 14px,
   and this sets it between 17px and 19px. 14px is small for continuous reading
   on a phone and it is the size the live site's own reader tools exist to
   escape from. Every other size is expressed as a multiple of the body size, in
   `em`, at exactly the ratio the live stylesheet uses, so the whole scale moves
   together and the five-level reader scale is one token when it is built.

   THE PALETTE IS THE PAPER'S, not the site's. The one amber in site.css is
   drawn to carry on a midnight ground and has 1.7:1 on ivory, which is
   invisible. So this file has its own accent, a deeper amber that is the same
   hue further down, and its own ink and rules. Every value is a token and every
   rule uses the token, which is the standing rule of site.css and is what let
   that file be repointed through two complete palettes without a drawing being
   touched.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* THE PAPER. A warm ivory, not white. White at this size over this many
     words is glare, and it is the wrong colour beside the covers: every one of
     the nine is a warm object. ONE value, not a set: a reading surface with
     three shades of paper on it is a reading surface with panels on it, and the
     only thing that separates the notes from the text is a hairline. */
  --paper: #f7f3ea;

  /* THE INK ON IT. A very dark warm grey rather than black, for the same
     reason the site's type is ivory rather than white: the maximum contrast is
     not the most readable contrast over a long passage. 14.6:1 on the paper,
     so it clears AAA at every size on the page.

     The dim tone carries the captions and the sign-offs, which are attributions
     rather than the text itself. 7.3:1, which clears AAA at the sizes it is
     used at and AA everywhere. */
  --read-ink: #21201d;
  --read-dim: #57534a;

  /* The quoted translation. Between the two: it is the text, but it is a
     quotation of it, and it is set italic and centred so it needs to be a
     shade quieter than the body without receding to a caption. 9.7:1. */
  --read-quote: #3f3b34;

  /* Hairlines, drawn in the ink rather than in a grey, so they sit ON the
     paper the way the site's are drawn in its own light. */
  --read-rule: rgba(33, 32, 29, 0.14);
  --read-rule-soft: rgba(33, 32, 29, 0.07);

  /* THE ACCENT ON PAPER. The site's amber is #e0a878 and it is 1.7:1 here,
     which cannot carry a footnote marker or a link. This is the same hue with
     the lightness taken down until it carries small text: 5.9:1 on the paper,
     which clears AA at any size and AAA at the sizes it is used at, which are
     a superscript number and a link. */
  --read-accent: #8a5729;
  --read-accent-soft: rgba(138, 87, 41, 0.14);

  /* THE BODY SIZE, and the one place the reading scale is set. Everything in
     section 3 is an `em` multiple of this, at the ratios the live stylesheet
     uses at its level 3, so the whole page moves when this does.

     17px on a phone, 19px on a wide screen. */
  --read-size: clamp(1.0625rem, 1rem + 0.35vw, 1.1875rem);

  /* THE READER'S OWN SCALE, sitting on top of the size above, and the three
     leadings that move with it. These are the resting values, which are
     level 3. Section 6 is where the other four levels change them, and they
     change nothing else in this file: that is the whole reason the sizes in
     section 3 are `em` multiples rather than absolute. DESIGN-CONSISTENCY.md
     section 2.3 is the specification and records what was chosen where the
     live stylesheet left a gap. */
  --read-scale: 1;
  --read-lead: 1.75;
  --read-lead-ar: 2.1;
  --read-lead-ur: 3;

  /* THE FACE'S OWN CORRECTION, one for each of the five reading faces, set by
     section 5.1 and resting at 1 for the default serif. Apparent size is
     x-height rather than em size, and the five are far apart on it, so without
     this a step on the scale above would mean a different size in every face. */
  --read-face: 1;

  /* The measure. 34rem at the body size above is a little over 70 characters,
     which is the middle of the range long-form text is comfortable at. It is
     the same token site.css uses for prose, restated here in the reading
     surface's own terms so the two can be moved apart if they ever need to be.

     IT GROWS WITH THE READING SCALE, above the default and only above it, which
     it did not before 20 August 2026 and has to now. A measure is a count of
     CHARACTERS, not a width, and the slider added that day reaches 2.187 times
     the default size: held at 34rem, the largest step would put about twenty
     six characters on a line, which is a newspaper column rather than a book.
     The growth is damped to 0.6 of the scale's own, so the line does get a
     little shorter as the type gets larger, which is what a large-print edition
     does too. Below the default nothing moves: `max()` holds the factor at 1,
     because a reader who has chosen small type wants more words on the screen
     and not a narrower ribbon of them.

     The sheet is drawn from this as well, so the paper widens with the words
     rather than the words outgrowing the paper. */
  --read-measure: 34rem;
  --read-column: calc(var(--read-measure) * max(1, 1 + (var(--read-scale) - 1) * 0.6));

  /* The paper's own inset, which is larger than the site's gutter: a sheet
     needs a margin or the type runs into the edge and stops reading as a page. */
  --read-pad: clamp(1.5rem, 6vw, 4.5rem);
}

/* --------------------------------------------------------------------------
   2. The book page
   -------------------------------------------------------------------------- */

/* NO CLOSING HAIRLINE. The Contents head directly below opens with one of its
   own, and the two were a rule, a screen's worth of nothing, and another rule.
   The section head's line is the one that stays, because it is the line that
   says what is starting rather than what has stopped. */
.tmp-bhero {
  padding-block: calc(var(--head) + clamp(2.5rem, 7vw, 5rem)) clamp(2.5rem, 6vw, 4.5rem);
  /* `.tmp-wrap` sets a measure and `.tmp-band` sets the gutter, and they are
     two separate classes in site.css. This section uses the first for its
     inner element, so the gutter has to come from here: without it the cover
     and the type run off both edges of a phone. */
  padding-inline: var(--gutter);
}

.tmp-bhero-inner {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  align-items: center;
}

@media (min-width: 46rem) {
  .tmp-bhero-inner {
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
    gap: clamp(2.5rem, 5vw, 4.5rem);
  }
}

/* The cover, at the size it deserves on the one page that is about this book
   and nothing else. It keeps the edge and the lift the library grid gives it,
   which is one hairline of the page's own light rather than a shadow: a dark
   shadow on a dark ground is nothing at all. */
.tmp-bhero-cover {
  max-width: 15rem;
  border-radius: 2px;
  overflow: hidden;
  box-shadow: var(--cover-edge);
}

.tmp-bhero-cover img {
  display: block;
  width: 100%;
  height: auto;
}

.tmp-bhero-cat {
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--warm);
}

/* THE BOOK'S TITLE IS SET IN THE READING SERIF, not in the interface face and
   not word by word like the site's own headlines. Two reasons, and the second
   is the load-bearing one. It is the book's words rather than the site's, which
   is the line site.css draws between its two faces. And the site's headline
   treatment splits a heading into one box per word for the staggered rise,
   which pages/parts.js says in terms is only ever safe on constants written in
   this project: a book title arrives from a record and may hold anything. */
.tmp-bh1 {
  margin-top: 0.75rem;
  font-family: var(--read);
  font-size: clamp(2rem, 5.5vw, 3.25rem);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.02em;
  color: var(--bone);
  text-wrap: balance;
}

.tmp-bhero-sub {
  margin-top: 0.6rem;
  font-family: var(--read);
  font-style: italic;
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  color: var(--bone-dim);
}

.tmp-bhero-by {
  margin-top: 1.25rem;
  font-family: var(--ui);
  font-size: 0.875rem;
  color: var(--bone);
}

.tmp-bhero-blurb {
  margin-top: 1.25rem;
  max-width: var(--column);
  font-size: 1rem;
  line-height: 1.7;
  color: var(--bone-dim);
}

.tmp-bhero .tmp-actions {
  margin-top: 2rem;
}

/* ---- The contents list ---------------------------------------------------

   Rows with a hairline between them, not cards. The same decision the library
   grid takes and for the same reason: a border drawn around a row is furniture
   competing with the words inside it.

   THE WHOLE ROW IS THE LINK, so the target is the full width of the measure
   rather than the length of a title. On a phone that is the difference between
   a comfortable tap and an accurate one. */

/* The section head is doing the separating here, and the hero above it has
   already ruled itself off, so this section takes about two thirds of the
   site's usual band. A full band between a book's cover and its contents is a
   screen of nothing on the one page a reader came to a list for. */
.tmp-book-contents {
  padding-block: clamp(3rem, 7vw, 5rem) var(--band);
}

/* The contents opens on no rule of its own, for the reason the numbered list
   in site.css does not: the Contents head above it has already drawn one, and
   the chapters are told apart by the rule between them. */
.tmp-toc {
  margin-top: 2.5rem;
  list-style: none;
}

.tmp-toc-item {
  border-bottom: 1px solid var(--line-soft);
}

.tmp-toc-link {
  display: grid;
  grid-template-columns: 2.75rem minmax(0, 1fr) 1.25rem;
  align-items: baseline;
  gap: 0.5rem 1rem;
  padding-block: clamp(1rem, 2.5vw, 1.5rem);
  text-decoration: none;
  color: inherit;
  transition: background-color 0.25s var(--ease-soft);
}

.tmp-toc-link:hover {
  background: var(--ink-raised);
}

/* The number is drawn, not counted by the list, because it is the chapter's
   position among the ones that can be READ rather than its position in the
   book. Aria-hidden for the same reason the section numbers on the front page
   are: it is a position on a page, not part of the sentence. */
.tmp-toc-n {
  font-family: var(--ui);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--warm);
  padding-inline-start: clamp(0rem, 2vw, 1rem);
}

.tmp-toc-title {
  font-family: var(--read);
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  font-weight: 500;
  line-height: 1.35;
  color: var(--bone);
  transition: color 0.25s var(--ease-soft);
}

/* The opening sentence of the chapter, held to a readable measure. The row
   itself runs the full width of the page, which is what makes the whole row a
   comfortable target, but a line of prose 1100 pixels long is a line nobody
   finishes: the eye loses the start of the next one. The title above it may
   run as wide as it likes, because a title is scanned rather than read. */
.tmp-toc-sum {
  grid-column: 2;
  max-width: 46rem;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--bone-faint);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.tmp-toc-go {
  color: var(--bone-faint);
  justify-self: end;
  padding-inline-end: clamp(0rem, 2vw, 1rem);
  transition: transform 0.25s var(--ease-soft), color 0.25s var(--ease-soft);
}

.tmp-toc-link:hover .tmp-toc-go {
  color: var(--warm);
  transform: translateX(3px);
}

/* ---- What is inside a chapter, folded away under it ----------------------

   Added 20 August 2026, when Aamir asked for the contents pages to carry the
   levels the live site's book pages have. There every chapter's own headings
   are listed under it in grey, always visible, and on the book with fourteen
   chapters that is two hundred and forty-five lines of grey between the reader
   and the fourteenth. So they are here, and they fold.

   A REAL `details`, styled the same way the disclosure in site.css is: the
   browser's own triangle taken off both ways it can be drawn, the site's own
   chevron in its place, and the chevron turned over when the panel opens.

   THE BAR IS QUIET ON PURPOSE. It sits under a chapter's title and it is not
   competing with it: small capitals in the interface face, the site's faint
   tone, and the count in the accent. The thing a reader came for is the
   chapter, and this is the way to look inside it without going in. */

/* Everything in the fold hangs off the title's column, so a section lines up
   under the chapter it belongs to rather than under its number. It is the
   number's column plus the gap between the two, which is what the row's own
   grid puts the title at. */
.tmp-toc-fold {
  --toc-indent: 3.75rem;
  padding-bottom: 0.5rem;
}

.tmp-toc-fold-bar {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding-block: 0.5rem;
  padding-inline-start: var(--toc-indent);
  cursor: pointer;
  /* Both halves of taking the browser's own marker off: Firefox and the
     standard property draw it from the list style, Safari and Chrome from
     their own pseudo-element. */
  list-style: none;
}

.tmp-toc-fold-bar::-webkit-details-marker {
  display: none;
}

.tmp-toc-fold-bar:focus-visible {
  outline: 2px solid var(--bone);
  outline-offset: 4px;
}

.tmp-toc-fold-name {
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
  transition: color 0.25s var(--ease-soft);
}

.tmp-toc-fold-n {
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--warm);
}

.tmp-toc-fold-bar .tmp-open-mark {
  color: var(--bone-faint);
  transition: transform 0.35s var(--ease-soft), color 0.25s var(--ease-soft);
}

.tmp-toc-fold[open] .tmp-open-mark {
  transform: rotate(180deg);
}

.tmp-toc-fold-bar:hover .tmp-toc-fold-name,
.tmp-toc-fold-bar:hover .tmp-open-mark {
  color: var(--bone);
}

/* The list itself. A hairline down the left of it, in the way an outline is
   ruled off in a printed contents, so a long fold reads as one thing that
   belongs to the chapter above rather than as loose rows between two chapters.

   It cannot animate its height without a script and does not try to. What it
   does is arrive rather than appear, the same short rise the disclosure on the
   Contact page uses, so the two read as one design. Under reduced motion the
   blanket rule in site.css section 14 cuts the duration to nothing and it is
   simply there. */
.tmp-toc-sub {
  margin-inline-start: var(--toc-indent);
  padding-inline-start: 1rem;
  border-inline-start: 1px solid var(--line-soft);
  list-style: none;
  animation: tmp-unfold 0.4s var(--ease) both;
}

/* The nested level. No rule of its own: one hairline down the fold is the
   spine, and a second one inside it would draw a diagram of the nesting rather
   than showing it. The indent and the quieter tone are what say 'inside'. */
.tmp-toc-sub-deep {
  margin-inline-start: 0;
  padding-inline-start: 1rem;
  border-inline-start: none;
  animation: none;
}

.tmp-toc-sub-link {
  display: block;
  padding-block: 0.3125rem;
  /* Held to the same measure the chapter's opening sentence is held to. Most
     headings are three or four words, but a book that sets an attribution as a
     heading has one that is a full line, and a line of prose eleven hundred
     pixels wide is a line the eye loses the end of. */
  max-width: 46rem;
  /* THE BOOK'S OWN WORDS, so the reading serif, which is the line the site
     draws between its two faces. A heading inside a chapter is the author's
     sentence and not a label this site put on it. */
  font-family: var(--read);
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--bone-dim);
  text-decoration: none;
  transition: color 0.2s var(--ease-soft);
}

.tmp-toc-sub-deep .tmp-toc-sub-link {
  font-size: 0.875rem;
  color: var(--bone-faint);
}

.tmp-toc-sub-link:hover,
.tmp-toc-sub-link:focus-visible {
  color: var(--warm);
}

/* A front matter row carries no number, so its gutter is empty. Nothing has to
   be done to draw that, and the class is here so the row can be recognised
   from the stylesheet without reading the markup: a foreword, a preface or an
   endorsement is not chapter one, and content/structure.js is where that is
   decided. */
.tmp-toc-item.is-matter .tmp-toc-title {
  font-weight: 400;
}

/* ---- A chapter that is not written yet ----------------------------------

   Listed, numbered, and plainly not available. Aamir asked on 19 August 2026
   for these to be shown rather than left out, so a book's contents is the whole
   book and a reader can see what is coming.

   It is drawn as a row that is present but inactive rather than as a row with
   something wrong with it: no strike-through, no warning colour, no icon. One
   book is 89 of these in a row, and anything louder than this would turn a plan
   into ninety complaints. */

.tmp-toc-link.is-waiting {
  cursor: default;
}

.tmp-toc-link.is-waiting:hover {
  background: none;
}

.tmp-toc-link.is-waiting .tmp-toc-n {
  color: var(--bone-faint);
}

.tmp-toc-link.is-waiting .tmp-toc-title {
  color: var(--bone-dim);
  font-weight: 400;
}

/* The state, in the arrow's column, so it reads as what is in the place where
   the way onward would be. On a narrow screen it drops under the title, because
   at that width it and the title cannot both have room. */
.tmp-toc-state {
  grid-column: 2;
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bone-faint);
  align-self: center;
}

@media (min-width: 42rem) {
  .tmp-toc-link.is-waiting {
    grid-template-columns: 2.75rem minmax(0, 1fr) auto;
  }

  .tmp-toc-state {
    grid-column: 3;
    justify-self: end;
    padding-inline-end: clamp(0rem, 2vw, 1rem);
  }
}

.tmp-toc-more {
  margin-top: 2rem;
  font-size: 0.9375rem;
  color: var(--bone-faint);
  max-width: var(--column);
}

/* --------------------------------------------------------------------------
   3. The chapter
   -------------------------------------------------------------------------- */

/* ---- The header band ----------------------------------------------------

   Dark, like the rest of the site, and this is the moment of handover: above it
   is the site, below it is the book. The live site drew this as a forest green
   panel with rounded corners and a gold gradient hairline; here it is the
   ground itself, ruled off, because a panel floating on a page that is already
   this colour is a box for the sake of a box. */

.tmp-ch-head {
  padding-block: clamp(2.25rem, 6vw, 4rem) clamp(1.75rem, 4vw, 2.75rem);
}

/* ALIGNED WITH THE SHEET BELOW IT, edge to edge, rather than with the site's
   own measure. The sheet is the reading column plus its inset either side, so
   the same expression here puts the chapter title's left edge exactly above
   the paper's left edge, and the handover reads as one object rather than as
   two blocks that nearly line up. */
.tmp-ch-inner {
  max-width: calc(var(--read-column) + var(--read-pad) * 2);
  margin-inline: auto;
}

.tmp-ch-book {
  display: inline-block;
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--warm);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s var(--ease-soft);
}

.tmp-ch-book:hover {
  border-bottom-color: var(--warm);
}

/* THE CHAPTER TITLE IS THE ONE THING ON THIS SITE SET IN PLAYFAIR. It is the
   book's own words at their largest, it is what DESIGN-CONSISTENCY section 4
   specifies, and site.css records Playfair as being kept for exactly this. */
.tmp-ch-title {
  margin-top: 0.9rem;
  font-family: 'Playfair', Georgia, serif;
  font-size: clamp(1.875rem, 5vw, 3rem);
  font-weight: 500;
  line-height: 1.12;
  letter-spacing: -0.012em;
  color: var(--bone);
  text-wrap: balance;
}

.tmp-ch-where {
  margin-top: 1rem;
  font-family: var(--ui);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--bone-faint);
}

/* ---- The navigation, above and below ------------------------------------

   Both ends, which is Aamir's instruction of 19 August 2026 and is also what
   the live site does. A reader who has finished a chapter should not have to
   scroll back to the top to find the next one, and a reader who has arrived
   from a link should not have to reach the bottom to find the contents.

   AN UNAVAILABLE DIRECTION IS SHOWN, NOT HIDDEN, so the row keeps its shape on
   the first and last chapters of a book. It is rendered as a span rather than a
   link, so there is nothing to focus and nothing to press, which is what makes
   it disabled rather than merely grey. */

.tmp-ch-nav {
  max-width: calc(var(--read-column) + var(--read-pad) * 2);
  margin-inline: auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  padding-block: clamp(1.25rem, 3vw, 2rem);
}

.tmp-ch-nav-top {
  border-bottom: 1px solid var(--line-soft);
}

.tmp-ch-nav-foot {
  border-top: 1px solid var(--line-soft);
}

@media (min-width: 42rem) {
  .tmp-ch-nav {
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 1.5rem;
  }
}

.tmp-ch-step {
  display: block;
  padding: 0.75rem 1rem;
  border: 1px solid var(--line-soft);
  border-radius: 3px;
  text-decoration: none;
  color: inherit;
  transition: border-color 0.25s var(--ease-soft), background-color 0.25s var(--ease-soft);
}

.tmp-ch-step:hover {
  border-color: var(--line);
  background: var(--ink-raised);
}

.tmp-ch-next {
  text-align: right;
}

.tmp-ch-dir {
  display: block;
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--warm);
}

.tmp-ch-name {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  /* Three lines rather than two, because these titles are long: 'Chapter Two:
     Signs of Arrogance' is three lines in half the width of a phone, and two
     would cut it in the middle of the phrase that says which chapter it is. */
  -webkit-line-clamp: 3;
  overflow: hidden;
  margin-top: 0.35rem;
  font-family: var(--read);
  font-size: 0.9375rem;
  line-height: 1.4;
  color: var(--bone);
}

/* THE TOP ROW IS THE DIRECTIONS ALONE ON A PHONE, and this is the one place
   the two navigation rows differ.

   Aamir asked for the navigation at the top and at the bottom, and it is at
   both. But a phone is 844 points tall, and a header bar, an eyebrow, a
   three-line title, a position and two boxes carrying full chapter titles come
   to more than that: the reader would arrive at a chapter and have to scroll
   before meeting a single word of it. Stripping the titles from the TOP row
   only costs nothing, because the reader is about to read this chapter rather
   than choose between them, and the row at the foot, which is where a reader
   IS choosing, keeps them in full.

   From 42rem there is room for everything and both rows are the same. */
@media (max-width: 41.999rem) {
  .tmp-ch-nav-top {
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 0.5rem;
  }

  .tmp-ch-nav-top .tmp-ch-name {
    display: none;
  }

  .tmp-ch-nav-top .tmp-ch-step {
    padding: 0.6rem 0.75rem;
    text-align: center;
  }

  /* THE ROW IS NAMED AS WELL AS THE COLUMN, and it has to be. Auto-placement
     fills left to right and the cursor never goes backwards, so an item that
     asks for column 2 AFTER one that took column 3 is placed on a second row.
     Naming the row is what keeps all three on one line. */
  .tmp-ch-nav-top .tmp-ch-toc {
    grid-row: 1;
    grid-column: 2;
    padding-inline: 0.9rem;
  }

  .tmp-ch-nav-top .tmp-ch-prev {
    grid-row: 1;
    grid-column: 1;
  }

  .tmp-ch-nav-top .tmp-ch-next {
    grid-row: 1;
    grid-column: 3;
  }
}

/* Disabled. Half the light and no interaction, and it still occupies its
   column, which is the whole reason it is drawn at all. */
.tmp-ch-step.is-off {
  opacity: 0.32;
  border-style: dashed;
}

.tmp-ch-step.is-off .tmp-ch-name {
  color: var(--bone-dim);
}

/* The contents link in the middle. On a phone it takes the full width under
   the two steps, because a third column at that width leaves three things that
   are all too narrow to read. */
.tmp-ch-toc {
  grid-column: 1 / -1;
  justify-self: center;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.1rem;
  font-family: var(--ui);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-dim);
  text-decoration: none;
  border: 1px solid var(--line-soft);
  border-radius: 999px;
  transition: color 0.25s var(--ease-soft), border-color 0.25s var(--ease-soft);
}

.tmp-ch-toc:hover {
  color: var(--bone);
  border-color: var(--line);
}

@media (min-width: 42rem) {
  /* The row is named for the reason given above: without it the middle item
     lands under the other two rather than between them. */
  .tmp-ch-toc {
    grid-row: 1;
    grid-column: 2;
  }
  .tmp-ch-prev {
    grid-row: 1;
    grid-column: 1;
  }
  .tmp-ch-next {
    grid-row: 1;
    grid-column: 3;
  }
}

/* ---- The sheet -----------------------------------------------------------

   The light ground the words are read on. It runs edge to edge on a phone,
   where a margin of dark either side would be wasted width, and becomes a sheet
   with its own edge from the width at which there is room for one.

   The lift is a real shadow here, unlike everywhere else on the site: this IS a
   light object on a dark ground, so a shadow under it does what a shadow does. */

.tmp-sheet {
  background: var(--paper);
  color: var(--read-ink);
  padding-block: clamp(2.5rem, 7vw, 4.5rem);
  padding-inline: var(--read-pad);
}

@media (min-width: 52rem) {
  .tmp-sheet {
    max-width: calc(var(--read-column) + var(--read-pad) * 2);
    margin-inline: auto;
    border-radius: 3px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
  }
}

.tmp-read {
  max-width: var(--read-column);
  margin-inline: auto;
  font-family: var(--read);
  font-size: calc(var(--read-size) * var(--read-scale) * var(--read-face));
  line-height: var(--read-lead);
  color: var(--read-ink);
  /* The transliteration diacritics and the Arabic both benefit, and the body
     face has real oldstyle figures that would otherwise never be used. */
  font-variant-numeric: oldstyle-num;
  text-rendering: optimizeLegibility;
}

/* Anything the reader lands on from a heading link clears the sticky header
   instead of hiding underneath it. */
.tmp-read [id],
.tmp-notes[id],
.tmp-ch-head {
  scroll-margin-top: calc(var(--head) + 1rem);
}

/* ---- The blocks ----------------------------------------------------------

   One rule per style in the corpus, at DESIGN-CONSISTENCY section 2.1's own
   proportions. The sizes are `em` of the body size above, so the whole scale
   moves as one. */

.tmp-read > * + * {
  margin-top: 1.15em;
}

.tmp-read > :first-child {
  margin-top: 0;
}

.tmp-p {
  text-wrap: pretty;
}

/* Arabic. Always in Arabic script, never transliterated (CLAUDE.md section
   2.4), centred, inset from both edges, and given the leading that fully
   vocalised naskh needs: the marks sit above and below the letters, so a line
   of Quranic text is physically taller than a line of Latin at the same size. */
.tmp-ar {
  font-family: 'Scheherazade New', serif;
  font-size: 1.35em;
  line-height: var(--read-lead-ar);
  text-align: center;
  margin-block: 1.75em;
  margin-inline: 6%;
  color: var(--read-ink);
}

/* Urdu, in nastaliq, which slopes down to the left and needs close to three
   times its size in leading before the lines stop colliding. */
.tmp-ur {
  font-family: 'Noto Nastaliq Urdu', 'Scheherazade New', serif;
  font-size: 1.07em;
  line-height: var(--read-lead-ur);
  text-align: center;
  margin-block: 1.75em;
  margin-inline: 6%;
  color: var(--read-ink);
}

/* The translation of a quoted passage. Italic by definition, which is why the
   importer drops the whole-paragraph emphasis 741 chapters' worth of it carried:
   the italic is the style, not something the text has to ask for. */
.tmp-qt {
  font-size: 0.93em;
  line-height: 1.75;
  font-style: italic;
  text-align: center;
  color: var(--read-quote);
  margin-inline: 8%;
}

/* The attribution under a verse or a hadith. Never under dialogue and never
   under a long extract (CLAUDE.md section 2.5). */
.tmp-cap {
  font-size: 0.8em;
  line-height: 1.6;
  font-weight: 600;
  text-align: center;
  color: var(--read-dim);
  margin-top: 0.5em;
}

/* Dialogue or a long extract. Inset from the left only, with no border and no
   quotation mark: the inset is the signal, and a vertical rule beside it is the
   kind of ornament this design is trying not to have. */
.tmp-dlg {
  margin-inline-start: 5%;
  color: var(--read-quote);
}

.tmp-sign {
  font-size: 0.86em;
  line-height: 1.6;
  font-weight: 600;
  text-align: right;
  color: var(--read-dim);
}

/* CONSECUTIVE SIBLINGS OF THE SAME STYLE TIGHTEN, and DESIGN-CONSISTENCY
   section 2.1 calls this the single thing that makes a page of poetry or a
   stacked sign-off read correctly. Two Arabic blocks in a row are one passage
   broken across lines, not two passages, and the full gap between them says the
   opposite. */
.tmp-ar + .tmp-ar,
.tmp-ur + .tmp-ur {
  margin-top: 0.5em;
}

.tmp-qt + .tmp-qt,
.tmp-cap + .tmp-cap,
.tmp-sign + .tmp-sign,
.tmp-dlg + .tmp-dlg {
  margin-top: 0.4em;
}

/* ---- Headings ------------------------------------------------------------

   The chapter's own headings, at the levels the book uses. One book nests to
   level 5 and one uses no headings at all, which is why the schema stores a
   level rather than assuming one.

   They are set in the READING serif, not the interface face: they are the
   book's words. The chapter title above is the one that takes Playfair. */

.tmp-h {
  font-family: var(--read);
  font-weight: 600;
  color: var(--read-ink);
  line-height: 1.3;
  text-wrap: balance;
}

h2.tmp-h {
  font-size: 1.6em;
  margin-top: 2.4em;
  margin-bottom: 0.7em;
}

h3.tmp-h {
  font-size: 1.35em;
  margin-top: 2em;
  margin-bottom: 0.6em;
}

h4.tmp-h {
  font-size: 1.14em;
  margin-top: 1.75em;
  margin-bottom: 0.5em;
}

h5.tmp-h {
  font-size: 1em;
  margin-top: 1.5em;
  margin-bottom: 0.4em;
}

/* Level 6 is a label rather than a heading: uppercase, letter spaced and in the
   dim tone, which is what DESIGN-CONSISTENCY section 2.2 specifies. */
h6.tmp-h {
  font-size: 0.8em;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--read-dim);
  margin-top: 1.5em;
  margin-bottom: 0.4em;
}

/* A heading is usually preceded by a paragraph, and the gap above it has to be
   larger than the gap below it or the heading reads as belonging to the text it
   follows rather than to the text it introduces. `* + *` above would give both
   the same, so these win. The first thing in a chapter takes nothing. */
.tmp-read > .tmp-h:first-child {
  margin-top: 0;
}

/* ---- Lists and tables ---------------------------------------------------- */

/* THE MARKERS ARE PUT BACK, and this is not a detail. site.css strips
   `list-style` from every list on the site, because every list OUTSIDE a
   chapter is a set of navigation links or a row of metadata where a bullet
   would be furniture. Inside a chapter the opposite is true: a numbered list is
   numbered because the author counted, and a passage reading 'and within
   necessity there are also degrees:' followed by four unnumbered lines has lost
   the thing it was saying. */
ol.tmp-list-read {
  list-style: decimal;
}

ul.tmp-list-read {
  list-style: disc;
}

.tmp-list-read {
  padding-inline-start: 1.6em;
}

.tmp-list-read li + li {
  margin-top: 0.5em;
}

.tmp-list-read li::marker {
  color: var(--read-dim);
}

/* A table of five columns cannot be made to fit a phone, so it scrolls inside
   its own box rather than making the whole page scroll sideways. */
.tmp-table-wrap {
  overflow-x: auto;
  margin-block: 1.75em;
  border-top: 1px solid var(--read-rule);
  border-bottom: 1px solid var(--read-rule);
}

.tmp-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.86em;
  font-variant-numeric: tabular-nums;
}

.tmp-table th,
.tmp-table td {
  padding: 0.6em 0.9em;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--read-rule-soft);
}

.tmp-table th {
  font-weight: 600;
  white-space: nowrap;
  color: var(--read-dim);
  font-size: 0.9em;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.tmp-table tbody tr:last-child td {
  border-bottom: 0;
}

/* ---- Inline --------------------------------------------------------------- */

.tmp-read em {
  font-style: italic;
}

.tmp-read strong {
  font-weight: 700;
}

.tmp-read a {
  color: var(--read-accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

.tmp-read ::selection {
  background: var(--read-accent-soft);
}

/* THE HONORIFIC. A single letter set in the commissioned face, which draws the
   whole invocation as one glyph. It is a key into a font and not a word, which
   is why it takes no colour of its own and why it is silent to a screen reader
   (see format/web.js).

   Raised very slightly and given a hair of space either side: the glyph is
   drawn to sit beside a name rather than inside a word. */
.tmp-hon {
  font-family: 'Honorifics', serif;
  font-size: 1.1em;
  line-height: 1;
  display: inline-block;
  vertical-align: -0.02em;
  margin-inline: 0.1em;
  color: inherit;
  /* It must never be italicised or emboldened by the run it sits inside: the
     face has one weight and one style, and a browser asked for the others
     synthesises them by slanting and smearing the glyph. */
  font-style: normal;
  font-weight: 400;
}

.tmp-ar-inline {
  font-family: 'Scheherazade New', serif;
  font-size: 1.15em;
  line-height: 1;
}

/* ---- Footnotes ----------------------------------------------------------- */

.tmp-fn-ref {
  font-size: 0.7em;
  line-height: 0;
  vertical-align: super;
}

.tmp-fn-ref a {
  color: var(--read-accent);
  text-decoration: none;
  padding-inline: 0.15em;
  font-variant-numeric: lining-nums;
}

.tmp-fn-ref a:hover {
  text-decoration: underline;
}

/* The divider above the notes is this border and nothing else. The corpus
   carries a separator block for it and the importer drops it, because a
   chapter that stored its own divider could end up with a divider and no
   notes. */
.tmp-notes {
  margin-top: 3.5em;
  padding-top: 1.75em;
  border-top: 1px solid var(--read-rule);
}

.tmp-notes-head {
  font-family: var(--ui);
  font-size: 0.7em;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--read-dim);
}

.tmp-fn-list {
  margin-top: 1.25em;
  list-style: none;
}

.tmp-fn {
  display: grid;
  grid-template-columns: 1.75em minmax(0, 1fr);
  gap: 0.5em;
  font-size: 0.86em;
  line-height: 1.6;
  color: var(--read-quote);
}

.tmp-fn + .tmp-fn {
  margin-top: 0.9em;
}

/* The number IS the way back. One control rather than a number and a separate
   arrow beside it, and the target is the whole numeral rather than a glyph. */
.tmp-fn-back {
  font-variant-numeric: lining-nums;
  font-weight: 600;
  color: var(--read-accent);
  text-decoration: none;
}

.tmp-fn-back:hover {
  text-decoration: underline;
}

/* A note that has just been jumped to is marked for a moment, so a reader who
   pressed a marker can see which of eleven notes answered. It fades rather than
   staying, because a permanent highlight on one note is a page that looks
   wrong to the next reader who scrolls past it. */
.tmp-fn:target .tmp-fn-body {
  animation: tmp-fn-found 2.4s var(--ease-soft) both;
}

@keyframes tmp-fn-found {
  0%,
  45% {
    background: var(--read-accent-soft);
    box-shadow: 0 0 0 0.35em var(--read-accent-soft);
    border-radius: 2px;
  }
  100% {
    background: transparent;
    box-shadow: 0 0 0 0.35em transparent;
    border-radius: 2px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .tmp-fn:target .tmp-fn-body {
    animation: none;
    background: var(--read-accent-soft);
  }
}

/* --------------------------------------------------------------------------
   4. The gate
   --------------------------------------------------------------------------

   One password in front of every book, and since 20 August 2026 rather more
   than that. The door names the book and the chapter the reader was opening,
   says why it is shut, offers to write to them when it opens and offers a way
   to ask for early access. The password comes last, under a rule, because
   almost nobody who reaches this page has one.

   IT IS STILL A NARROW COLUMN AND NOT A CARD. The site draws no cards anywhere
   else, and a box floated in the middle of the ground would be the one piece of
   furniture on the site borrowed from the kit this design replaced. The order
   down the column is the hierarchy; the hairlines are the only division. */

.tmp-gate {
  max-width: 30rem;
  margin-inline: auto;
  padding-block: calc(var(--head) + clamp(3rem, 10vw, 6rem)) var(--band);
  padding-inline: var(--gutter);
  text-align: center;
}

.tmp-gate-mark {
  width: 3rem;
  margin-inline: auto;
  color: var(--warm);
}

.tmp-gate-mark svg {
  width: 100%;
  height: auto;
}

.tmp-gate h1 {
  margin-top: 1.75rem;
  /* Inter, not the reading serif. This heading is the SITE speaking, not a
     book: the serif on this site means you are reading a book, and a door is
     the one thing on the page that is certainly not one. */
  font-family: var(--ui);
  font-size: clamp(1.75rem, 5vw, 2.5rem);
  letter-spacing: -0.024em;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.018em;
  color: var(--bone);
}

.tmp-gate-lede {
  margin-top: 1rem;
  font-size: 1rem;
  line-height: 1.65;
  color: var(--bone-dim);
}

.tmp-gate-form {
  margin-top: 1.5rem;
  display: grid;
  gap: 0.75rem;
}

/* The invitation above the field reads as small print, so it is centred with
   the rest of it, and the label below it starts the form proper. */
.tmp-gate-form .tmp-gate-fine {
  margin-top: 0;
  margin-bottom: 0.5rem;
}

.tmp-gate-form label {
  text-align: left;
  font-family: var(--ui);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-dim);
}

.tmp-gate-form input {
  width: 100%;
  padding: 0.85rem 1rem;
  font-family: var(--ui);
  font-size: 1rem;
  color: var(--bone);
  background: var(--ink-raised);
  border: 1px solid var(--line);
  border-radius: 3px;
  transition: border-color 0.25s var(--ease-soft);
}

.tmp-gate-form input:focus-visible {
  outline: none;
  border-color: var(--warm);
}

.tmp-gate-form .tmp-act {
  justify-content: center;
  margin-top: 0.5rem;
}

/* The refusal. Marked in the same rose the contact form uses, and the SENTENCE
   is what says what is wrong: about one man in twelve cannot tell the rose from
   the amber, so the colour is never the message on its own. */
.tmp-gate-form.is-wrong input {
  border-color: var(--warn);
}

.tmp-gate-wrong {
  font-size: 0.9375rem;
  color: var(--warn);
  text-align: left;
}

/* The book, above the chapter. Same eyebrow as every section on the site, so a
   reader who has seen the front page already knows what a line like this is. */
.tmp-gate-eyebrow {
  margin-top: 1.75rem;
  font-family: var(--ui);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--warm);
}

/* With an eyebrow above it the heading no longer needs the space of its own. */
.tmp-gate-eyebrow + h1 {
  margin-top: 0.75rem;
}

/* --- The waiting list ---------------------------------------------------- */

.tmp-gate-ask {
  margin-top: 2.25rem;
  padding-top: 2rem;
  border-top: 1px solid var(--line);
  text-align: left;
}

.tmp-gate-label {
  display: block;
  font-family: var(--ui);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bone-dim);
  text-align: center;
}

/* The field and the button on one line above 30rem and stacked below it. The
   button is `flex: none` so it keeps the width of its own words: a Notify me
   that grows and shrinks with the viewport reads as a different button on every
   screen. */
.tmp-gate-row {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.tmp-gate-row input {
  flex: 1;
  min-width: 0;
  width: 100%;
  padding: 0.85rem 1rem;
  font-family: var(--ui);
  font-size: 1rem;
  color: var(--bone);
  background: var(--ink-raised);
  border: 1px solid var(--line);
  border-radius: 3px;
  transition: border-color 0.25s var(--ease-soft);
}

.tmp-gate-row input::placeholder {
  color: var(--bone-faint);
}

.tmp-gate-row input:focus-visible {
  outline: none;
  border-color: var(--warm);
}

.tmp-gate-row .tmp-act {
  flex: none;
  justify-content: center;
}

.tmp-gate-row.is-wrong input {
  border-color: var(--warn);
}

@media (max-width: 30rem) {
  .tmp-gate-row {
    display: grid;
  }
}

/* What the address is for, and the invitation above the password. One class for
   both, because they are the same voice: the site's small print. */
.tmp-gate-fine {
  margin-top: 0.75rem;
  font-size: 0.8125rem;
  line-height: 1.55;
  color: var(--bone-faint);
  text-align: center;
}

/* The answer to a posted address. The SENTENCE is what says what happened: about
   one man in twelve cannot tell the rose from the amber, so the colour never
   carries the message on its own. It is focusable so that a reader with no
   script is brought to it rather than left at the top of a page that looks
   unchanged. */
.tmp-gate-said {
  margin-top: 1.25rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--bone-dim);
}

.tmp-gate-said:focus-visible {
  outline: none;
}

.tmp-gate-said.is-ok {
  color: var(--bone);
}

.tmp-gate-said.is-wrong {
  color: var(--warn);
}

/* --- Asking for the way in ----------------------------------------------- */

.tmp-gate-request {
  margin-top: 1.75rem;
}

/* --- The rule the password sits under ------------------------------------ */

.tmp-gate-or {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  margin-top: 2.5rem;
}

.tmp-gate-or::before,
.tmp-gate-or::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
}

.tmp-gate-or span {
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-faint);
}

.tmp-gate-note {
  margin-top: 2rem;
  font-size: 0.875rem;
  color: var(--bone-faint);
}

.tmp-gate-note a {
  color: var(--bone-dim);
}

/* --------------------------------------------------------------------------
   5. The reader's tools
   --------------------------------------------------------------------------

   The type scale, the paper tint, the dyslexia face, focus mode, the bookmarks
   and the reading place. reader.js is the behaviour; every size, colour and
   position is here, and that split is what makes a change to the reading scale
   a one-file change (CLAUDE.md section 3).

   THEY ARE THE SITE SPEAKING, NOT THE BOOK, and the design says so on purpose.
   Everything in section 3 is warm paper, dark ink and Source Serif, because it
   is a printed page. Every control below is the site's own midnight ground,
   bone type and Inter, because it is the site's furniture resting on top of
   that page. site.css states the rule the two halves of this file already
   keep: the serif means you are reading a book, and anything the site says
   about itself takes the other face. A panel of settings set in the reading
   serif on the reading paper would read as part of the chapter.

   ONE LAYOUT AT EVERY WIDTH, which is the second shape these tools have had.
   The first put the rail at the bottom right on a phone and in the middle of
   the right margin on a laptop, and opened each panel from the bottom edge or
   beside the button that called it. Aamir asked on 20 August 2026 for the live
   site's arrangement instead: the rail held against the TOP RIGHT with a tab
   that folds it away, and the panels in the MIDDLE of the screen rather than
   along its bottom edge, narrower than they were. So there is one rail
   position, one panel position, and the breakpoints below are only about how
   much room each has. The live site's tools were hidden outright above 768px;
   DESIGN-CONSISTENCY.md section 3 records that as a limitation of injecting
   into a theme rather than a decision, and somebody reading a long chapter on a
   laptop wants larger type as much as somebody on a phone does.

   NOTHING IS DRAWN UNTIL THE SCRIPT SAYS SO. Every rule that shows a control
   is written under `.js-reader`, which only the arming script in shell.js ever
   adds. So a browser that never runs the script gets a chapter with no
   controls rather than a chapter with controls that do nothing, and that is
   the same bargain site.css makes for the menu and for the reveal.
   -------------------------------------------------------------------------- */

/* ---- 5.1 The settings, applied to the sheet -----------------------------

   THE READING SCALE IS ONE MULTIPLIER, not a table of sizes. The live site
   sets forty-odd absolute pixel values per level, which is why its scale has
   holes in it: a style nobody remembered to list at level 4 simply does not
   move. Here section 3 is already `em` multiples of one body size, so the whole
   page scales from a single number and no style can be left behind.

   THIRTY-ONE STEPS, NOT FIVE, since 20 August 2026. It was five named levels,
   XS to XL, and Aamir asked for two things at once: for XL to go further than
   it did, and for a slider under the five that carries on past both ends. So
   the five names are still there and are still what most readers will use, but
   they are now POSITIONS ON A SCALE rather than the whole of it. XS is step 0,
   the default is step 10, XL is step 20, and the slider runs from -5 to 25:
   five steps below the smallest named size and five above the largest.

   THE NUMBERS BELOW ARE GENERATED, not typed. The five named levels are the
   anchors, and every step between them is a geometric interpolation of the
   scale with a linear one of the three leadings; the ten steps outside the
   named range carry the end segment's own ratio onward, so the slider's extra
   travel feels like the same scale continuing rather than a different one
   bolted on. The generator is recorded in the change log entry for 20 August
   2026; this table is its output, kept here because the stylesheet is where
   every size on this site lives (CLAUDE.md section 3) and because one attribute
   is then all the arming script in shell.js has to set before the first paint.

   THE ANCHORS. The live site's own levels are 11, 12, 14, 17 and 22 pixels
   against a default of 14, which is 0.79, 0.86, 1, 1.21 and 1.57. This site's
   default is 17px to 19px rather than 14px, because 14px is small for
   continuous reading and is the size the live site's reader tools exist to
   escape from. The four lower anchors are unchanged from the five-level scale
   this replaces, so nobody's saved size moves under them: 0.8, 0.89, 1 and 1.2.
   XL WAS 1.45 AND IS NOW 1.62, which is what Aamir asked for, and the slider
   reaches 2.187 above it: about 41px on a wide screen, which is a large-print
   edition and is meant to be.

   THE LEADING GOES THE OTHER WAY at the top of the scale, and that is not a
   slip. Leading is a proportion of the size, so type set half as large again at
   the same ratio gets half as much air again between the lines, which is too
   much: the lines stop reading as a paragraph. The live stylesheet does the
   same thing at the same place, taking Arabic from 2.4 at level 3 down to 2.0
   at level 4, and DESIGN-CONSISTENCY.md section 2.3 records that as deliberate
   rather than as an error. Latin body text is the exception and grows very
   slightly throughout, which is what the live site does as well.

   The Urdu leading is the one that cannot be cut far: nastaliq slopes down to
   the left, and below about 2.5 its lines collide with each other. So it is
   held at 2.5 across the top of the slider rather than being allowed to follow
   the slope down.

   Step 10 is the default and carries no rule at all, which is why reader.js
   removes the attribute rather than setting it to 10. Its values are the tokens
   in section 1. */

:root[data-read-step='-5'] { --read-scale: 0.719; --read-lead: 1.68; --read-lead-ar: 1.9; --read-lead-ur: 2.65; }
:root[data-read-step='-4'] { --read-scale: 0.735; --read-lead: 1.68; --read-lead-ar: 1.91; --read-lead-ur: 2.67; }
:root[data-read-step='-3'] { --read-scale: 0.75; --read-lead: 1.69; --read-lead-ar: 1.92; --read-lead-ur: 2.69; }
:root[data-read-step='-2'] { --read-scale: 0.767; --read-lead: 1.69; --read-lead-ar: 1.93; --read-lead-ur: 2.71; }
:root[data-read-step='-1'] { --read-scale: 0.783; --read-lead: 1.7; --read-lead-ar: 1.94; --read-lead-ur: 2.73; }
:root[data-read-step='0'] { --read-scale: 0.8; --read-lead: 1.7; --read-lead-ar: 1.95; --read-lead-ur: 2.75; }
:root[data-read-step='1'] { --read-scale: 0.817; --read-lead: 1.7; --read-lead-ar: 1.96; --read-lead-ur: 2.77; }
:root[data-read-step='2'] { --read-scale: 0.835; --read-lead: 1.71; --read-lead-ar: 1.97; --read-lead-ur: 2.79; }
:root[data-read-step='3'] { --read-scale: 0.853; --read-lead: 1.71; --read-lead-ar: 1.98; --read-lead-ur: 2.81; }
:root[data-read-step='4'] { --read-scale: 0.871; --read-lead: 1.72; --read-lead-ar: 1.99; --read-lead-ur: 2.83; }
:root[data-read-step='5'] { --read-scale: 0.89; --read-lead: 1.72; --read-lead-ar: 2; --read-lead-ur: 2.85; }
:root[data-read-step='6'] { --read-scale: 0.911; --read-lead: 1.73; --read-lead-ar: 2.02; --read-lead-ur: 2.88; }
:root[data-read-step='7'] { --read-scale: 0.932; --read-lead: 1.73; --read-lead-ar: 2.04; --read-lead-ur: 2.91; }
:root[data-read-step='8'] { --read-scale: 0.954; --read-lead: 1.74; --read-lead-ar: 2.06; --read-lead-ur: 2.94; }
:root[data-read-step='9'] { --read-scale: 0.977; --read-lead: 1.74; --read-lead-ar: 2.08; --read-lead-ur: 2.97; }
:root[data-read-step='11'] { --read-scale: 1.037; --read-lead: 1.76; --read-lead-ar: 2.07; --read-lead-ur: 2.94; }
:root[data-read-step='12'] { --read-scale: 1.076; --read-lead: 1.77; --read-lead-ar: 2.04; --read-lead-ur: 2.88; }
:root[data-read-step='13'] { --read-scale: 1.116; --read-lead: 1.78; --read-lead-ar: 2.01; --read-lead-ur: 2.82; }
:root[data-read-step='14'] { --read-scale: 1.157; --read-lead: 1.79; --read-lead-ar: 1.98; --read-lead-ur: 2.76; }
:root[data-read-step='15'] { --read-scale: 1.2; --read-lead: 1.8; --read-lead-ar: 1.95; --read-lead-ur: 2.7; }
:root[data-read-step='16'] { --read-scale: 1.274; --read-lead: 1.81; --read-lead-ar: 1.93; --read-lead-ur: 2.67; }
:root[data-read-step='17'] { --read-scale: 1.353; --read-lead: 1.82; --read-lead-ar: 1.91; --read-lead-ur: 2.64; }
:root[data-read-step='18'] { --read-scale: 1.437; --read-lead: 1.83; --read-lead-ar: 1.89; --read-lead-ur: 2.61; }
:root[data-read-step='19'] { --read-scale: 1.526; --read-lead: 1.84; --read-lead-ar: 1.87; --read-lead-ur: 2.58; }
:root[data-read-step='20'] { --read-scale: 1.62; --read-lead: 1.85; --read-lead-ar: 1.85; --read-lead-ur: 2.55; }
:root[data-read-step='21'] { --read-scale: 1.72; --read-lead: 1.86; --read-lead-ar: 1.83; --read-lead-ur: 2.52; }
:root[data-read-step='22'] { --read-scale: 1.827; --read-lead: 1.87; --read-lead-ar: 1.81; --read-lead-ur: 2.5; }
:root[data-read-step='23'] { --read-scale: 1.94; --read-lead: 1.88; --read-lead-ar: 1.79; --read-lead-ur: 2.5; }
:root[data-read-step='24'] { --read-scale: 2.06; --read-lead: 1.89; --read-lead-ar: 1.77; --read-lead-ur: 2.5; }
:root[data-read-step='25'] { --read-scale: 2.187; --read-lead: 1.9; --read-lead-ar: 1.75; --read-lead-ur: 2.5; }

/* THE TINT IS THE PAPER, and it is scoped to the sheet rather than set on the
   root. `--paper` is used by exactly one rule outside this section, the sheet's
   own background, so redefining it on that element is the whole of the change
   and it cannot leak on to a page that has no sheet on it.

   Three tints, retuned rather than copied across. The live site's are laid
   over a white page; this paper is already a warm ivory, so its warm tint has
   to go further to read as a change at all, and its cool one has to stay close
   or the sheet stops looking like paper. Each is held above 13:1 against the
   ink, so no tint costs a reader any contrast worth the name.

   THE VALUE IS NAMED ON THE ROOT AND HANDED DOWN, which is a change made on 20
   August 2026 for the focus beam below. The beam washes the page in the colour
   of the paper it is lying on, and it is drawn by an element the old tint rules
   could not reach. One named value read in two places cannot drift; two hex
   codes for one colour would. */

:root { --read-wash: #f7f3ea; }
:root[data-read-tint='warm'] { --read-wash: #f7ecd3; }
:root[data-read-tint='cool'] { --read-wash: #eef1f4; }
:root[data-read-tint='sage'] { --read-wash: #ecf1ea; }

.tmp-sheet {
  --paper: var(--read-wash);
}

/* ---- The reading faces ---------------------------------------------------

   FIVE, SINCE 20 AUGUST 2026, where there were two, because Aamir asked for
   more. The set is chosen so that each is a different KIND of face rather than
   a different version of the same one: a transitional serif, a screen serif, an
   old style serif, a sans, and the dyslexia face.

   EACH IS NUDGED BY ITS OWN X-HEIGHT. Apparent size is x-height, not em size,
   and these are far apart on it: measured off the fonts themselves, Source
   Serif 4 is 0.475 of the em, Literata 0.507, EB Garamond 0.400 and Inter
   0.546. Set at one font size, EB Garamond reads a fifth smaller than Inter,
   and a step on the reading scale would then mean something different for every
   face. So each carries a factor, applied on top of the reader's chosen step,
   at about two thirds of the full correction: correcting the whole way makes
   the five faces the same size and takes their character with it.

   THE STACK BEHIND EACH ONE IS THE DEFAULT SERIF, not a system font, and that
   is what covers U+02BE and U+02BF for the dyslexia face, which does not draw
   them and which the corpus uses about three thousand times. The Latin faces
   here were each checked codepoint by codepoint against the unsubsetted file
   before being added, the way Playfair was: the dot-below letters, the macron
   vowels and the two marks are present in all of them. */

:root[data-read-font='book'] .tmp-read {
  --read-face: 0.96;
  font-family: 'Literata', var(--read);
}

:root[data-read-font='garamond'] .tmp-read {
  --read-face: 1.12;
  font-family: 'EB Garamond', var(--read);
}

/* The sans. It is the site's own interface face, so it costs a reader nothing
   to choose: every chapter has already loaded it for the header and the
   navigation. */
:root[data-read-font='sans'] .tmp-read {
  --read-face: 0.92;
  font-family: var(--ui);
}

/* THE DYSLEXIA FACE. OpenDyslexic, self hosted with everything else; the live
   site pulls it off a public CDN on every chapter view.

   The three settings move together because that is the finding they come from:
   the face alone is only part of it, and loosening the letters, the words and
   the lines is the rest. The leading is added to whatever the reader's chosen
   step already gives, so the two settings compose instead of one overwriting
   the other.

   THE ARABIC AND THE URDU ARE PUT BACK. Their own rules already name their own
   faces, so the family never reaches them, but letter spacing and word spacing
   are inherited, and spacing forced between the letters of a joined script
   breaks the joins: a line of Arabic would come apart into separate letters.

   TWO CHARACTERS FALL THROUGH TO THE SERIF, U+02BE and U+02BF, the hamza and
   the ayn, which the face does not draw and which the corpus uses about three
   thousand times between them. That is what the second name in the stack is
   for, and it is why there is no unicode-range on the @font-face; the reasons
   are in tools/brand/fetch-fonts.mjs. */

:root[data-read-font='dyslexic'] .tmp-read {
  font-family: 'OpenDyslexic', var(--read);
  letter-spacing: 0.012em;
  word-spacing: 0.14em;
  line-height: calc(var(--read-lead) + 0.15);
}

:root[data-read-font='dyslexic'] .tmp-ar,
:root[data-read-font='dyslexic'] .tmp-ur,
:root[data-read-font='dyslexic'] .tmp-ar-inline {
  letter-spacing: normal;
  word-spacing: normal;
}

/* ONLY THE LATIN IS SWAPPED, and no rule here is needed to make that true.
   `.tmp-ar`, `.tmp-ur` and `.tmp-ar-inline` each name their own face in section
   3, so a family set on `.tmp-read` never reaches a verse: a sans or an old
   style serif has nothing to say about either script, and those two faces are
   the only ones here that draw vocalised Arabic properly at all (CLAUDE.md
   section 2.4). The dyslexia rule above has to put the letter and word spacing
   back only because spacing IS inherited where a family is not. */

/* ---- Focus mode ----------------------------------------------------------

   TWO SHAPES SINCE 20 AUGUST 2026, AND THE READER CHOOSES. It was one: the
   block at the reading line stayed lit and every other block was dimmed. Aamir
   asked for a line by line guide as well, and for that to be the default,
   because a paragraph in these books is often the length of a screen and
   dimming everything except a screenful of text is not focus.

   BY THE PARAGRAPH is the original, and it is opacity rather than a lighter
   ink, so it takes the whole block with it, including an Arabic passage and a
   footnote marker, and so it costs one composited property rather than a
   repaint per paragraph.

   0.28 is far enough down that the eye stops trying to read the neighbours and
   near enough that the shape of the page is still there: a reader has to be
   able to see that there is more, or the setting reads as a fault. */

:root[data-read-focus='on'][data-read-beam='para'] .tmp-read > * {
  opacity: 0.28;
  transition: opacity 0.35s var(--ease-soft);
}

:root[data-read-focus='on'][data-read-beam='para'] .tmp-read > .is-lit {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  :root[data-read-focus='on'][data-read-beam='para'] .tmp-read > * {
    transition: none;
  }
}

/* BY THE LINE is a beam: two washes of the paper's own colour laid over the
   sheet with a gap between them, and the gap is as many lines tall as the
   reader has asked for.

   IT IS A WASH RATHER THAN A CUT because the rest of the page has to be dimmed,
   not hidden, for the same reason the paragraph above is dimmed rather than
   hidden. 0.82 of the paper leaves the words underneath as a ghost.

   IT IS DRAWN INSIDE THE SHEET, positioned against it, and that is what keeps
   it honest at no cost. It inherits the paper's colour including the reader's
   chosen tint, it stops at the sheet's own edges so the site's dark ground and
   its header are never washed, it takes the sheet's corner radius, and it
   scrolls with the page rather than having to be repositioned every frame.
   `pointer-events: none` so a reader can still select the words through it.

   THE TWO HEIGHTS ARE THE ONLY THING THE SCRIPT SETS, as custom properties in
   the sheet's own coordinates, because they are the one part that cannot be
   known here: where a line box falls depends on the face, the step on the
   scale, the width of the window and where the reader has scrolled to.
   server/public/reader.js measures the real line boxes and hands the answer
   over. */

.tmp-sheet {
  position: relative;
}

.tmp-beam {
  display: none;
}

:root[data-read-focus='on'][data-read-beam='lines'] .tmp-beam {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  overflow: hidden;
  /* Held back until the script has measured a first band. Without it the two
     washes meet at the top of the sheet for one frame and the whole chapter
     flashes as focus mode is switched on. */
  visibility: hidden;
}

:root[data-read-focus='on'][data-read-beam='lines'] .tmp-beam.is-set {
  visibility: visible;
}

.tmp-beam-top,
.tmp-beam-bot {
  position: absolute;
  left: 0;
  right: 0;
  background: var(--paper);
  opacity: 0.82;
  transition: top 0.18s var(--ease-soft), height 0.18s var(--ease-soft);
}

.tmp-beam-top {
  top: 0;
  height: var(--beam-top, 0px);
}

.tmp-beam-bot {
  top: var(--beam-bot, 100%);
  bottom: 0;
}

@media (prefers-reduced-motion: reduce) {
  .tmp-beam-top,
  .tmp-beam-bot {
    transition: none;
  }
}


/* ---- 5.2 The rail --------------------------------------------------------

   Three buttons: the reading settings, the bookmarks, and the chapter with the
   progress ring drawn around it. The third is the live site's progress circle,
   which was display only, finally given something to do. Above them sits the
   tab that folds the three away and brings them back.

   It is `fixed` rather than sticky. Sticky would keep it inside the article
   and therefore inside the sheet, which is the light object it must not look
   like a part of. */

.tmp-rail,
.tmp-panel,
.tmp-scrim,
.tmp-pop,
.tmp-toast {
  display: none;
}

/* HELD AGAINST THE TOP RIGHT, as a card whose right edge is the screen's own,
   which is the live site's arrangement and is what Aamir asked for. It is a
   column, at every width: the tab has to sit above what it folds, and three
   buttons in a column against the right edge cover a strip of margin rather
   than a band across the words.

   It clears the header rather than guessing at it. `--head` is the one place
   the header's height is written down (site.css section 1), so the rail moves
   with it instead of drifting the first time the header is touched. The header
   is z-index 50 and this is 45, so the two never fight: on a page scrolled to
   the top the rail is simply underneath it, which is where the header belongs.

   The unrounded right side is not a saving. A card that meets the edge of the
   screen reads as attached to it, which is what makes the tab beside it look
   like a thing that slides rather than a fourth button. */

.js-reader .tmp-rail {
  position: fixed;
  z-index: 45;
  top: calc(var(--head) + 0.6rem);
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.4rem;
  padding-right: calc(0.4rem + env(safe-area-inset-right, 0px));
  border: 1px solid var(--line);
  border-right: 0;
  border-radius: 1.75rem 0 0 1.75rem;
  /* The CARD takes the darker ground and everything in it takes the raised
     one, which is the opposite way round from the buttons' first arrangement
     and has to be: they used to float free over the sheet, where a raised
     ground was what lifted them off it. Inside a card they need to be lighter
     than what they sit in or they are three outlines on one flat shape. */
  background: var(--ink);
  box-shadow: -4px 8px 24px rgba(0, 0, 0, 0.35);
  transition: border-radius 0.24s var(--ease);
}

/* Folded, the card is a tab and nothing else, so it stops pretending to be a
   round-ended rail of buttons. */
:root[data-read-tools='shut'] .tmp-rail {
  border-radius: 0.75rem 0 0 0.75rem;
}

/* THE TAB. Wide as a button and a third as tall, so it reads as a handle on
   the card rather than as another control competing with the three. The
   chevron points right, the way the buttons go when they fold away against the
   edge; folded, it is turned to point back out at the page, which is where
   pressing it brings them. */
.tmp-rail-pull {
  width: 2.75rem;
  height: 1.2rem;
  margin-bottom: 0.4rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 0.6rem;
  background: var(--ink-raised);
  color: var(--bone-dim);
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.2s var(--ease-soft), color 0.2s var(--ease-soft),
    margin-bottom 0.24s var(--ease);
}

.tmp-rail-pull:hover {
  background: var(--ink-hover);
  color: var(--bone);
}

.tmp-rail-pull:focus-visible {
  outline: 2px solid var(--warm);
  outline-offset: 2px;
}

.tmp-rail-pull svg {
  display: block;
  transition: rotate 0.24s var(--ease);
}

:root[data-read-tools='shut'] .tmp-rail-pull {
  margin-bottom: 0;
}

:root[data-read-tools='shut'] .tmp-rail-pull svg {
  rotate: 180deg;
}

/* WHAT FOLDS. `max-height` rather than `display`, so there is something to
   animate, and `visibility` alongside it so that folded buttons are out of the
   tab order and out of a screen reader's way rather than merely invisible. The
   visibility is delayed by exactly the length of the fold on the way out and
   immediate on the way in, which is the whole reason it is written twice. */
.tmp-rail-btns {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  overflow: hidden;
  max-height: 12rem;
  visibility: visible;
  transition: max-height 0.28s var(--ease), opacity 0.2s var(--ease-soft),
    visibility 0s;
}

:root[data-read-tools='shut'] .tmp-rail-btns {
  max-height: 0;
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  transition: max-height 0.28s var(--ease), opacity 0.2s var(--ease-soft),
    visibility 0s 0.28s;
}

@media (prefers-reduced-motion: reduce) {
  .js-reader .tmp-rail,
  .tmp-rail-pull,
  .tmp-rail-pull svg,
  .tmp-rail-btns {
    transition-duration: 0s;
    transition-delay: 0s;
  }
}

.tmp-rail-btn {
  position: relative;
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--ink-raised);
  color: var(--bone);
  font-family: var(--ui);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  cursor: pointer;
  /* NO SHADOW OF THEIR OWN ANY MORE. They used to carry a real one, because
     they sat over the light sheet as often as over the dark ground and a
     hairline disappears against paper. The card they are now inside carries
     that shadow instead, and a shadow drawn in here would be cut off square by
     the clipping that lets the buttons fold. */
  transition: background 0.2s var(--ease-soft), color 0.2s var(--ease-soft),
    border-color 0.2s var(--ease-soft);
}

.tmp-rail-btn:hover {
  background: var(--ink-hover);
  border-color: var(--bone-faint);
}

/* THE OPEN STATE IS THE ACCENT, which is the one strong colour this design
   allows itself (site.css section 1). It is the only place on a chapter page
   where the amber is a ground rather than type, and that is what makes it read
   as 'this one is on' at a glance from the far side of a phone. */
.tmp-rail-btn.is-on {
  background: var(--warm);
  border-color: var(--warm);
  color: var(--ink);
}

.tmp-rail-btn:focus-visible {
  outline: 2px solid var(--warm);
  outline-offset: 3px;
}

.tmp-rail-btn svg {
  display: block;
}

/* The ring. It is drawn behind the label rather than around the button, so the
   progress reads as belonging to the control, and it is turned a quarter so
   that nothing starts at three o'clock. */
.tmp-ring {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  rotate: -90deg;
  pointer-events: none;
}

.tmp-ring-track {
  fill: none;
  stroke: var(--line);
  stroke-width: 2;
}

.tmp-ring-fill {
  fill: none;
  stroke: var(--warm);
  stroke-width: 2;
  stroke-linecap: round;
  /* 2πr at r = 15.5, which reader.js computes for itself. It rests fully
     offset, so a browser running no script draws an empty ring rather than a
     full one claiming the chapter has been read. */
  stroke-dasharray: 97.39;
  stroke-dashoffset: 97.39;
  transition: stroke-dashoffset 0.15s linear;
}

.tmp-rail-btn.is-on .tmp-ring-track {
  stroke: rgba(14, 20, 36, 0.2);
}

.tmp-rail-btn.is-on .tmp-ring-fill {
  stroke: var(--ink);
}

.tmp-ring-pct {
  position: relative;
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* ---- 5.3 The panels ------------------------------------------------------

   One panel at a time, IN THE MIDDLE OF THE SCREEN at every width, which is
   what Aamir asked for on 20 August 2026 and is the second arrangement these
   have had. They used to rise from the bottom edge on a phone and open beside
   the rail button that called them on a laptop. Two things were wrong with
   that. A sheet along the bottom edge is where a phone puts a share menu, and
   it put the settings furthest from the eye and covered the passage a reader
   had just been looking at; and a card beside the rail had to be nudged back
   into the viewport by script whenever the button that opened it sat low.
   Centred, neither problem exists and no script decides anything.

   NARROWER THAN IT WAS, at 18rem against the old 20.5rem, which is also what
   he asked for. Nothing in a panel is a paragraph: the widest thing any of
   them holds is a row of five size dots and a bookmark's one line of quoted
   words, and both sit inside 18rem comfortably. The chapter's heading list is
   the one thing that would take more, and a heading that runs on wraps, which
   is better than a panel wide enough to cover the page it navigates.

   THE SCRIM IS NOW AT EVERY WIDTH, and it has to be. A panel over the middle
   of a laptop screen is over the words, so the chapter behind it is not being
   read and pretending otherwise would leave a card floating in the middle of a
   fully lit page with nothing saying which of the two is being addressed. This
   is what makes the panels modal in earnest, and it is why they became
   dialogues that hold focus; server/src/pages/reader.js records the change. */

.js-reader .tmp-scrim {
  position: fixed;
  inset: 0;
  z-index: 44;
  display: none;
  background: rgba(6, 9, 17, 0.6);
  cursor: pointer;
}

.js-reader.is-panel-open .tmp-scrim {
  display: block;
}

/* CENTRED WITH `translate` RATHER THAN A TRANSFORM, so the opening animation
   can take `scale` and `opacity` without either of them having to restate the
   centring. The two are separate properties and they compose; one `transform`
   shorthand carrying both would have to repeat the offset in every keyframe,
   and the day somebody changed the offset in one place it would jump. */
.js-reader .tmp-panel {
  position: fixed;
  z-index: 46;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  display: flex;
  flex-direction: column;
  width: min(18rem, calc(100vw - 2.5rem));
  max-height: min(78vh, 34rem);
  background: var(--ink-raised);
  color: var(--bone);
  font-family: var(--ui);
  border: 1px solid var(--line);
  border-radius: 6px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
}

.js-reader .tmp-panel[hidden] {
  display: none;
}

/* The panel takes focus when it opens and must not draw a ring for it: it is
   focused because a keyboard is being put inside it, not because anybody
   pressed it. Its own controls still show theirs. */
.js-reader .tmp-panel:focus {
  outline: none;
}

@media (prefers-reduced-motion: no-preference) {
  .js-reader .tmp-panel.is-open {
    animation: tmp-panel-in 0.18s var(--ease) both;
  }
}

@keyframes tmp-panel-in {
  from { opacity: 0; scale: 0.96; }
  to   { opacity: 1; scale: 1; }
}

.tmp-panel-head {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 0.9rem 1.1rem 0.85rem;
  border-bottom: 1px solid var(--line-soft);
  flex-shrink: 0;
}

.tmp-panel-head > div {
  flex: 1;
  min-width: 0;
}

.tmp-panel-eyebrow {
  display: block;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--warm);
}

.tmp-panel-title {
  display: block;
  margin-top: 0.2rem;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--bone);
}

.tmp-panel-shut {
  width: 1.875rem;
  height: 1.875rem;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: transparent;
  color: var(--bone-dim);
  cursor: pointer;
  transition: color 0.2s var(--ease-soft), border-color 0.2s var(--ease-soft);
}

.tmp-panel-shut:hover {
  color: var(--bone);
  border-color: var(--bone-faint);
}

.tmp-panel-body {
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.tmp-panel-foot {
  flex-shrink: 0;
  padding: 0.8rem 1.1rem;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

/* ---- 5.4 Inside the panels ---------------------------------------------- */

.tmp-set {
  padding: 0.9rem 1.1rem;
  border-bottom: 1px solid var(--line-soft);
}

.tmp-set-name {
  display: block;
  margin-bottom: 0.65rem;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-dim);
}

/* THE FIVE NAMED SIZES, drawn as five dots at five sizes. A row of the letters
   XS to XL says the same thing in words the reader has to translate; five dots
   say it in the shape of the thing being changed. The live site does this, and
   it is the one part of its reader tools kept unchanged.

   THEY ARE NOW SHORTCUTS RATHER THAN THE WHOLE SCALE. Since 20 August 2026 the
   slider below them carries thirty-one steps and the five dots sit on five of
   them, so a dot is lit only when the slider is exactly on its step. Pressing
   one moves the slider to it. */
.tmp-sizes {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 0.25rem;
}

.tmp-size {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  padding: 0.4rem 0.1rem;
  border: 0;
  border-radius: 3px;
  background: transparent;
  color: var(--bone-dim);
  font-family: var(--ui);
  font-size: 0.5625rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
}

.tmp-size-dot {
  border-radius: 50%;
  background: var(--bone-faint);
  transition: background 0.2s var(--ease-soft), scale 0.2s var(--ease-soft);
}

.tmp-size[data-size='1'] .tmp-size-dot { width: 0.4rem; height: 0.4rem; }
.tmp-size[data-size='2'] .tmp-size-dot { width: 0.55rem; height: 0.55rem; }
.tmp-size[data-size='3'] .tmp-size-dot { width: 0.7rem; height: 0.7rem; }
.tmp-size[data-size='4'] .tmp-size-dot { width: 0.875rem; height: 0.875rem; }
.tmp-size[data-size='5'] .tmp-size-dot { width: 1.05rem; height: 1.05rem; }

.tmp-size:hover .tmp-size-dot {
  background: var(--bone-dim);
}

.tmp-size.is-on {
  color: var(--warm);
}

.tmp-size.is-on .tmp-size-dot {
  background: var(--warm);
  scale: 1.1;
}

/* ---- The fine slider ----------------------------------------------------

   UNDER THE FIVE DOTS AND NOT INSTEAD OF THEM, which is what Aamir asked for on
   20 August 2026: the named sizes for the reader who wants one press, and a
   slider under them for the reader for whom none of the five is quite it. It
   runs five steps past the smallest and five past the largest, so it is also
   how a reader gets type smaller than XS or larger than XL.

   IT IS A REAL `input type=range`, not a track and a knob built out of divs. It
   arrives already operable by arrow key, Home, End, Page Up and Page Down, it
   announces its own value, it is draggable by touch without a single line of
   script, and every one of those is something a hand built slider gets wrong.
   The appearance is reset to none and the track and thumb are drawn here,
   because the two vendors draw them differently and a control that is a
   different shape in Safari than in Firefox is the site speaking with two
   voices.

   THE READOUT BESIDE IT NAMES THE SIZE rather than counting steps. 'Step 17' is
   a number about the implementation; 'Between L and XL' is about the type. */

.tmp-scale {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  margin-top: 0.7rem;
}

.tmp-scale-range {
  flex: 1;
  min-width: 0;
  height: 1.5rem;
  margin: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.tmp-scale-range:focus-visible {
  outline: 2px solid var(--warm);
  outline-offset: 3px;
  border-radius: 3px;
}

.tmp-scale-range::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 2px;
  background: var(--line);
}

.tmp-scale-range::-moz-range-track {
  height: 3px;
  border-radius: 2px;
  background: var(--line);
}

.tmp-scale-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 0.95rem;
  height: 0.95rem;
  /* The track is 3px and the thumb is a good deal taller, so it has to be
     lifted by half the difference or it hangs off the bottom of the track in
     every WebKit browser. Firefox centres it for us, which is why there is no
     equivalent line in the rule below. */
  margin-top: -0.4rem;
  border: 0;
  border-radius: 50%;
  background: var(--warm);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

.tmp-scale-range::-moz-range-thumb {
  width: 0.95rem;
  height: 0.95rem;
  border: 0;
  border-radius: 50%;
  background: var(--warm);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

/* Fixed width and tabular figures, so the row does not shuffle sideways as the
   reader drags: a readout that moves the slider it is reporting on is a readout
   that is hard to aim. */
.tmp-scale-now {
  flex: none;
  width: 5.5rem;
  font-family: var(--ui);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-align: right;
  color: var(--bone-dim);
  font-variant-numeric: tabular-nums;
}

/* The tints and the faces, each as a row of choices where exactly one is on.
   They are buttons rather than radio inputs because the choice takes effect the
   moment it is made and there is no form to submit; `aria-pressed` carries the
   state, and reader.js sets it on every one of them, so a screen reader hears
   which is chosen rather than only which was pressed. */
.tmp-choices {
  display: flex;
  gap: 0.5rem;
}

/* THE FACES OUTGREW A ROW on 20 August 2026, at five rather than two, so they
   wrap into a grid of three and the tints stay a row of four. Three across
   rather than four, because a face's name has to be set in the face to be worth
   showing and 'Dyslexic' at a quarter of a narrow panel is four letters and an
   ellipsis. */
.tmp-choices-wrap {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.5rem;
}

.tmp-tint,
.tmp-face {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  padding: 0.55rem 0.3rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: transparent;
  color: var(--bone-dim);
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.2s var(--ease-soft), color 0.2s var(--ease-soft);
}

.tmp-tint:hover,
.tmp-face:hover {
  border-color: var(--bone-faint);
  color: var(--bone);
}

.tmp-tint.is-on,
.tmp-face.is-on {
  border-color: var(--warm);
  color: var(--warm);
}

/* The swatch IS the paper it selects, drawn at the value the sheet will take,
   so the choice is made by looking rather than by reading a word for a
   colour. */
.tmp-swatch {
  width: 1.375rem;
  height: 1.375rem;
  border-radius: 50%;
  border: 1px solid rgba(14, 20, 36, 0.25);
}

.tmp-tint[data-tint=''] .tmp-swatch { background: #f7f3ea; }
.tmp-tint[data-tint='warm'] .tmp-swatch { background: #f7ecd3; }
.tmp-tint[data-tint='cool'] .tmp-swatch { background: #eef1f4; }
.tmp-tint[data-tint='sage'] .tmp-swatch { background: #ecf1ea; }

/* Each face sets its own name in itself, which is the whole of the preview a
   reader needs and is more honest than a sentence describing it. */
.tmp-face-eg {
  font-size: 1.0625rem;
  line-height: 1;
  color: var(--bone);
}

.tmp-face[data-face=''] .tmp-face-eg { font-family: var(--read); }
.tmp-face[data-face='book'] .tmp-face-eg { font-family: 'Literata', var(--read); }
.tmp-face[data-face='garamond'] .tmp-face-eg { font-family: 'EB Garamond', var(--read); }
.tmp-face[data-face='sans'] .tmp-face-eg { font-family: var(--ui); }
.tmp-face[data-face='dyslexic'] .tmp-face-eg { font-family: 'OpenDyslexic', var(--read); }

/* A switch row: a name, a sentence saying what it does, and the switch itself.
   The sentence is the part the live site's panel got right. */
.tmp-switch {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  padding: 0.9rem 1.1rem;
  border: 0;
  background: transparent;
  color: var(--bone);
  font-family: var(--ui);
  text-align: left;
  cursor: pointer;
}

.tmp-switch-text {
  flex: 1;
  min-width: 0;
}

.tmp-switch-title {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.3;
}

.tmp-switch-note {
  display: block;
  margin-top: 0.15rem;
  font-size: 0.6875rem;
  line-height: 1.4;
  color: var(--bone-dim);
}

.tmp-switch-pill {
  position: relative;
  flex-shrink: 0;
  width: 2.125rem;
  height: 1.25rem;
  border-radius: 0.625rem;
  background: rgba(242, 239, 230, 0.18);
  transition: background 0.2s var(--ease-soft);
}

.tmp-switch-pill::after {
  content: '';
  position: absolute;
  top: 0.1875rem;
  left: 0.1875rem;
  width: 0.875rem;
  height: 0.875rem;
  border-radius: 50%;
  background: var(--bone);
  transition: translate 0.2s var(--ease-soft);
}

.tmp-switch.is-on .tmp-switch-pill {
  background: var(--warm);
}

.tmp-switch.is-on .tmp-switch-pill::after {
  translate: 0.875rem 0;
  background: var(--ink);
}

/* ---- What focus mode does -----------------------------------------------

   THE OPTIONS UNDER THE SWITCH, added on 20 August 2026 when Aamir asked for a
   line by line guide beside the paragraph one. They are HIDDEN UNTIL THE SWITCH
   IS ON, by the `hidden` attribute rather than by a class, because settings for
   a thing that is turned off are noise: the panel is short, and every row in it
   that does nothing is a row the reader has to rule out. `hidden` is also what
   keeps them out of a screen reader's reach, which a class could not do.

   INDENTED AND ON A QUIETER GROUND, so the rows read as belonging to the switch
   above rather than as two more settings beside it.

   The pills are the same shape as the tint and face choices, in miniature: one
   vocabulary for 'exactly one of these', whether the choice is a paper colour
   or a number of lines. */

.tmp-sub {
  padding: 0 1.1rem 0.9rem 1.1rem;
  display: grid;
  gap: 0.7rem;
}

.tmp-sub-row {
  display: grid;
  gap: 0.4rem;
}

/* A CLASS THAT SETS A DISPLAY BEATS THE BROWSER'S OWN RULE FOR `hidden`, so
   both of the above have to switch themselves off again or the attribute means
   nothing. This failure has happened on this site before, which is why the
   Library page carries three rules of the same kind and why there is a test
   that walks the markup looking for it. */
.tmp-sub[hidden],
.tmp-sub-row[hidden] {
  display: none;
}

.tmp-sub-name {
  font-family: var(--ui);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bone-dim);
}

.tmp-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

.tmp-pill {
  flex: 1;
  min-width: 2.25rem;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: transparent;
  color: var(--bone-dim);
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 500;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.2s var(--ease-soft), color 0.2s var(--ease-soft);
}

.tmp-pill:hover {
  border-color: var(--bone-faint);
  color: var(--bone);
}

.tmp-pill.is-on {
  border-color: var(--warm);
  color: var(--warm);
}

/* THE LINE COUNTS ARE A GRID, NOT A WRAPPING ROW, so all six sit on one line at
   equal widths. Left to wrap they came out five and one, which reads as five
   choices and a mistake rather than as a scale from one to six. */
.tmp-pills-num {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
}

.tmp-pills-num .tmp-pill {
  min-width: 0;
  padding-inline: 0.2rem;
  font-variant-numeric: tabular-nums;
}

/* The two buttons every panel foot is built from. The filled one is the thing
   the panel is for; the quiet one is everything else. */
.tmp-do,
.tmp-quiet {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  width: 100%;
  padding: 0.65rem 0.9rem;
  border-radius: 3px;
  font-family: var(--ui);
  font-size: 0.8125rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.2s var(--ease-soft), color 0.2s var(--ease-soft),
    border-color 0.2s var(--ease-soft);
}

.tmp-do {
  border: 1px solid var(--warm);
  background: var(--warm);
  color: var(--ink);
}

.tmp-do:hover {
  background: #eec096;
  border-color: #eec096;
}

.tmp-quiet {
  border: 1px solid var(--line);
  background: transparent;
  color: var(--bone-dim);
  font-weight: 500;
}

.tmp-quiet:hover {
  color: var(--bone);
  border-color: var(--bone-faint);
}

/* THIS SITE HAS NO GLOBAL `[hidden]` RESET and it is not getting one here: the
   Library page states the same rule for its own three, and a blanket
   `[hidden] { display: none !important }` would be a rule that quietly wins
   arguments nobody knew they were having.

   It is needed because these two set a display of their own, and an author's
   class rule beats the browser's own `[hidden]`. The Carry on button on a book
   page starts hidden and would otherwise be a button offering to resume a book
   nobody has opened. */
.tmp-do[hidden],
.tmp-quiet[hidden] {
  display: none;
}

/* ---- The bookmarks panel ------------------------------------------------ */

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

.tmp-mark {
  display: flex;
  align-items: stretch;
  border-bottom: 1px solid var(--line-soft);
}

.tmp-mark:last-child {
  border-bottom: 0;
}

.tmp-mark-go {
  flex: 1;
  min-width: 0;
  display: block;
  padding: 0.7rem 0.5rem 0.7rem 1.1rem;
  text-decoration: none;
  color: var(--bone);
}

.tmp-mark-go:hover {
  background: var(--ink-hover);
}

.tmp-mark-ch {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--bone);
}

/* THE EXCERPT IS SET IN THE READING SERIF and it is one of only two places in
   all this furniture that is. It is the book's own words quoted back to the
   reader, and it is what tells them which bookmark this is: a line of Inter
   here would look like another label.

   Two lines and then it stops. A bookmark on a long passage would otherwise be
   a paragraph inside a list of paragraphs. */
.tmp-mark-text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 0.15rem;
  font-family: var(--read);
  font-size: 0.75rem;
  font-style: italic;
  line-height: 1.45;
  color: var(--bone-dim);
}

.tmp-mark-when {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.625rem;
  letter-spacing: 0.04em;
  color: var(--bone-faint);
}

/* A bookmark in the chapter being read is marked in the accent down its edge,
   so the panel answers 'which of these is on this page' without a word. */
.tmp-mark.is-here .tmp-mark-go {
  box-shadow: inset 2px 0 0 var(--warm);
}

.tmp-mark-del {
  flex-shrink: 0;
  width: 2.25rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--bone-faint);
  cursor: pointer;
  transition: color 0.2s var(--ease-soft);
}

.tmp-mark-del:hover {
  color: var(--warn);
}

.tmp-none {
  margin: 0;
  padding: 1.5rem 1.1rem;
  text-align: center;
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--bone-dim);
}

/* ---- The chapter panel -------------------------------------------------- */

.tmp-nav-pct {
  display: block;
  padding: 0.85rem 1.1rem;
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--warm);
  font-variant-numeric: tabular-nums;
}

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

.tmp-chtoc a {
  display: block;
  padding: 0.6rem 1.1rem;
  border-bottom: 1px solid var(--line-soft);
  font-size: 0.75rem;
  line-height: 1.45;
  color: var(--bone);
  text-decoration: none;
}

.tmp-chtoc a:hover {
  background: var(--ink-hover);
  color: var(--warm);
}

/* A heading nested inside another is indented by its level, so the panel shows
   the shape of the chapter rather than a flat list. One of the nine books
   nests heavily (CLAUDE.md section 2.5) and this is the screen where that
   shows. */
.tmp-chtoc [data-level='3'] a { padding-left: 2rem; color: var(--bone-dim); }
.tmp-chtoc [data-level='4'] a { padding-left: 2.75rem; color: var(--bone-dim); }
.tmp-chtoc [data-level='5'] a,
.tmp-chtoc [data-level='6'] a { padding-left: 3.5rem; color: var(--bone-dim); }

.tmp-jump {
  display: flex;
  gap: 0.5rem;
}

.tmp-jump .tmp-quiet {
  flex: 1;
}

/* ---- 5.5 The passage control, the flash and the note -------------------- */

/* Select some words and this appears above them. It is the one gesture the
   live site got right, and it is the difference between a bookmark that says
   'you were about here' and one that says 'this sentence'. */
.js-reader .tmp-pop {
  position: fixed;
  z-index: 47;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--ink-raised);
  color: var(--bone);
  font-family: var(--ui);
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
}

.js-reader .tmp-pop[hidden] {
  display: none;
}

.js-reader .tmp-toast {
  position: fixed;
  z-index: 48;
  left: 50%;
  bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
  translate: -50% 0.5rem;
  padding: 0.55rem 1.1rem;
  border-radius: 999px;
  background: var(--ink-raised);
  border: 1px solid var(--line);
  color: var(--bone);
  font-family: var(--ui);
  font-size: 0.75rem;
  font-weight: 500;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s var(--ease-soft), translate 0.25s var(--ease-soft);
}

.js-reader .tmp-toast.is-up {
  opacity: 1;
  translate: -50% 0;
}

/* Landing on a bookmark. The passage is marked for a couple of seconds and the
   mark then fades, which is the least a reader needs to be told 'this is the
   bit'. It is a wash in the reading accent rather than a highlighter yellow,
   because it is on paper and because it is temporary. */
.tmp-flash {
  animation: tmp-flash 2.4s var(--ease-soft) both;
  border-radius: 3px;
}

@keyframes tmp-flash {
  0%, 55% { background-color: var(--read-accent-soft); }
  100% { background-color: rgba(138, 87, 41, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .tmp-flash {
    animation: none;
    background-color: var(--read-accent-soft);
  }
}

/* ---- 5.6 The banner on a book page --------------------------------------

   What the live site put above a book's contents, which offered the single
   most recent bookmark. This offers the READING PLACE, which is what somebody
   opening a book they are part way through actually wants, and which is
   recorded without their ever having asked for it.

   IT IS HIDDEN IN THE MARKUP AND SHOWN BY THE SCRIPT, which is the opposite of
   the rule everywhere else on this site and is right here: there is nothing to
   show until the browser has been asked what it remembers, so a banner that
   was visible by default would be an empty box on every first visit. */

.tmp-resume {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  margin-bottom: 1.75rem;
  padding: 0.95rem 1.15rem;
  border: 1px solid var(--line);
  border-left: 2px solid var(--warm);
  border-radius: 3px;
  background: var(--ink-raised);
}

.tmp-resume[hidden] {
  display: none;
}

.tmp-resume-text {
  flex: 1;
  min-width: 12rem;
}

.tmp-resume-label {
  display: block;
  font-family: var(--ui);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--warm);
}

.tmp-resume-where {
  display: block;
  margin-top: 0.3rem;
  font-family: var(--ui);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--bone);
}

.tmp-resume-meta {
  display: block;
  margin-top: 0.2rem;
  font-family: var(--ui);
  font-size: 0.75rem;
  color: var(--bone-dim);
}

.tmp-resume-meta > span:not([hidden]) + span:not([hidden])::before {
  content: ' · ';
}

.tmp-resume-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.tmp-resume-acts .tmp-do,
.tmp-resume-acts .tmp-quiet {
  width: auto;
}

/* ---- 5.7 The bookmarks page ---------------------------------------------

   The centralised place Aamir asked for on 20 August 2026. Everything a reader
   has saved, in one list, across every book, with the reading places above it.

   It is on the SITE's ground rather than on paper, because nothing on it is a
   book: it is a set of pointers into books. The only reading serif on the page
   is the excerpt in each row, for the same reason it is the only one in the
   panel. */

/* The page's own sections. `.tmp-page` in site.css sets the h1, the measure and
   the rhythm, and stops there; these are the three headings under it and the
   fold the carrying tools sit behind. */

.tmp-msec {
  margin-top: clamp(2.75rem, 7vw, 4rem);
}

.tmp-msec-head {
  margin-bottom: 1.25rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--warm);
}

.tmp-msec[hidden] {
  display: none;
}

.tmp-mopen {
  margin-top: 1.5rem;
  border: 1px solid var(--line);
  border-radius: 3px;
}

.tmp-mopen > summary {
  padding: 0.8rem 1rem;
  font-family: var(--ui);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--bone);
  cursor: pointer;
}

.tmp-mopen[open] > summary {
  border-bottom: 1px solid var(--line);
}

.tmp-mopen-body {
  padding: 1rem;
}

.tmp-places {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

@media (min-width: 46rem) {
  .tmp-places {
    grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  }
}

.tmp-place-go {
  display: flex;
  gap: 0.9rem;
  align-items: center;
  height: 100%;
  padding: 0.85rem 1rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--ink-raised);
  text-decoration: none;
  transition: border-color 0.2s var(--ease-soft), background 0.2s var(--ease-soft);
}

.tmp-place-go:hover {
  border-color: var(--bone-faint);
  background: var(--ink-hover);
}

.tmp-place-text {
  min-width: 0;
}

.tmp-place-book {
  display: block;
  font-family: var(--ui);
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--warm);
}

.tmp-place-ch {
  display: block;
  margin-top: 0.25rem;
  font-family: var(--ui);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--bone);
}

.tmp-place-pct {
  display: block;
  margin-top: 0.2rem;
  font-family: var(--ui);
  font-size: 0.6875rem;
  color: var(--bone-dim);
}

.tmp-mcover {
  flex-shrink: 0;
  width: 3.5rem;
  height: auto;
  border-radius: 2px;
  box-shadow: var(--cover-edge);
}

.tmp-mcover.is-blank {
  width: 3.5rem;
  height: 5.25rem;
  background: var(--ink-hover);
}

.tmp-mgroup {
  margin-top: 2.5rem;
}

.tmp-mgroup:first-child {
  margin-top: 0;
}

.tmp-mgroup-head {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding-bottom: 0.85rem;
  border-bottom: 1px solid var(--line);
}

.tmp-mgroup-text {
  flex: 1;
  min-width: 0;
}

.tmp-mgroup-title {
  margin: 0;
  font-family: var(--ui);
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--bone);
}

.tmp-mgroup-count {
  margin: 0.15rem 0 0;
  font-family: var(--ui);
  font-size: 0.75rem;
  color: var(--bone-dim);
}

.tmp-mgroup-clear,
.tmp-marks-tool {
  flex-shrink: 0;
  padding: 0.45rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: transparent;
  color: var(--bone-dim);
  font-family: var(--ui);
  font-size: 0.75rem;
  cursor: pointer;
  transition: color 0.2s var(--ease-soft), border-color 0.2s var(--ease-soft);
}

.tmp-mgroup-clear:hover,
.tmp-marks-tool:hover {
  color: var(--bone);
  border-color: var(--bone-faint);
}

/* ARMED, AND SAYING SO IN THE ONE COLOUR THIS SITE KEEPS FOR A WARNING. A
   button that has become its own confirmation has to look different from the
   button it was a moment ago, or the second press is made without the reader
   noticing that the first changed anything. */
.tmp-mgroup-clear.is-armed,
.tmp-marks-tool.is-armed {
  border-color: var(--warn);
  color: var(--warn);
}

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

.tmp-mrow {
  display: flex;
  align-items: stretch;
  border-bottom: 1px solid var(--line-soft);
}

.tmp-mrow-go {
  flex: 1;
  min-width: 0;
  padding: 0.85rem 0.5rem 0.85rem 0;
  text-decoration: none;
}

.tmp-mrow-go:hover .tmp-mrow-ch {
  color: var(--warm);
}

.tmp-mrow-ch {
  display: block;
  font-family: var(--ui);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--bone);
  transition: color 0.2s var(--ease-soft);
}

.tmp-mrow-text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 0.25rem;
  font-family: var(--read);
  font-size: 0.875rem;
  font-style: italic;
  line-height: 1.5;
  color: var(--bone-dim);
}

.tmp-mrow-when {
  display: block;
  margin-top: 0.3rem;
  font-family: var(--ui);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--bone-faint);
}

.tmp-mrow-del {
  flex-shrink: 0;
  width: 2.5rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--bone-faint);
  cursor: pointer;
  transition: color 0.2s var(--ease-soft);
}

.tmp-mrow-del:hover {
  color: var(--warn);
}

/* Carrying them to another device. Folded away in a disclosure because it is
   the rarest thing on the page and the most alarming looking: a box of raw
   text is not what anybody came here for. */
.tmp-marks-carry {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: center;
}

.tmp-marks-paste {
  width: 100%;
  min-height: 5.5rem;
  padding: 0.65rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--ink);
  color: var(--bone-dim);
  font-family: ui-monospace, 'Cascadia Mono', 'Consolas', monospace;
  font-size: 0.6875rem;
  line-height: 1.5;
  resize: vertical;
}

.tmp-marks-paste:focus-visible {
  outline: 2px solid var(--warm);
  outline-offset: 2px;
}

.tmp-marks-note {
  margin: 0.85rem 0 0;
  font-family: var(--ui);
  font-size: 0.8125rem;
  color: var(--warm);
}

/* --------------------------------------------------------------------------
   6. Print
   --------------------------------------------------------------------------

   A chapter printed from the browser is not the print EDITION, which is a
   different renderer over the same segments and is not built yet. This is the
   ordinary courtesy of a reader pressing print: the site's furniture goes, the
   sheet loses its shadow and its inset, and the words are left on white.
   -------------------------------------------------------------------------- */

@media print {
  .tmp-ch-nav,
  .tmp-ch-where,
  .tmp-rail,
  .tmp-panel,
  .tmp-scrim,
  .tmp-pop,
  .tmp-toast,
  .tmp-beam,
  .tmp-resume {
    display: none !important;
  }

  /* A chapter printed while focus mode is on would come out with one paragraph
     readable and the rest at a quarter of the ink, or with two washes of paper
     laid over most of it. The setting is about a screen being scrolled; paper
     is not scrolled. The beam goes with the furniture above; the paragraph
     dimming is opacity on the blocks themselves, so it is put back here. */
  :root[data-read-focus='on'] .tmp-read > * {
    opacity: 1;
  }

  /* And it prints at the size the book is set at rather than at whatever the
     reader has the screen turned up to. A type scale is a property of a
     screen, and a page coming out of the printer in 29pt because somebody had
     chosen extra large last week would be a surprise at the paper tray. */
  .tmp-read {
    font-size: var(--read-size);
    line-height: 1.75;
  }

  .tmp-sheet {
    max-width: none;
    margin: 0;
    padding: 0;
    background: #fff;
    box-shadow: none;
    border-radius: 0;
  }

  .tmp-read {
    max-width: none;
    color: #000;
  }

  .tmp-ch-title,
  .tmp-ch-book {
    color: #000;
  }

  /* A footnote marker means nothing on paper if it is a link to a place the
     reader cannot press, so it is left as the number it already is. Notes
     print at the end of the chapter, where they already are. */
  .tmp-fn-ref a,
  .tmp-fn-back {
    color: #000;
    text-decoration: none;
  }
}
