/* ==========================================================================
   THE ELEMENTAL NINJAS
   Stylesheet. System fonts only, no downloads, no images -- every decoration
   is a CSS shape drawn in code. There is no internet on the machine that
   builds this site, and there is none in the page either: the cracked Core,
   the five element wedges and the lantern string are all CSS.

   THE LOOK
   --------
   Chinese New Year: lantern red and gold leaf, cream rice paper, black
   lacquer edges. The five elements each own a colour, and those same five
   colours are the five ninjas' speaker colours -- Kai is fire, Shen is
   water, Liang is earth, Jin is wind, Wei is lightning -- so the colour
   beside a line of dialogue means the same thing everywhere on the site.

   THE A4 RULE
   -----------
   Every sheet (cover, story page, THE END) is a real A4 page: 210mm x 297mm
   with 16mm side and top margins and an 18mm foot. On screen the whole sheet
   is scaled down to fit the window, keeping the exact A4 shape -- what you
   see is a small photograph of the paper, not a different layout.

   The trick is one custom property:

       --u   = ONE MILLIMETRE OF THE SHEET, at whatever size the sheet is
               currently drawn.

   So calc(var(--u) * 210) is always the full width of an A4 sheet, and
   calc(var(--u) * 6.7) is always 19pt type. Change nothing else and the whole
   page scales together. In print, --u is a real millimetre.

   Sections:  1 tokens  2 base  3 top bar  4 contents  5 the sheets
              6 story type  7 back-to-top / footer  7b print controls
              8 mobile  9 dark mode  10 PRINT  11 old-print-engine fallback
   ========================================================================== */

/* --- 1. tokens ---------------------------------------------------------- */
:root {
  /* --- paper, and the red ground the sheets lie on --------------------- */
  --paper:      #fdf6e6;   /* rice paper */
  --paper-edge: #d9c39a;
  --bg:         #7c1122;   /* lantern red */
  --bg-2:       #4d0a15;
  --ink:        #241610;   /* ink ON the paper */
  --ink-soft:   #6f5340;
  --chrome-ink: #ffe9c9;   /* text on the red ground, never on paper */
  --gold:       #e0a83c;
  --gold-dark:  #a8761a;
  --lacquer:    #2a0a10;
  --red:        #c0392b;
  --red-dark:   #8c1b16;
  --sky:        #1668a8;   /* focus rings only */
  --radius:     4px;

  /* --- the five elements ----------------------------------------------
     One colour each, and they are the ninjas' colours too. Change one here
     and it changes on the cover mark, on the shelf and beside every line
     that character speaks. */
  --fire:  #b3261e;
  --water: #14568c;
  --earth: #7a5223;
  --wind:  #4f6f7d;
  --bolt:  #8a6508;

  color-scheme: light dark;

  /* --- the A4 sheet ---------------------------------------------------- */
  --sheet-w:      210mm;   /* A4 width  */
  --sheet-h:      297mm;   /* A4 height */
  --sheet-top:    16mm;    /* book margins */
  --sheet-side:   16mm;
  --sheet-foot:   18mm;

  /* type sizes, in millimetres of the sheet (1pt = 0.352778mm) */
  --t-story:      6.7028;  /* 19pt   -- narration */
  /* Adhiyan asked for the dialogue to be bigger. It was the SAME size as the
     narration, but it reads smaller because the name column insets it, so a
     matching size was not enough -- it now genuinely outsizes the narration. */
  --t-dialogue:   7.4058;  /* 21pt   -- the spoken lines */
  --t-who:        3.5278;  /* 10pt   -- the speaker's name beside a line */
  --t-title:      3.5278;  /* 10pt   -- the "PAGE 7" header */
  --t-sfx:        9.1722;  /* 26pt   -- DONG!  THUMP.  SPLAT! */
  --t-sign:       4.5861;  /* 13pt   -- PAELLA FORTRESS */
  --t-cover:      12.7000; /* 36pt   -- the book title */
  /* Adhiyan wrote in chapters, not pages, so his chapter headings are real
     headings on the sheet. They are HIS lines -- see the note in build.js. */
  --t-chapter:     6.3500; /* 18pt   -- CHAPTER 7 - THE TRUTH */
  --t-part:        5.6444; /* 16pt   -- BOOK 1: THE BROKEN CORE */
  --t-byline:     4.5861;  /* 13pt   */
  --t-end:        8.4667;  /* 24pt   */

  /* the play-script name column beside every line of dialogue, in sheet mm */
  --who-col: 24;
  --who-gap: 2;

  /* how much room the window leaves either side of the sheet */
  --gutter: 1.4rem;

  /* the width the sheet is actually drawn at, never more than real A4 */
  --sheet-fit: min(210mm, calc(100vw - var(--gutter) - var(--gutter)));

  /* ONE MILLIMETRE OF THE SHEET at the size it is currently drawn */
  --u: calc(var(--sheet-fit) / 210);

  /* --- THE SCREEN-HEIGHT SWITCH (round 8) ------------------------------
     ON PAPER a sheet is always 297mm tall. That never changes: the @page
     box in section 10 is A4, one story page per sheet, and verify.js still
     measures every page against 297mm of real paper before it will pass.

     ON A SCREEN nobody is holding paper. Adhiyan sent a photo of the site
     on his phone: a page that is only half full was drawing the whole 297mm
     anyway, so half of every sheet was empty and you had to scroll past the
     nothing to reach the next page. So on screen a sheet is now only as tall
     as the words on it (plus the same margins), and the stack of sheets
     reads like a book instead of a stack of mostly-blank paper.

     ONE LINE FLIPS IT BACK:
        --sheet-screen-h: auto;                 <- fits the words (now)
        --sheet-screen-h: calc(var(--u) * 297); <- true A4 rectangles
     It is a variable on purpose: a "show me real pages" toggle would just
     set this on <html> and nothing else in the stylesheet would move.
     Print ignores it completely (section 10 sets height: auto anyway).
     --------------------------------------------------------------------- */
  --sheet-screen-h: auto;

  /* a sheet never gets shorter than this, so a one-line page still looks
     like a page and the "Print page n" button keeps its foot margin */
  --sheet-screen-min: calc(var(--u) * 74);
}

/* --- 2. base ------------------------------------------------------------ */
* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 5.5rem;   /* so anchors clear the sticky bar */
  -webkit-text-size-adjust: 100%;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation: none !important; transition: none !important; }
}

body {
  margin: 0;
  overflow-x: hidden;
  font-family: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
  line-height: 1.4;
  /* Outside the sheets everything sits on the red ground, so the DEFAULT
     text colour is the pale one. The sheets set their own ink back to dark
     (see section 5) -- paper is paper wherever it is. */
  color: var(--chrome-ink);
  background-color: var(--bg);
  background-image:
    /* a gold lattice, the kind painted on a temple screen. Pure CSS. */
    repeating-linear-gradient( 45deg, rgba(224,168,60,.10) 0 2px, transparent 2px 16px),
    repeating-linear-gradient(-45deg, rgba(224,168,60,.10) 0 2px, transparent 2px 16px),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%);
  background-attachment: fixed;
}

.progress {
  position: fixed; inset: 0 0 auto 0;
  height: 5px; z-index: 60;
  background: rgba(0,0,0,.10);
}
.progress-bar {
  height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--gold), var(--red) 55%, var(--water));
  transition: width .1s linear;
}

/* --- 3. top bar --------------------------------------------------------- */
.topbar {
  position: sticky; top: 0; z-index: 50;
  background: linear-gradient(180deg, #a3151f, #7c1122);
  color: var(--chrome-ink);
  box-shadow: 0 2px 10px rgba(0,0,0,.35);
  font-size: 1rem;
}
.topbar-inner {
  max-width: 60rem;
  margin: 0 auto;
  padding: .45rem .7rem;
  display: flex; align-items: center; gap: .5rem;
  min-height: 3rem;
}
.brand {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 700;
  letter-spacing: .02em;
  font-size: clamp(.95rem, 3.4vw, 1.2rem);
  color: var(--chrome-ink);
  text-decoration: none;
  margin-right: auto;
  display: flex; align-items: center; gap: .45rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* the little cracked Core: a gold coin with a fracture through it */
.brand-mark {
  position: relative;
  width: 1.15rem; height: 1.15rem;
  flex: none;
  border-radius: 50%;
  background: radial-gradient(circle at 36% 32%,
    #ffe6a8 0 22%, var(--gold) 30%, var(--gold-dark) 74%, #6d4409 100%);
  box-shadow: 0 0 0 2px rgba(255,233,201,.5);
}
.brand-mark::before {         /* the crack */
  content: ""; position: absolute; left: 48%; top: 4%;
  width: 2px; height: 92%;
  background: linear-gradient(180deg,
    transparent 0 6%, var(--lacquer) 6% 36%,
    transparent 36% 46%, var(--lacquer) 46% 88%, transparent 88%);
  transform: rotate(13deg);
}
.brand-mark::after {          /* a chip knocked off the rim */
  content: ""; position: absolute; right: -1px; top: 24%;
  width: .3rem; height: .2rem;
  background: #a3151f;
  border-radius: 0 50% 50% 0;
  transform: rotate(-20deg);
}

.btn {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .95rem;
  font-weight: 700;
  line-height: 1;
  color: var(--red-dark);
  background: #fff4dd;
  border: 2px solid var(--lacquer);
  border-radius: 999px;
  padding: .62rem 1rem;
  min-height: 44px;              /* comfortable tap target */
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  box-shadow: 0 2px 0 var(--lacquer);
  list-style: none;
  white-space: nowrap;
}
.btn::-webkit-details-marker { display: none; }
.btn:hover  { background: #ffeac4; }
.btn:active { transform: translateY(2px); box-shadow: 0 0 0 var(--lacquer); }
.btn:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
.btn-print { background: var(--gold); color: #3a2200; }
.btn-print:hover { background: #f0bb4f; }

/* a string of paper lanterns under the top bar, drawn in CSS */
.lanterns {
  height: 14px;
  background:
    radial-gradient(circle at 12px 3px, var(--gold) 0 2px, transparent 2px) 0 0 / 24px 14px repeat-x,
    radial-gradient(circle at 12px 9px, #d8402f 0 5px, transparent 5px) 0 0 / 24px 14px repeat-x,
    linear-gradient(180deg, var(--gold) 0 2px, transparent 2px);
  filter: drop-shadow(0 1px 1px rgba(0,0,0,.35));
}

/* --- 4. contents -------------------------------------------------------- */
.contents { position: relative; }
.contents-panel {
  position: absolute;
  top: calc(100% + .55rem);
  right: 0;
  width: min(92vw, 22rem);
  max-height: min(70vh, 30rem);
  overflow-y: auto;
  background: var(--paper);
  color: var(--ink);
  border: 3px solid var(--lacquer);
  border-radius: 14px;
  box-shadow: 0 12px 26px rgba(0,0,0,.4);
  padding: .8rem;
  z-index: 55;
  font-size: 1rem;
}
.contents-head {
  margin: 0 0 .55rem;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--red-dark);
}
.toc-list {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(3.4rem, 1fr));
  gap: .4rem;
}
.toc-list a {
  display: flex; align-items: center; justify-content: center;
  min-height: 44px;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .95rem; font-weight: 700;
  text-decoration: none;
  color: var(--ink);
  background: #fff;
  border: 2px solid var(--paper-edge);
  border-radius: 9px;
}
.toc-list a:hover { background: var(--gold); border-color: var(--lacquer); }
.toc-list a:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.toc-list .toc-end { grid-column: 1 / -1; }
.toc-list .toc-end a { font-size: .82rem; letter-spacing: .08em; background: #fff3dc; }

/* --- 5. the sheets ------------------------------------------------------
   A scrollable stack of A4 sheets. Each one is exactly 210 x 297 of --u.
   ------------------------------------------------------------------------ */
#book {
  width: calc(var(--u) * 210);
  margin: 0 auto;
  padding: 1.2rem 0 2.4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--u) * 12);
}

.cover, .page, .the-end {
  /* ---- the sheet of paper ---- */
  width:  calc(var(--u) * 210);
  /* height comes from --sheet-screen-h (section 1). It is "auto" now, so a
     sheet is as tall as its words -- no dead space under a short page. The
     WIDTH is still exactly A4, so every line breaks in the same place it
     will break on paper, and --u still means one millimetre of a real
     sheet. Print flattens both anyway (section 10). */
  height: var(--sheet-screen-h);
  min-height: var(--sheet-screen-min);
  /* The foot margin is 18mm of sheet -- but the "Print page 7" button parked
     in it is a real 44px finger target, and on a phone 18mm of a shrunken
     sheet is only about 35px. Now that a page stops where its words stop,
     that button would sit on the last line. So on screen the foot is never
     allowed to be shorter than the button. Sides and top are untouched, so
     every line still breaks exactly where it breaks on paper, and print
     throws all of this padding away anyway (section 10). */
  padding: calc(var(--u) * 16) calc(var(--u) * 16) max(calc(var(--u) * 18), 3.4rem);
  /* the body text colour is the pale one, for the red ground. A sheet is
     paper, so it puts the dark ink back. */
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(0,0,0,.45);
  margin: 0;
  /* if the words ever outgrow the sheet they must SPILL, visibly, so it can
     be seen and fixed -- a sheet that silently swallows a line is a bug.
     verify.js measures every page against the same numbers used here. */
  overflow: visible;
  position: relative;
}

.cover {
  text-align: center;
  overflow: hidden;              /* only so the sun rays stay on the paper */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.cover::before {   /* rays of the broken Core, behind the title */
  content: ""; position: absolute; inset: -20% -20% auto -20%; height: 140%;
  background: repeating-conic-gradient(from 0deg at 50% 32%,
    rgba(224,168,60,.20) 0deg 7deg, rgba(224,168,60,0) 7deg 18deg);
  pointer-events: none;
}
.book-title {
  position: relative;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  font-size: calc(var(--u) * var(--t-cover));
  line-height: 1.1;
  letter-spacing: -.01em;
  margin: 0 0 calc(var(--u) * 8);
  color: var(--red-dark);
  text-shadow: calc(var(--u) * .7) calc(var(--u) * .7) 0 var(--gold);
  text-wrap: balance;
}
.byline {
  position: relative;
  margin: 0;
  font-size: calc(var(--u) * var(--t-byline));
  color: var(--ink-soft);
  font-style: italic;
}
/* THE BROKEN CORE, drawn in CSS -- the mark of the whole series.
   A gold-rimmed disc cut into five wedges, one per element, with the Core
   itself burning in the middle and a crack running right through it. */
.cover-mark {
  position: relative;
  width:  calc(var(--u) * 56);
  height: calc(var(--u) * 56);
  margin: 0 auto calc(var(--u) * 9);
  flex: none;
  border-radius: 50%;
  background: conic-gradient(from -90deg,
    var(--fire)  0deg  72deg,
    var(--water) 72deg 144deg,
    var(--earth) 144deg 216deg,
    var(--wind)  216deg 288deg,
    var(--bolt)  288deg 360deg);
  box-shadow:
    0 0 0 calc(var(--u) * 1.8) var(--gold),
    0 calc(var(--u) * 2) calc(var(--u) * 5) rgba(0,0,0,.35);
}
.cover-mark::before {  /* the Core */
  content: ""; position: absolute; inset: 21%;
  border-radius: 50%;
  background: radial-gradient(circle at 36% 30%,
    #fff4d2 0 14%, var(--gold) 24%, var(--gold-dark) 64%, #66400a 100%);
}
.cover-mark::after {   /* ...and the crack */
  content: ""; position: absolute; left: 49%; top: 3%;
  width: calc(var(--u) * 1.5); height: 94%;
  background: linear-gradient(180deg,
    transparent 0 5%,  var(--lacquer) 5% 29%,
    transparent 29% 37%, var(--lacquer) 37% 63%,
    transparent 63% 70%, var(--lacquer) 70% 95%, transparent 95%);
  transform: rotate(11deg);
}

/* the running head on every story sheet: PAGE 7 */
.page-title {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: calc(var(--u) * var(--t-title));
  font-weight: 700;
  letter-spacing: .18em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--red-dark);
  margin: 0 0 calc(var(--u) * 6);
  padding-bottom: calc(var(--u) * 2);
  border-bottom: calc(var(--u) * .35) solid var(--paper-edge);
  display: flex; align-items: center; gap: calc(var(--u) * 2);
}
.page-title::before {   /* a shard of the Core, CSS only */
  content: ""; flex: none;
  width: calc(var(--u) * 2.2); height: calc(var(--u) * 2.2);
  background: var(--red);
  border-radius: calc(var(--u) * .4);
  transform: rotate(45deg);
}
.page-title::after {    /* rule that fills the row */
  content: ""; flex: 1 1 auto; height: calc(var(--u) * .7);
  background: repeating-linear-gradient(90deg,
    var(--gold) 0 calc(var(--u) * 2), transparent calc(var(--u) * 2) calc(var(--u) * 4));
  opacity: .8;
}

/* --- 6. story typography ------------------------------------------------ */

/* CHAPTER HEADINGS -- Adhiyan's own lines, set as real headings.
   His manuscript is written in chapters with no page breaks at all, so
   "CHAPTER 7 - THE TRUTH" is a line of his file exactly like any other and
   is covered by the byte-for-byte check in verify.js. All this does is give
   it the look of a chapter opening. Every chapter starts a fresh sheet --
   build.js and books.js explain how the sheets were worked out. */
.part {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 700;
  font-size: calc(var(--u) * var(--t-part));
  line-height: 1.2;
  letter-spacing: .26em;
  color: var(--gold-dark);
  margin: 0 0 calc(var(--u) * 6);
  max-width: none;
  break-after: avoid;
  page-break-after: avoid;
}
.chapter {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  font-size: calc(var(--u) * var(--t-chapter));
  line-height: 1.2;
  letter-spacing: .08em;
  color: var(--red-dark);
  margin: calc(var(--u) * 2) 0 calc(var(--u) * 6);
  padding-bottom: calc(var(--u) * 2);
  border-bottom: calc(var(--u) * .6) solid var(--gold);
  max-width: none;
  break-after: avoid;
  page-break-after: avoid;
}

.line, .d, .sfx, .sign {
  font-size: calc(var(--u) * var(--t-story));
  line-height: 1.4;
  margin: 0 0 calc(var(--u) * 2.4);
  max-width: none;               /* the sheet margins ARE the measure */
}
.d {                       /* dialogue */
  font-size: calc(var(--u) * var(--t-dialogue));
  padding-left: calc(var(--u) * 3.5);
  border-left: calc(var(--u) * .9) solid rgba(192,57,43,.30);
  color: #2e1c14;
}

/* --- who is speaking -----------------------------------------------------
   Adhiyan's dialogue is bare quoted lines with no "said Kai" attached, so
   every quoted line is set like a play script: a small name in a column on
   the left, then his line, exactly as he wrote it. The NAME is the mechanism
   -- it is real text and it prints in black and white. The colour is only a
   bonus on screen. Nothing here is inside his sentence.
   ------------------------------------------------------------------------ */
.say {
  --speaker: var(--ink-soft);          /* every character overrides this */
  display: grid;
  grid-template-columns: calc(var(--u) * var(--who-col)) 1fr;
  column-gap: calc(var(--u) * var(--who-gap));
  align-items: baseline;
  break-inside: avoid;
  page-break-inside: avoid;
}
.who {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: calc(var(--u) * var(--t-who));
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: .06em;
  text-transform: uppercase;
  text-align: right;
  color: var(--speaker);
  margin: 0 0 calc(var(--u) * 2.4);
  max-width: none;
  overflow-wrap: break-word;
}
/* a name I was not certain about: it keeps its "?" and sits back a little */
.who.guess { font-weight: 600; opacity: .78; }

/* ---- a line that already says who is talking ---------------------------
   Adhiyan sometimes writes the attribution into the sentence himself:

       "What is happening?" asked the Emperor of Heaven.

   Printing EMPEROR beside that says it twice, so build.js leaves the name
   off (the rule is written out in full at the top of build.js). Everything
   else stays: the line keeps the speaker's colour on its rule, and it stays
   in the same column as every other line of dialogue, so the page does not
   develop a ragged left edge halfway down a chapter.

   The grid has two columns and this block only has one child, so the line
   has to be TOLD to sit in the second one -- otherwise it would slide into
   the empty name column and be the only line on the sheet out of step.
   (Print does not need this: on paper the name is a float, so a missing
   float just leaves .say > .d with its usual 26mm margin.)
   ------------------------------------------------------------------------ */
.say-named > .d { grid-column: 2; }

/* the little rule beside the line picks up the speaker's colour too */
.say > .d {
  border-left-color: var(--speaker);
  border-left-color: color-mix(in srgb, var(--speaker) 42%, transparent);
}

/* ---- the speaker colours ------------------------------------------------
   One colour per character, the same on every page of the book.
   To add a character: one "cast" line in speakers.txt + one rule here.
   ------------------------------------------------------------------------ */
.say-kai      { --speaker: var(--fire);  }  /* Kai Ren    -- fire, dragon   */
.say-shen     { --speaker: var(--water); }  /* Shen Wei   -- water, serpent */
.say-liang    { --speaker: var(--earth); }  /* Liang Chen -- earth, tiger   */
.say-jin      { --speaker: var(--wind);  }  /* Jin Tao    -- wind, rabbit   */
.say-wei      { --speaker: var(--bolt);  }  /* Wei Jun    -- lightning,     */
                                            /* monkey                       */
.say-five     { --speaker: #0f6b4a; }   /* the Elemental Five, speaking as  */
                                        /* one -- jade, the colour none of  */
                                        /* the five owns on their own       */
.say-zhao     { --speaker: #5b2d8e; }   /* Zhao Kuan, the Void Emperor      */
                                        /* -- void violet                   */
.say-general  { --speaker: #3f4b57; }   /* his Shadow Legion general        */
                                        /* -- cold steel                    */
.say-flame    { --speaker: #a34a12; }   /* the Flame General at Flame       */
                                        /* Mountain -- ember. Kin to Kai's  */
                                        /* fire, but not the same fire.     */
.say-emperor  { --speaker: #7d1240; }   /* the Emperor of Heaven -- imperial*/
.say-goddess  { --speaker: #a63a86; }   /* the Goddess of Balance -- lotus  */
.say-voice    { --speaker: #5c5470; }   /* the voice in the forest: a       */
                                        /* Shadow Legion trick, so a        */
                                        /* washed-out cousin of Zhao violet */
.say-darkness { --speaker: #2a1b3d; }   /* whatever escaped the Core. It    */
                                        /* speaks once, on the last sheet.  */
.say-crowd    { --speaker: #6b5643; }   /* the festival street -- soft ink  */
.say-unknown  { --speaker: #8a7d70; }   /* nobody can tell   -- grey        */
.sfx {                     /* CRACK!  BOOM!  WHOOSH!  SERPENT FLOW! */
  font-family: "Trebuchet MS", "Segoe UI Black", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  font-size: calc(var(--u) * var(--t-sfx));
  line-height: 1.15;
  letter-spacing: .04em;
  color: var(--red);
  text-shadow:
    calc(var(--u) * .7) calc(var(--u) * .7) 0 var(--gold),
    calc(var(--u) * 1.4) calc(var(--u) * 1.4) 0 rgba(42,10,16,.30);
  margin: calc(var(--u) * 4) 0 calc(var(--u) * 4.5);
  transform: rotate(-1.6deg);
  transform-origin: left center;
}
.sign {                    /* a sign on a wall: shouted, but no full stop */
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  letter-spacing: .22em;
  font-size: calc(var(--u) * var(--t-sign));
  line-height: 1.3;
  text-align: center;
  color: #3a2200;
  background: repeating-linear-gradient(90deg,
    rgba(224,168,60,.30) 0 calc(var(--u) * 3), rgba(224,168,60,.15) calc(var(--u) * 3) calc(var(--u) * 6));
  border: calc(var(--u) * .7) solid var(--lacquer);
  border-radius: calc(var(--u) * 1.5);
  padding: calc(var(--u) * 2);
  margin: calc(var(--u) * 4) auto calc(var(--u) * 4.5);
  max-width: calc(var(--u) * 110);
}
/* the first line under a chapter heading gets a drop cap -- so every chapter
   opens like a chapter, not like the middle of one */
.chapter + .line::first-letter {
  float: left;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  font-size: 2.6em;
  line-height: .82;
  padding: .06em .1em 0 0;
  color: var(--red);
}

.the-end {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.the-end-line {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  font-size: calc(var(--u) * var(--t-end));
  line-height: 1.2;
  letter-spacing: .1em;
  color: var(--red-dark);
  margin: 0;
  max-width: none;
}
.the-end::after {
  content: "";
  display: block;
  width: 60%;
  height: calc(var(--u) * 1.2);
  margin: calc(var(--u) * 8) auto 0;
  background: repeating-linear-gradient(90deg,
    var(--gold) 0 calc(var(--u) * 4), var(--red) calc(var(--u) * 4) calc(var(--u) * 8));
}

/* --- 7. back to top + footer -------------------------------------------- */
.totop {
  position: fixed;
  right: .8rem; bottom: .8rem;
  z-index: 40;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 52px; height: 52px;
  border-radius: 50%;
  background: var(--red-dark);
  color: var(--chrome-ink);
  border: 3px solid var(--gold);
  text-decoration: none;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .78rem; font-weight: 700; line-height: 1;
  box-shadow: 0 3px 0 var(--lacquer);
  transition: opacity .18s ease, transform .18s ease;
}
.totop span { font-size: .6rem; letter-spacing: .06em; }
.totop.hidden { opacity: 0; pointer-events: none; transform: translateY(8px); }

.site-foot {
  max-width: 46rem;
  margin: 0 auto;
  padding: 0 .8rem 4.5rem;
  text-align: center;
  color: var(--chrome-ink);
  font-size: 1rem;
}
.site-foot p { margin: 0 0 .9rem; font-style: italic; }

/* --- 7b. printing one page, or a few ------------------------------------
   Adhiyan: "can we print only one page at a time are many at once".

   Every sheet carries its own small "Print page 7" button, parked in the
   18mm foot margin of the paper where no story text ever goes -- so it can
   never sit on top of a word, on any screen size. The footer has a "pages
   N to M" box. Both need JavaScript to do anything, so both stay hidden
   until book.js puts the "js" class on <html>: a button that cannot work is
   worse than no button. None of this ever prints (section 10 hides it).
   ------------------------------------------------------------------------ */
.page-tools { display: none; }
.print-range { display: none; }

html.js .page-tools {
  display: flex;
  justify-content: flex-end;
  position: absolute;
  right: calc(var(--u) * 16);
  bottom: calc(var(--u) * 3);
  margin: 0;
  z-index: 2;
}
.btn-page {
  min-height: 44px;              /* a real finger target, even though it is small */
  min-width: 44px;
  padding: 0 .55rem;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  /* ROUND 8: this used to be opacity .55, which on Adhiyan's phone was a grey
     smudge you could barely read. The button is now drawn at FULL opacity and
     kept quiet by its colour instead: real ink on a faint tint of the paper,
     with a visible edge. Quiet is a colour choice, not a transparency -- so it
     stays legible in dark mode, where .55 was the worst. */
  color: var(--ink);
  background: rgba(140, 105, 60, .10);
  border: 1px solid var(--paper-edge);
  border-radius: var(--radius);
  cursor: pointer;
  opacity: 1;
  transition: background-color .18s ease, border-color .18s ease, color .18s ease;
}
.btn-page:hover, .btn-page:focus-visible {
  background: #fff3dd;
  border-color: var(--red);
  color: var(--red-dark);
}
.btn-page:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }

html.js .print-range {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  margin: 0 0 1rem;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .9rem;
}
.print-range input {
  width: 4.5rem;
  min-height: 44px;
  padding: .3rem .4rem;
  font: inherit;
  text-align: center;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--lacquer);
  border-radius: var(--radius);
}
.print-range input:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.range-msg {
  flex-basis: 100%;
  margin: .1rem 0 0;
  min-height: 1.3em;
  font-style: normal;
  font-size: .85rem;
}
.range-msg.bad { color: #ffd0c4; font-weight: 700; }

/* --- 7c. READ ALOUD (round 10) ------------------------------------------
   Adhiyan: "include and speaking button that repeat the story in either
             tamil or english".

   A bar parked along the bottom of the SCREEN -- play, stop, and the Voices
   panel that lists what this particular phone can actually say. It never
   covers a word: the book and the footer are given extra room underneath
   while the bar is up, and the back-to-top button steps up out of its way.

   NOTHING HERE EXISTS WITHOUT speak.js. The bar and the per-sheet "Read page
   n" buttons are display:none until speak.js has proved the browser really
   has a speech engine and then puts "speak" on <html> -- exactly the rule the
   print controls follow with "js". A button that cannot work is worse than no
   button, so the one case where the bar DOES appear disabled is when speech
   is missing entirely: then it appears with the reason written next to it.

   None of it prints (section 10 hides the lot).
   ------------------------------------------------------------------------ */
.speak-bar   { display: none; }
.speak-tools { display: none; }

html.speak .speak-bar {
  display: block;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 55;
  padding: .5rem .6rem;
  /* clear of the home-bar gesture area on a phone. Its own declaration, so a
     browser that has never heard of env() simply keeps the line above. */
  padding-bottom: calc(.5rem + env(safe-area-inset-bottom, 0px));
  color: var(--ink);
  background: var(--paper);
  border-top: 3px solid var(--lacquer);
  box-shadow: 0 -4px 14px rgba(0, 0, 0, .35);
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  /* a very long voice list must scroll inside the bar, never off the screen */
  max-height: 92vh;
  overflow-y: auto;
}
/* while it is actually reading, the bar wears the guacamole stripe */
html.speak .speak-bar.is-reading { border-top-color: var(--gold); }
.speak-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: .4rem;
}
.btn-speak { background: var(--gold); color: #3a2200; }
.btn-speak:hover { background: #f0bb4f; }
.btn-speak[disabled] { opacity: .55; cursor: not-allowed; box-shadow: none; }
.btn-voices { background: #1d5f8f; color: #fff; }
.btn-voices:hover { background: #2a7ab4; }

.speak-status {
  margin: .35rem 0 0;
  text-align: center;
  font-size: .82rem;
  font-style: normal;
  line-height: 1.35;
  color: var(--ink-soft);
}
.speak-bar.no-speech .speak-status { color: var(--red-dark); font-weight: 700; }

/* the line being spoken. Chrome only: a wash and an outline, never a border
   and never a size change, so not one word can move on the page. */
.speaking-now {
  background: rgba(224, 168, 60, .30);
  outline: 3px solid var(--gold);
  outline-offset: 3px;
  border-radius: var(--radius);
  transition: background-color .2s ease;
}

/* the panel of voices, above the bar */
.speak-panel {
  max-width: 34rem;
  margin: .5rem auto 0;
  max-height: 58vh;
  overflow-y: auto;
  padding: .6rem .7rem;
  background: #fff8ea;
  color: var(--ink);
  border: 2px solid var(--paper-edge);
  border-radius: var(--radius);
  font-size: .86rem;
  line-height: 1.45;
}
.speak-panel[hidden] { display: none; }
.speak-head { margin: 0 0 .4rem; font-weight: 800; font-style: normal; font-size: .95rem; }
.speak-verdict {
  margin: 0 0 .5rem;
  padding: .45rem .55rem;
  font-style: normal;
  border-radius: var(--radius);
  border-left: 5px solid var(--ink-soft);
  background: rgba(140, 105, 60, .10);
}
.speak-verdict.has-ta { border-left-color: #0f6b4a; background: rgba(15, 107, 74, .12); font-weight: 700; }
.speak-verdict.no-ta  { border-left-color: var(--red); background: rgba(192, 57, 43, .10); }
.speak-langs { margin: 0 0 .6rem; font-style: normal; color: var(--ink); }
.speak-field {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .45rem;
  margin: 0 0 .5rem;
  font-style: normal;
}
.speak-field label { font-weight: 700; }
.speak-field select,
.speak-field input[type="range"] {
  flex: 1 1 12rem;
  min-width: 0;
  min-height: 44px;                /* a real finger target */
}
.speak-field select {
  font: inherit;
  padding: .3rem .4rem;
  color: var(--ink);
  background: var(--paper);
  border: 2px solid var(--lacquer);
  border-radius: var(--radius);
  max-width: 100%;
}
.speak-check label { display: flex; align-items: center; gap: .5rem; font-weight: 400; }
.speak-check input { width: 22px; height: 22px; }
.speak-list { margin: 0 0 .5rem; }
.speak-list summary { cursor: pointer; font-weight: 700; min-height: 44px; display: flex; align-items: center; }
.speak-all {
  margin: .3rem 0 0;
  padding: .5rem;
  max-height: 40vh;
  overflow: auto;
  white-space: pre-line;           /* one voice per line, straight from the device */
  font-family: "Courier New", ui-monospace, monospace;
  font-size: .78rem;
  font-style: normal;
  line-height: 1.5;
  background: rgba(140, 105, 60, .10);
  border-radius: var(--radius);
}
.speak-note { margin: 0; font-style: normal; font-size: .8rem; color: var(--ink-soft); }

/* the bar takes room from the page instead of sitting on top of the words */
html.speak #book      { padding-bottom: 7.5rem; }
html.speak .site-foot { padding-bottom: 8.5rem; }
html.speak .totop     { bottom: 6.2rem; }

/* "Read page 12" -- the other end of the same foot margin the print button
   lives in, so neither of them can ever sit on a line of the story */
html.speak .speak-tools {
  display: flex;
  justify-content: flex-start;
  position: absolute;
  left: calc(var(--u) * 16);
  bottom: calc(var(--u) * 3);
  margin: 0;
  z-index: 2;
}
.btn-speak-page { letter-spacing: .02em; }

@media (max-width: 420px) {
  html.speak .speak-bar { padding: .4rem .35rem; padding-bottom: calc(.4rem + env(safe-area-inset-bottom, 0px)); }
  .speak-row .btn { padding: .6rem .7rem; font-size: .85rem; }
  .speak-status { font-size: .76rem; }
}

/* --- 8. mobile ----------------------------------------------------------
   The sheet does NOT change shape on a phone. It is the same A4 rectangle,
   drawn smaller. All that changes is how much gutter we waste around it.
   ------------------------------------------------------------------------ */
@media (max-width: 700px) {
  :root { --gutter: .7rem; }
  #book { padding: .8rem 0 2rem; }
}
@media (max-width: 420px) {
  :root { --gutter: .35rem; }
  .topbar-inner { padding: .4rem .5rem; gap: .35rem; }
  .btn { padding: .6rem .75rem; font-size: .88rem; }
  .brand { font-size: .92rem; }
  .totop { width: 46px; height: 46px; right: .5rem; bottom: .5rem; }
}
@media (max-width: 360px) {
  :root { --gutter: .25rem; }
  .brand { font-size: .84rem; }
  .btn { padding: .55rem .65rem; font-size: .84rem; }
  .toc-list { grid-template-columns: repeat(auto-fill, minmax(3rem, 1fr)); }
}

/* --- 9. dark mode (screen only -- print is always black on white) -------- */
@media (prefers-color-scheme: dark) {
  :root {
    --paper:      #241b17;
    --paper-edge: #4c3a2e;
    --bg:         #2b0a11;
    --bg-2:       #14050a;
    --ink:        #f2e7d9;
    --ink-soft:   #c2a88e;
    --chrome-ink: #ffe3c4;
    --gold:       #f0c05a;
    --gold-dark:  #d4a33f;
    --lacquer:    #0b0305;
    --red:        #ff7a6b;
    --red-dark:   #ffb0a3;
    /* the five elements, lit for a dark page */
    --fire:  #ff9a7a;
    --water: #7fc4ff;
    --earth: #d6a97a;
    --wind:  #9fc3d4;
    --bolt:  #e6b84a;
  }
  body { background-image:
    repeating-linear-gradient( 45deg, rgba(240,192,90,.07) 0 2px, transparent 2px 16px),
    repeating-linear-gradient(-45deg, rgba(240,192,90,.07) 0 2px, transparent 2px 16px),
    linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%); }

  /* --red / --red-dark are pale now, which is right for INK on dark paper
     and wrong for the two places they are a BACKGROUND. Those two get told
     directly, and nothing else has to know. */
  .topbar { background: linear-gradient(180deg, #5a0c16, #38070e); }
  .totop  { background: #5a0c16; color: var(--chrome-ink); border-color: var(--gold); }

  .btn { background: #ffe1b8; color: #5a1410; border-color: #0b0305; box-shadow: 0 2px 0 #0b0305; }
  .btn-print { background: var(--gold); color: #3a2200; }
  .toc-list a { background: #2e231d; color: var(--ink); border-color: var(--paper-edge); }
  .toc-list a:hover { color: #2b2018; }
  .d { color: #e9dccc; border-left-color: rgba(255,122,107,.38); }

  /* the same cast, lit for a dark page. The five ninjas ride the element
     tokens above, so they are already re-lit; these are the rest. */
  .say-five     { --speaker: #5fd6a0; }
  .say-zhao     { --speaker: #c0a3ff; }
  .say-general  { --speaker: #9db2c4; }
  .say-flame    { --speaker: #ffab6b; }
  .say-emperor  { --speaker: #ff8fbb; }
  .say-goddess  { --speaker: #ee9fd6; }
  .say-voice    { --speaker: #b1a8cc; }
  .say-darkness { --speaker: #9a8fc4; }
  .say-crowd    { --speaker: #c2a88e; }
  .say-unknown  { --speaker: #9b8d7e; }

  .part    { color: var(--gold); }
  .chapter { color: var(--red-dark); border-bottom-color: var(--gold-dark); }
  .sfx  { text-shadow: calc(var(--u) * .7) calc(var(--u) * .7) 0 rgba(240,192,90,.45); }
  .sign { color: #ffe6c0; border-color: #6b533c; }
  .cover, .page, .the-end { box-shadow: 0 6px 18px rgba(0,0,0,.6); }
  /* quiet but readable: warm ink on a slightly lifted panel, never a faded grey */
  .btn-page { color: #f2e7d9; background: rgba(255, 240, 214, .10); border-color: #6b543f; }
  .btn-page:hover, .btn-page:focus-visible {
    background: #3c2e24; border-color: var(--red); color: #ffd9c9;
  }
  /* read aloud, lit for a dark page */
  html.speak .speak-bar { background: #261c17; border-top-color: #0b0305; box-shadow: 0 -4px 14px rgba(0,0,0,.6); }
  .btn-speak  { background: var(--gold); color: #3a2200; border-color: #0b0305; }
  .btn-voices { background: #1d5f8f; color: #fff; border-color: #0b0305; }
  .speak-panel { background: #2e231d; border-color: var(--paper-edge); }
  .speak-verdict { background: rgba(255, 240, 214, .08); }
  .speak-verdict.has-ta { background: rgba(95, 214, 160, .16); }
  .speak-verdict.no-ta  { background: rgba(255, 122, 107, .14); }
  .speak-all { background: rgba(255, 240, 214, .08); }
  .speak-field select { background: #2e231d; color: var(--ink); border-color: #6b543f; }
  .speaking-now { background: rgba(240, 192, 90, .24); outline-color: var(--gold); }
}

/* --- 10. PRINT ----------------------------------------------------------
   Real A4. One story page per physical sheet. The page box supplies the
   margins, so the sheet boxes above are flattened back to "just content" --
   otherwise a 297mm box inside a 263mm page box would spill onto a second
   sheet. Black text on white paper, no colour, no UI, no JavaScript.
   ------------------------------------------------------------------------ */
@page {
  size: A4;
  margin: 16mm 16mm 18mm;
}

@media print {
  :root {
    color-scheme: light;
    --u: 1mm;              /* on paper, one millimetre is one millimetre */
  }

  html, body {
    background: #fff !important;
    background-image: none !important;
    color: #000 !important;
    font-size: 19pt;
    line-height: 1.4;
    margin: 0; padding: 0;
    overflow: visible !important;
  }

  /* hide all the screen furniture */
  .topbar, .lanterns, .progress, .progress-bar, .contents, .contents-panel,
  .totop, .btn, .btn-print, button, .site-foot, .cover::before, .brand-mark,
  .page-tools, .btn-page, .print-range, .range-msg,
  /* round 10: not one pixel of the read-aloud machinery reaches the paper */
  .speak-bar, .speak-row, .speak-status, .speak-panel, .speak-head,
  .speak-verdict, .speak-langs, .speak-field, .speak-check, .speak-list,
  .speak-all, .speak-note, .speak-tools, .btn-speak, .btn-speak-page {
    display: none !important;
  }

  /* The line being read keeps its class while the print dialog is open (and
     speak.js also clears it on beforeprint). Either way it must leave no mark
     on the paper: the highlight is screen chrome, not part of the story. */
  .speaking-now {
    background: none !important;
    outline: 0 !important;
    box-shadow: none !important;
    border-radius: 0 !important;
  }

  /* the bar is gone, so the room that was made for it goes too */
  html.speak #book, html.speak .site-foot { padding-bottom: 0 !important; }

  /* --- printing ONE page, or a few of them -----------------------------
     book.js puts "print-scope" on <body> and "print-me" on the sheets you
     asked for, calls window.print(), and takes both off again the moment
     the dialog closes (and on a timer, in case it never says so). With no
     class on <body> NOTHING below applies and the whole book prints, exactly
     as it always did -- which is also what happens with JS switched off.
     --------------------------------------------------------------------- */
  body.print-scope .cover,
  body.print-scope .the-end,
  body.print-scope .page:not(.print-me) {
    display: none !important;
  }
  /* the first sheet you asked for must not push a blank sheet out ahead of
     itself: every .page normally forces a page break BEFORE it. */
  body.print-scope .print-first {
    break-before: auto !important;
    page-break-before: auto !important;
  }

  #book {
    max-width: none;
    width: auto !important;
    margin: 0;
    padding: 0;
    display: block;
    gap: 0;
  }

  /* flatten the screen sheet back to plain content: the @page box is the
     sheet now, and it is already exactly A4 with the same margins. */
  .cover, .page, .the-end {
    width: auto !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    padding: 0 !important;
    margin: 0 !important;
    background: none !important;
    border: 0 !important;
    box-shadow: none !important;
    border-radius: 0;
    overflow: visible !important;
    display: block;
  }

  /* --- the cover sheet --- */
  .cover {
    padding: 40mm 0 0 !important;
    text-align: center;
    break-after: page;
    page-break-after: always;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .cover-mark { display: none !important; }
  .book-title {
    color: #000 !important;
    text-shadow: none !important;
    font-size: 34pt;
    line-height: 1.15;
    margin: 0 0 8mm;
  }
  .byline { color: #000 !important; font-size: 13pt; }

  /* --- one story page per sheet --- */
  .page {
    break-before: page;
    page-break-before: always;
    break-inside: avoid;
    page-break-inside: avoid;
  }

  /* the page number stays on the printed sheet, as a plain line-art header */
  .page-title {
    color: #000 !important;
    font-size: 10pt;
    letter-spacing: .16em;
    border-bottom: 0.5pt solid #000 !important;
    padding-bottom: 2mm;
    margin: 0 0 6mm;
    break-after: avoid;
    page-break-after: avoid;
  }
  .page-title::before {
    background: #000 !important;   /* line-art chilli dot */
    width: 2.2mm; height: 2.2mm;
  }
  .page-title::after {
    background: none !important;
    height: 0;
  }

  /* chapter headings print too -- they are Adhiyan's words, not chrome */
  .part {
    color: #000 !important;
    font-size: 16pt;
    line-height: 1.2;
    letter-spacing: .26em;
    margin: 0 0 6mm;
    break-after: avoid;
    page-break-after: avoid;
  }
  .chapter {
    color: #000 !important;
    font-size: 18pt;
    line-height: 1.2;
    letter-spacing: .08em;
    margin: 2mm 0 6mm;
    padding-bottom: 2mm;
    border-bottom: 0.6pt solid #000 !important;
    break-after: avoid;
    page-break-after: avoid;
  }

  .line, .d, .sfx, .sign {
    color: #000 !important;
    font-size: 19pt;
    line-height: 1.4;
    max-width: none;
    margin: 0 0 2.4mm;
    orphans: 2;
    widows: 2;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .d {
    /* ROUND 9: the spoken lines are set LARGER than the narration, on paper
       exactly as on screen. The rule above sets every story line to 19pt;
       this one comes after it and lifts dialogue to --t-dialogue (21pt).
       Without it the book would print its dialogue at narration size -- and
       the pagination budget in verify.js, which measures dialogue at 21pt,
       would be budgeting for a page that never prints. */
    font-size: 21pt;
    border-left: 0.5pt solid #000 !important;
    padding-left: 3.5mm;
  }

  /* the speaker's name is TEXT, so it prints. On paper it is plain black --
     the name does the work, the colour was only ever a screen bonus.

     ON PAPER THE TWO COLUMNS ARE A FLOAT, NOT A GRID (round 6).
     The screen still uses CSS grid (section 6) -- browsers are excellent at
     it. Printers and PDF engines are not: several of them ANSWER YES to
     "do you support grid?" and then lay it out wrongly, which would drop the
     speaker's name on top of Adhiyan's line. A float is the one two-column
     trick every print engine ever built gets right, so print uses that and
     nothing else. Same 24mm column, same 2mm gap, same look. */
  .say {
    display: block;
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .say::after { content: ""; display: block; clear: both; }
  .say > .who {
    float: left;
    width: 24mm;
    margin: 0;
  }
  .say > .d { margin-left: 26mm; }     /* 24mm column + 2mm gap */
  .who {
    color: #000 !important;
    font-size: 10pt;
    line-height: 1.4;
    letter-spacing: .06em;
    margin: 0 0 2.4mm;
    text-align: right;
    orphans: 2;
    widows: 2;
  }
  .who.guess { opacity: 1; font-weight: 400; }
  .say > .d { border-left-color: #000 !important; }
  .sfx {
    color: #000 !important;
    text-shadow: none !important;
    transform: none !important;
    font-size: 26pt;
    line-height: 1.15;
    letter-spacing: .08em;
    margin: 4mm 0 4.5mm;
  }
  .sign {
    background: none !important;
    border: 0.5pt solid #000 !important;
    color: #000 !important;
    font-size: 13pt;
    line-height: 1.3;
    letter-spacing: .18em;
    padding: 2mm;
    margin: 4mm auto 4.5mm;
    max-width: 110mm;
  }
  .chapter + .line::first-letter { color: #000 !important; }

  .the-end {
    break-before: page;
    page-break-before: always;
    break-inside: avoid;
    page-break-inside: avoid;
    padding: 90mm 0 0 !important;
    text-align: center;
  }
  .the-end-line { color: #000 !important; font-size: 24pt; }
  .the-end::after { background: none !important; height: 0; margin: 0; }

  a { color: #000 !important; text-decoration: none !important; }
}

/* --- 11. why there is no @supports block here any more -------------------
   Round 4 and 5 printed the speaker column with CSS grid and kept a float
   version behind  @supports not (display: grid)  as a safety net. That net
   had a hole in it: an engine only falls into it if it ADMITS it cannot do
   grid. The print engines that actually get grid wrong (old wkhtmltopdf and
   friends) claim they support it, sail past the @supports test, and lay the
   name on top of the line.

   So round 6 turned it round: print uses the float ALWAYS (see section 10),
   and there is no fallback to get wrong. Screen keeps the grid. Nothing on
   screen changed at all.
   ------------------------------------------------------------------------ */

/* --- 12. the shelf, the books menu, and a book that is not written yet ----
   index.html is the shelf; each book is its own page; every page carries a
   "Books" menu to hop between them. books.js is the only place that knows
   what exists -- this is just paint.
   ------------------------------------------------------------------------ */

/* the books menu in the top bar (it reuses the contents panel) */
.book-list { list-style: none; margin: 0; padding: 0; }
.book-list li + li { margin-top: .4rem; }
.book-list a {
  display: flex; align-items: center; flex-wrap: wrap; gap: .4rem;
  min-height: 44px;
  padding: .5rem .7rem;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .95rem; font-weight: 700;
  text-decoration: none;
  color: var(--ink);
  background: #fff;
  border: 2px solid var(--paper-edge);
  border-radius: 9px;
}
.book-list a:hover { background: var(--gold); border-color: var(--lacquer); }
.book-list a:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.book-list a.here { background: #fff3dc; border-color: var(--lacquer); }
.book-list a.here::after { content: "you are here"; font-size: .7rem; font-weight: 400; font-style: italic; color: var(--ink-soft); }
.soon-tag {
  font-size: .7rem; font-weight: 700; font-style: normal;
  letter-spacing: .04em; text-transform: uppercase;
  color: #4a2a08;
  background: var(--gold);
  border-radius: 999px;
  padding: .12rem .5rem;
}
.toc-none { margin: 0; font-style: italic; color: var(--ink-soft); }

/* the shelf page */
.shelf { max-width: 46rem; margin: 0 auto; padding: 1.2rem var(--gutter) 2.4rem; }
.shelf-head { text-align: center; margin: 0 0 1.6rem; }
.shelf-head .cover-mark {
  width:  calc(var(--u) * 44);
  height: calc(var(--u) * 44);
  margin: 0 auto calc(var(--u) * 7);
}
.shelf-title {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(1.5rem, 7vw, 2.6rem);
  line-height: 1.1;
  margin: 0 0 .4rem;
  color: var(--gold);
  text-shadow: 2px 2px 0 var(--lacquer);
  text-wrap: balance;
}
.shelf-sub { margin: 0; font-style: italic; color: var(--chrome-ink); }

.books { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.1rem; }
.book-card {
  color: var(--ink);                 /* a card is paper: dark ink, not pale */
  background: var(--paper);
  border: 3px solid var(--paper-edge);
  border-radius: 16px;
  box-shadow: 0 6px 0 rgba(0,0,0,.35);
  padding: 1rem 1rem 1.1rem;
}
.book-card.is-soon { border-style: dashed; box-shadow: none; }
.book-num {
  margin: 0 0 .2rem;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .8rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-soft);
  font-style: normal;
}
.book-name {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  font-size: clamp(1.15rem, 5vw, 1.6rem);
  line-height: 1.15;
  margin: 0 0 .35rem;
  text-wrap: balance;
}
.book-name a { color: var(--red-dark); text-decoration: none; }
.book-name a:hover { text-decoration: underline; }
.book-sub { margin: 0 0 .45rem; font-style: italic; color: var(--ink-soft); font-size: .95rem; }
.book-status { margin: 0 0 .6rem; font-size: .9rem; color: var(--ink-soft); }
.pill {
  display: inline-block;
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-size: .72rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  padding: .18rem .55rem;
  border-radius: 999px;
  background: var(--gold);
  color: #4a2a08;
  margin-right: .35rem;
}
.is-soon .pill { background: #e6ded2; }
.book-blurb { margin: 0 0 .9rem; line-height: 1.5; }
.book-go { margin: 0; }
.btn-read {
  text-decoration: none;
  color: var(--red-dark);
}
.shelf-extra {
  margin: 1.4rem 0 0;
  font-size: .95rem;
  line-height: 1.5;
  color: var(--chrome-ink);
  text-align: center;
}
.shelf-extra a { color: var(--gold); }

/* A book that has no story in it yet. It is still a real A4 sheet -- an
   empty one, waiting -- so Book 2 looks like it belongs on the same shelf.
   The dashed border is the only thing saying "nothing here yet". */
.soon {
  width:  calc(var(--u) * 210);
  height: calc(var(--u) * 297);
  padding: calc(var(--u) * 16) calc(var(--u) * 16) calc(var(--u) * 18);
  color: var(--ink);
  background: var(--paper);
  border: 2px dashed var(--paper-edge);
  border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(0,0,0,.35);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  overflow: visible;
  position: relative;
}
.soon-head {
  font-family: "Trebuchet MS", "Segoe UI", system-ui, sans-serif;
  font-weight: 800;
  font-size: calc(var(--u) * 9);
  color: var(--red-dark);
  margin: 0 0 calc(var(--u) * 8);
}
.soon-line {
  max-width: calc(var(--u) * 150);
  margin: 0 0 calc(var(--u) * 5);
  font-size: calc(var(--u) * 5);
  line-height: 1.55;
}
.soon-links { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; margin-top: calc(var(--u) * 6); }

.foot-shelf { margin: 1.2rem 0 0 !important; font-style: normal !important; }
.foot-shelf a { color: var(--gold); font-weight: 700; text-decoration: none; }
.foot-shelf a:hover { text-decoration: underline; }

/* the shelf never needs the sheet machinery */
.shelf-body #book { display: none; }

/* the brand shortens itself rather than squeezing the buttons off a phone */
.brand-short { display: none; }
@media (max-width: 420px) {
  .brand-long  { display: none; }
  .brand-short { display: inline; }
}

@media (prefers-color-scheme: dark) {
  .book-list a { background: #261c17; color: var(--ink); border-color: #3d2d24; }
  .book-list a.here { background: #372620; }
  .book-card { background: #261c17; border-color: #3d2d24; box-shadow: none; }
  .is-soon .pill { background: #3d2d24; color: #e8dccd; }
  .book-name a, .btn-read, .shelf-extra a, .foot-shelf a { color: var(--gold); }
}

@media print {
  .shelf, .shelf-body .topbar, .shelf-body .site-foot,
  .book-list, .soon-links, .foot-shelf { display: none !important; }
  .soon, .soon-head, .soon-line { display: block; }
}
