/* Rushlight Books — hub mockup stylesheet (shared by every page)
   Palette: bone/cream page · ink text · rushlight amber accent. Type: EB Garamond (display) + Inter (UI). */
:root{
  --bg:#f5efe3; --card:#fcfaf4; --alt:#efe7d6; --ink:#26211b; --muted:#6c6153; --faint:#9a8f7d;
  --amber:#c8862c; --amberdark:#9a5f13; --deep:#1a1410; --border:#e4dac6;
  --serif:'EB Garamond',Georgia,serif; --sans:'Inter',system-ui,sans-serif;
  /* franchise bands */
  --endurance:#3d4b5a; --wireless:#8a6f47; --wayhouses:#5c7355; --keptflame:#b5732a;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.6;-webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}
.wrap{max-width:1080px;margin:0 auto;padding:0 28px}
.serif{font-family:var(--serif)}
.mock{background:var(--deep);color:#e9dcbf;text-align:center;font-size:12.5px;letter-spacing:.06em;padding:6px}

/* header */
header{border-bottom:1px solid var(--border)}
.nav{display:flex;align-items:center;justify-content:space-between;padding:20px 0}
.brand{display:flex;align-items:center;gap:11px}
.flame{width:26px;height:34px;flex:0 0 auto}
.brand .name{font-family:var(--serif);font-size:23px;font-weight:600;letter-spacing:.02em;line-height:1}
.brand .sub{font-size:10px;letter-spacing:.34em;color:var(--faint);text-transform:uppercase;margin-top:3px}
nav.links{display:flex;gap:26px;font-size:14.5px;color:var(--muted)}
nav.links a:hover,nav.links a.on{color:var(--ink)}

/* buttons */
.btn{display:inline-block;font-size:14px;font-weight:500;padding:9px 18px;border-radius:2px;border:1px solid var(--deep);background:var(--deep);color:#f5efe3;transition:.15s;cursor:pointer}
.btn:hover{background:transparent;color:var(--deep)}
.btn.amber{background:var(--amber);border-color:var(--amber);color:#2a1c05}
.btn.amber:hover{background:transparent;color:var(--amberdark)}
.btn.ghost{background:transparent;color:var(--deep)}
.btn.ghost:hover{background:var(--deep);color:#f5efe3}

/* type + sections */
.rule{width:44px;height:1px;background:var(--amber);margin:0 auto 26px}
.sec{padding:56px 0}
.sec.alt{background:var(--alt);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.eyebrow{font-size:11px;letter-spacing:.28em;text-transform:uppercase;color:var(--faint);text-align:center;margin-bottom:8px}
.sec h2{font-family:var(--serif);font-weight:500;font-size:30px;text-align:center;margin-bottom:34px}
.pagehead{text-align:center;padding:56px 0 34px;border-bottom:1px solid var(--border)}
.pagehead h1{font-family:var(--serif);font-weight:500;font-size:40px;margin-bottom:10px}
.pagehead p{color:var(--muted);max-width:56ch;margin:0 auto;font-size:16.5px}

/* hero */
.hero{text-align:center;padding:78px 0 64px}
.hero h1{font-family:var(--serif);font-weight:500;font-size:46px;line-height:1.15;max-width:15ch;margin:0 auto 20px}
.hero p{font-size:18px;color:var(--muted);max-width:52ch;margin:0 auto 30px}
.hero .cta{display:flex;gap:14px;justify-content:center}

/* covers */
.cover{aspect-ratio:5/8;border-radius:3px;box-shadow:0 6px 22px rgba(30,22,10,.16);display:flex;flex-direction:column;justify-content:space-between;padding:16px 14px;color:#f0eae0}
.cover .ct{font-family:var(--serif);font-size:19px;line-height:1.1;font-weight:600}
.cover .ca{font-size:10px;letter-spacing:.14em;text-transform:uppercase;opacity:.82}
.cover .cs{font-size:9px;letter-spacing:.2em;text-transform:uppercase;opacity:.7}
.cover.sm .ct{font-size:15px}

/* featured book */
.feature{display:grid;grid-template-columns:200px 1fr;gap:38px;align-items:center;background:var(--card);border:1px solid var(--border);border-radius:6px;padding:34px}
.fq{font-family:var(--serif);font-style:italic;font-size:20px;color:var(--amberdark);margin-bottom:12px}
.feature h3{font-family:var(--serif);font-size:26px;font-weight:600;margin-bottom:4px}
.meta{font-size:12.5px;letter-spacing:.06em;color:var(--faint);text-transform:uppercase;margin-bottom:14px}
.feature p{color:var(--muted);font-size:15.5px;margin-bottom:20px;max-width:60ch}
.row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.ku{font-size:12.5px;color:var(--faint)}

/* franchise cards — real key art + the franchise wordmark in its own cover face */
.worlds{display:grid;grid-template-columns:repeat(2,1fr);gap:22px}
.world{background:var(--card);border:1px solid var(--border);border-radius:6px;overflow:hidden;display:block;transition:.15s}
.world:hover{border-color:var(--amber);transform:translateY(-2px)}
.world .art{aspect-ratio:3/2;background-size:cover;background-position:center;position:relative;display:flex;align-items:flex-end;padding:18px 20px}
.world .art::after{content:"";position:absolute;inset:0;background:linear-gradient(to top,rgba(12,10,8,.72) 0%,rgba(12,10,8,.28) 38%,rgba(12,10,8,0) 68%)}
.world .wm{position:relative;z-index:1;color:#f2e7d2;line-height:1.05}
.world .inner{padding:20px 22px 22px}
.world .pen{font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);margin-bottom:8px}
.world .tag{font-family:var(--serif);font-style:italic;font-size:17px;color:var(--amberdark);margin-bottom:9px}
.world p{font-size:14.5px;color:var(--muted);margin-bottom:14px}
.world .enter{font-size:13.5px;font-weight:500;color:var(--amberdark)}
/* the four locked cover faces (ref-cover-typography.md) */
.wm-endurance{font-family:'EB Garamond',Georgia,serif;font-weight:600;font-size:27px;letter-spacing:.20em;text-transform:uppercase}
.wm-wireless {font-family:'Lora',Georgia,serif;font-weight:600;font-size:24px;letter-spacing:.14em;text-transform:uppercase}
.wm-wayhouses{font-family:'Fraunces',Georgia,serif;font-weight:600;font-size:30px;letter-spacing:.01em}
.wm-keptflame{font-family:'Cinzel','Trajan Pro',Georgia,serif;font-weight:600;font-size:24px;letter-spacing:.22em;text-transform:uppercase}

/* catalogue grid */
.filters{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin-bottom:34px}
.chip{font-size:13px;padding:6px 14px;border:1px solid var(--border);border-radius:20px;background:var(--card);color:var(--muted);cursor:pointer}
.chip.on{background:var(--deep);color:#f5efe3;border-color:var(--deep)}
.grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px}
.bk .cover{margin-bottom:10px}
.bk h4{font-family:var(--serif);font-size:17px;font-weight:600;line-height:1.2}
.bk .bm{font-size:12px;color:var(--faint);margin-top:2px}
.shelf{margin-bottom:44px}
.shelf .sh{display:flex;align-items:baseline;justify-content:space-between;border-bottom:1px solid var(--border);padding-bottom:8px;margin-bottom:20px}
.shelf .sh h3{font-family:var(--serif);font-size:23px;font-weight:600}
.shelf .sh .pen{font-size:12.5px;color:var(--faint)}

/* book detail */
.detail{display:grid;grid-template-columns:280px 1fr;gap:44px;align-items:start}
.detail h1{font-family:var(--serif);font-size:38px;font-weight:600;line-height:1.15;margin-bottom:6px}
.detail .blurb p{color:var(--muted);font-size:16px;margin-bottom:14px;max-width:62ch}
.buy{background:var(--card);border:1px solid var(--border);border-radius:6px;padding:20px;margin:24px 0}
.buy .row{margin-bottom:8px}
.facts{border-top:1px solid var(--border);padding-top:16px;margin-top:22px;font-size:13.5px;color:var(--muted)}
.facts div{display:flex;gap:10px;margin-bottom:5px}
.facts span:first-child{color:var(--faint);min-width:110px}

/* authors */
.pens{display:grid;grid-template-columns:repeat(2,1fr);gap:22px}
.pen-card{background:var(--card);border:1px solid var(--border);border-radius:6px;padding:26px}
.pen-card h3{font-family:var(--serif);font-size:24px;font-weight:600}
.pen-card .shelf-line{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);margin:2px 0 12px}
.pen-card p{font-size:14.5px;color:var(--muted);margin-bottom:14px}

/* newsletter */
.news{background:var(--deep);color:#f2e8d4;border-radius:8px;padding:46px 40px;text-align:center}
.news h2{font-family:var(--serif);color:#f6eeda;font-size:29px;font-weight:500;margin-bottom:10px}
.news p{color:#cdbfa4;max-width:50ch;margin:0 auto 22px;font-size:15.5px}
.news form{display:flex;gap:10px;max-width:440px;margin:0 auto}
.news input{flex:1;padding:12px 15px;border-radius:2px;border:1px solid #4a4030;background:#241d15;color:#f2e8d4;font-family:var(--sans);font-size:14.5px}
.news input::placeholder{color:#8a7c63}
.news small{display:block;margin-top:12px;color:#8a7c63;font-size:12px}

/* prose */
.prose{max-width:62ch;margin:0 auto}
.prose p{font-size:17px;color:var(--muted);margin-bottom:18px}
.prose p.lead{font-family:var(--serif);font-size:22px;color:var(--ink);line-height:1.5}

/* footer */
footer{border-top:1px solid var(--border);padding:44px 0 30px;margin-top:16px}
.fcols{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:30px;margin-bottom:30px}
.fcols h4{font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--faint);margin-bottom:12px}
.fcols a{display:block;font-size:14px;color:var(--muted);margin-bottom:8px}
.fcols a:hover{color:var(--ink)}
.fbrand .name{font-family:var(--serif);font-size:20px;font-weight:600}
.fbrand p{font-size:13px;color:var(--muted);margin-top:8px;max-width:34ch}
.legal{border-top:1px solid var(--border);padding-top:18px;display:flex;justify-content:space-between;font-size:12.5px;color:var(--faint);flex-wrap:wrap;gap:8px}

@media(max-width:860px){.grid{grid-template-columns:repeat(2,1fr)}.detail{grid-template-columns:1fr}}
@media(max-width:760px){
  .feature{grid-template-columns:1fr;text-align:center}.feature .cover{width:170px;margin:0 auto}
  .worlds,.pens{grid-template-columns:1fr}.fcols{grid-template-columns:1fr 1fr}.hero h1{font-size:36px}nav.links{display:none}
}

/* --- generator additions (appended to _brand/mockups/rushlight.css at build) ------- */

/* ⛔ .cover in rushlight.css was written for a DIV (the mockup drew fake covers with the title
   typeset inside it, hence padding + flex + a text colour). The generator uses a real <img>.
   Padding on an <img> shows the PAGE BACKGROUND inside the element — which is what put a cream
   "white border" around all four Kettle covers. The art is clean; the class was for a different
   element. Reset everything the div version assumed. */
.cover{width:100%;height:auto;aspect-ratio:5/8;object-fit:cover;border-radius:3px;
  box-shadow:0 6px 22px rgba(30,22,10,.16);display:block;
  padding:0;background:none;border:0}
.cover.sm{box-shadow:0 4px 14px rgba(30,22,10,.14)}
.coverlink{display:block}
.detail img.cover{max-width:280px}
.blurb p{color:var(--muted);font-size:16px;margin-bottom:14px;max-width:62ch}
.bk.sm{display:block}
.wgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.wcard{display:block;background:var(--card);border:1px solid var(--border);border-radius:6px;
  padding:20px 22px;transition:.15s}
.wcard:hover{border-color:var(--accent);transform:translateY(-2px)}
.wcard h4{font-size:19px;font-weight:600;margin-bottom:6px}
.wcard p{font-size:14px;color:var(--muted)}
.news form input:disabled,.news form button:disabled{opacity:.5;cursor:not-allowed}
.news small strong{color:#e8c98a}

/* ⛔ CLASS-NAME MISMATCH — the real reason "The Wireless War" looked small.
   build_site derives the class by stripping hyphens: the-wireless-war -> .wm-wirelesswar
   but rushlight.css only defines .wm-wireless. No rule matched, so the wordmark rendered
   completely UNSTYLED — not merely small, but sentence-case and untracked while the other
   three were uppercase and letter-spaced. (The other three survive by luck: wayhouses,
   endurance and keptflame happen to collapse to the names the stylesheet already uses.)
   Defining the derived name here fixes it without touching the shared design system.
   Lora's caps run smaller than Fraunces/Cinzel, so it is sized up slightly to sit optically
   level with its neighbours. */
.wm-wirelesswar{font-family:'Lora',Georgia,serif;font-weight:600;font-size:26px;
  letter-spacing:.13em;text-transform:uppercase}
/* --- the home page's free-book row: four peers, four-up -----------------------------
   Reuses .grid (4 cols → 2 at 860px) and .bk, plus the bits a taller card needs: the
   title becomes a link, a pen-name line, a line of copy, and a per-franchise CTA.
   ⛔ NO HERO. The home page used to lead with a single full-width featured book, which
   made one world — always the same one — read as the house default. Four worlds, four
   identical cards (hub plan §11.6). This row's four-up shape is Rowan's own instruction
   and is deliberately NOT differentiated; the two sections around it are.
   ⛔ The button on each card points at THAT franchise's own MailerLite page. */
.mgrid{align-items:start}
.mg .bkt a{color:inherit}
.mg .bkt a:hover{color:var(--amberdark)}
.mg .bm{margin-top:4px}
.mg .bpen{font-size:12px;color:var(--faint);margin-top:2px}
.mg p{font-size:14px;color:var(--muted);margin-top:10px}
.mg .row{margin-top:12px}
.mgbtn{font-size:13px}
/* the free book's art is a real cover on three of the four worlds and a typographic plate
   where the catalogue has none — both must occupy the same box, or the row stops reading
   as four peers. .bk .cover already carries the 10px gap, so the wrapper must not add a
   second one. */
.mg-art{display:block;margin-bottom:10px}
.mg-art .cover,.mg-art .plate{margin-bottom:0}

/* --- the home page's Book One SHELF (28 Jul 2026) ----------------------------------
   ⛔ WHY THIS IS NOT A GRID. The free-book row above and the worlds row below both show
   four items; when this one did too, in the same card shape as the row above it, three
   consecutive sections read as one long list and the page lost its shape. So this is a
   shelf, not a card grid: small covers in one tight CENTRED row standing on a hairline,
   with the blurb paragraph and the bordered button removed. It is narrower than the
   column it sits in — that is the point; it should not fill the width the way the row
   above it does.
   Nothing here reuses .bk, so the .bk rules further down do not reach it and it carries
   its own type + tap-target sizes. */
.b1shelf{display:flex;flex-wrap:wrap;justify-content:center;gap:18px 44px;
  border-bottom:1px solid var(--border);padding-bottom:26px}
.b1item{width:150px;text-align:center}
.b1item .cover{margin-bottom:8px}
.b1item .bkt{font-family:var(--serif);font-size:16px;font-weight:600;line-height:1.25}
/* 16px x 1.25 = 20 + 20 padding = 40, so min-height carries it to the 44px floor */
.b1item .bkt a{display:inline-block;min-height:44px;padding:10px 2px;color:inherit}
.b1item .bkt a:hover{color:var(--amberdark)}
.b1item .bm{font-size:12px;color:var(--faint);margin-top:-2px;line-height:1.35}
/* the notify link — a DIFFERENT intent from the amber "Get a free book" buttons, so
   deliberately a different affordance as well as different words.
   13px x 1.6 = 20.8 + 24 = 44.8 */
.b1notify{display:inline-block;font-size:13px;line-height:1.6;color:var(--amberdark);
  padding:12px 6px;margin:2px -6px 0}
.b1notify:hover{text-decoration:underline}
/* a phone is 334px of content at 390px wide: 128 + 22 + 128 = 278, so it stays two-up
   rather than collapsing to a single column of four */
@media(max-width:760px){
  .b1shelf{gap:14px 22px;padding-bottom:20px}
  .b1item{width:128px}
  .b1item .bkt{font-size:15px}
}

/* --- the home page's worlds row: the widest moment on the site ----------------------
   ⛔ THE ONE PLACE THE 1080px COLUMN IS BROKEN, and only here. These are the only items
   on the home page that are PLACES rather than books, and the only ones with key art;
   letting them out past the text column is what makes the section read as a different
   moment from the two book rows above it instead of a third row of four.
   .worlds is already 2-across in the base stylesheet — this widens the container, not
   the column count, so the CARDS get bigger rather than more numerous.
   Card width = (1240 − 56 padding − 30 gap) / 2 = 577px, which is what KEYART_W = 1200
   in build_site.py is sized to serve at 2x DPI. Change one and change the other. */
.wrap.wide{max-width:1240px}
.worlds.big{gap:30px}

/* --- the world, split one index per franchise (27 Jul 2026) -------------------------
   Each world's index now sits on ITS OWN franchise page at #world (no new route — see
   world_section() for why a third `/world*` path was rejected). `/world/` keeps its URL
   and becomes a chooser of four. The heading is the whole point either way: a reader must
   never meet one world's people presented as another's. */
/* the chooser's cards carry an <h2> (they sit directly under the page <h1>, and a level
   may not be skipped). .sec h2 would centre it at 30px with a 34px gap, so it is reset to
   the card heading it actually is — same specificity as .sec h2, and extra.css is later. */
.wcard h2{font-family:var(--serif);font-size:22px;font-weight:600;text-align:left;
  margin-bottom:6px;line-height:1.25}
/* the franchise with no public world entities yet: present, honest, and not a link to
   nothing. A <div>, so there is nothing to click and nothing for a crawler to follow.
   ⛔ Quieted with the dashed --alt treatment the greyed series shelf uses, NOT with
   opacity: opacity would blend the copy toward the card and drop it under 4.5:1, and this
   card's whole job is to say clearly that the world is coming. --muted on --alt = 4.92:1. */
.wcard.off{background:var(--alt);border-style:dashed;cursor:default}
.wcard.off:hover{border-color:var(--border);transform:none}
/* four worlds, four peers: 2x2, not a 3-across row with one orphan underneath.
   Must stay ABOVE the 760px rule below, which collapses every .wgrid to one column. */
.wchoose{grid-template-columns:repeat(2,1fr)}

/* --- portraits: an enhancement layered on the card, never a slot (28 Jul 2026) -------
   ⛔ ONE ENTITY IN SEVENTY-ONE HAS ART. So the design problem is not "how does the picture
   look" — it is "how do the seventy cards WITHOUT one look standing beside it". Two rules
   follow, and they are the whole of this block:
     1. FLOAT, never grid. A float occupies space only when the element exists, so a card
        with no portrait is byte-for-byte and pixel-for-pixel the card the site already
        renders. There is no reserved box, no placeholder, no grey hole to explain.
     2. SMALL AND ROUND. At 56px it reads as an avatar decorating a text card, not as the
        card's hero image. A large portrait would make the row it sits in look like a grid
        of missing pictures with one filled; an avatar looks like a card that happens to
        have a face on it — which is exactly the truth.
   The text wraps around it, so nothing shifts and no min-height is needed. */
.wcard .wthumb{float:right;width:56px;height:56px;border-radius:50%;object-fit:cover;
  /* the master is a 2:3 headshot with the face high in frame; a centred square crop would
     cut the chin. 22% from the top puts the eyes just above the circle's centre. */
  object-position:center 22%;margin:2px 0 6px 14px;
  border:1px solid var(--border);box-shadow:0 2px 8px rgba(30,22,10,.14)}

/* the entity's own page: the portrait sits ABOVE the entry, not floated beside it.
   ⛔ MEASURED, not preferred. Floated right at 200px it took 226px out of the .prose column's
   570px reading measure and left the text at 32–38 characters a line — visibly ragged, and well
   under the ~45ch floor for comfortable reading. The picture is not worth breaking the one thing
   the page is for. As a block above the prose the entry keeps its full 58ch measure, the portrait
   still reads at a proper size, and it needs no responsive exception because it never competes
   with the text for width. An entity with no portrait gets the page it has today, unchanged. */
.wportrait{display:block;width:190px;aspect-ratio:2/3;object-fit:cover;object-position:center 22%;
  border-radius:4px;margin:6px 0 24px;box-shadow:0 6px 22px rgba(30,22,10,.16)}

/* --- family sections on the People page (29 Jul 2026) --------------------------------
   ⛔ THE BOX IS --alt, NOT A SECOND BORDERED CARD. The cards inside are --card (#fcfaf4) on a
   --bg (#f5efe3) page; a bordered white box around bordered white cards reads as a table, and
   the page stops looking like a shelf. Dropping the group to --alt (#efe7d6) instead puts the
   cards a step LIGHTER than their container, so the family reads as a tray the cards sit in
   and the eye still lands on the people, not on the joinery.
   The heading is the small-caps meta treatment the site already uses for shelf labels, so a
   family name never competes with the character names it is introducing. --muted on --alt is
   the pairing measured at 4.64:1 in rushlight.css — the one contrast this block relies on. */
.wfam{background:var(--alt);border:1px solid var(--border);border-radius:8px;
  padding:20px 22px 22px;margin-bottom:20px}
.wfam:last-child{margin-bottom:0}
/* ⛔ MUST BE `.wfam .wfam-h`, NOT `.wfam-h`. `.sec h2` (0,1,1) is a page-section title — serif,
   30px, centred, 34px below — and it out-specifies a bare class (0,1,0). On the first build the
   family label inherited all three and rendered as a second page heading competing with <h1>
   People; only the colour and letter-spacing, which .sec h2 does not set, came through. The
   result looked deliberate, which is exactly why it needed catching. */
.wfam .wfam-h{font-size:13px;letter-spacing:.13em;text-transform:uppercase;color:var(--muted);
  font-family:inherit;font-weight:600;text-align:left;margin:0 0 14px}
/* grouped cards drop to h3 (see build_spoke_world_index) and must not shrink for it —
   the heading level changed for the outline, not for the design. */
.wcard h3{font-family:var(--serif);font-size:22px;font-weight:600;margin-bottom:6px}

@media(max-width:760px){.wgrid{grid-template-columns:1fr}
  /* the tray costs horizontal room the one-column grid needs; keep the label, lose the inset */
  .wfam{padding:16px 14px 18px}}

/* ===================================================================================
   THE IA PASS — nav dropdowns, franchise/series/author pages, shelf labels, filters
   (hub plan §11, built 27 Jul 2026)
   =================================================================================== */

/* --- nav: two axes, Books ▾ and Worlds ▾ -------------------------------------------
   ⛔ NO JAVASCRIPT OPENS THESE. They open on :hover AND on :focus-within, which means a
   tap works (tapping the <button> focuses it) and Tab works. A hover-only menu is
   invisible to every phone reader, and the phone is where the back-of-book QR lands. */
nav.links{align-items:center;position:relative}
.ni{position:relative}
.nt{background:none;border:0;padding:0;font:inherit;color:var(--muted);cursor:pointer;
  display:inline-flex;align-items:center;gap:5px;line-height:inherit}
.nt::after{content:"▾";font-size:9px;opacity:.75;position:relative;top:1px}
.nt:hover,.nt.on,.ni:hover .nt,.ni:focus-within .nt{color:var(--ink)}
.menu{display:none;position:absolute;left:-16px;top:100%;padding-top:14px;z-index:80}
.ni:hover>.menu,.ni:focus-within>.menu{display:block}
.mpane{background:var(--card);border:1px solid var(--border);border-radius:6px;padding:8px 0;
  box-shadow:0 12px 34px rgba(30,22,10,.16);min-width:238px}
.mf{display:flex;align-items:baseline;justify-content:space-between;gap:18px;padding:9px 18px;
  font-size:14.5px;color:var(--ink)}
.mfsub{font-size:11.5px;color:var(--faint)}
.mchev{color:var(--faint);font-size:15px}
.mrow{position:relative}
.mrow:hover>.mf,.mrow:focus-within>.mf{background:var(--alt);color:var(--amberdark)}
/* ⛔ THE SAME HIGHLIGHT FOR MENUS WITH NO `.mrow` (4 Aug 2026, Rowan: "the world drop down doesn't
   have the highlight bar"). The Books menu wraps every item in a `.mrow` because each one owns a
   flyout of series; the Worlds and Authors menus are flat lists of bare `.mf` links, so the rule
   directly above — which selects `.mrow:hover > .mf` — never matched them and those two menus had
   no hover state at all. Not a palette choice: it is the same declaration, applied to the same
   object, in the case where it has no wrapper. ⚑ `:focus-visible`, not `:focus`, so a mouse click
   does not leave the row lit after the menu closes; keyboard Tab still shows it. */
.mpane>a.mf:hover,.mpane>a.mf:focus-visible{background:var(--alt);color:var(--amberdark)}
.mseries{display:none;position:absolute;left:100%;top:-9px;min-width:250px;padding:8px 0;
  background:var(--card);border:1px solid var(--border);border-radius:6px;
  box-shadow:0 12px 34px rgba(30,22,10,.16)}
.mrow:hover>.mseries,.mrow:focus-within>.mseries{display:block}
.msh{font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--faint);
  padding:4px 18px 8px}
.ms{display:block;padding:7px 18px;font-size:14px;color:var(--ink)}
a.ms:hover{background:var(--alt);color:var(--amberdark)}
/* ⛔ the greyed, unclickable series (hub plan §11.3). A <span>, never an <a>: there is no
   page, so there must be nothing to click and nothing for a crawler to follow. */
.ms.off{color:var(--faint);cursor:default}
.ms.off i.pill{display:inline-block;margin-top:5px}

/* ⛔ TOUCH + NARROW — the Books menu is FOUR ROWS, not thirty-seven.
   The flyout cannot fly out on a phone, so every .mseries used to be forced open: ~37 rows in a
   560px scroll pane, 29 of them dead grey names, as the primary navigation on the device the
   back-of-book QR lands on. It worked; it just asked a reader to scroll a wall of things they
   cannot click. Now: the four franchises, each with one "+N more in this world →" row into the
   franchise page — where the same names sit under "Still to come", which frames them as ambition
   rather than as a broken menu. Desktop hover/focus behaviour is untouched: it works there. */
.mmore{display:none}
@media(hover:none),(max-width:900px){
  .mseries,.mrow:hover>.mseries,.mrow:focus-within>.mseries{display:none}
  .mmore{display:block;padding:11px 18px 13px 18px;font-size:13px;color:var(--amberdark)}
  .mrow+.mrow{border-top:1px solid var(--border)}
  .mf{font-weight:600;padding-bottom:4px}
  .mpane{max-height:min(70vh,560px);overflow-y:auto}
}
/* rushlight.css hides the whole nav below 760px — which left a phone with no menu at all.
   Wrap it onto its own row instead. */
@media(max-width:760px){
  .nav{flex-wrap:wrap;row-gap:12px}
  nav.links{display:flex;order:3;width:100%;gap:20px;padding-top:12px;
    border-top:1px solid var(--border)}
  .menu{left:0;padding-top:10px}
  .mpane{min-width:min(86vw,320px);max-width:calc(100vw - 44px)}
}

/* --- shelf labels: genre(+style) + audience ---------------------------------------- */
.labs{display:flex;gap:7px;flex-wrap:wrap;margin:8px 0 10px}
.pagehead .labs,.worlds .labs{justify-content:center}
.world .labs{justify-content:flex-start;margin:0 0 9px}
.lab{font-size:11px;letter-spacing:.11em;text-transform:uppercase;color:var(--muted);
  border:1px solid var(--border);background:var(--card);border-radius:20px;padding:3px 10px}
/* ⛔ over KEY ART, so the background is whatever the photograph happens to be. At .32 the label
   measured 1.73:1 against a light patch of art — the scrim has to carry the contrast on its own,
   because the art underneath cannot be relied on. At .68 the worst case (pure white art) is
   5.71:1. */
.labs.onart .lab{color:#f0e6d2;border-color:rgba(242,231,210,.52);background:rgba(12,10,8,.68)}
.shelflabs{margin:-8px 0 16px}
/* ⛔ THE STYLE QUALIFIER IS NOT A GENRE (Rowan, 28 Jul 2026). "literary" is the register these
   books are written in, and it rides INSIDE the primary genre chip after a middot — lower-case and
   italic against its chip-mate's uppercase tracking, so it reads as a qualifier of the shelf rather
   than as a second shelf. It is never a filter chip and never a chip of its own; a reader filtering
   by genre must not meet it standing between Dystopian Fiction and cozy fantasy.
   Contrast measured, not estimated: --muted (#5f5648) on --card = 5.79:1, so the qualifier keeps
   the same colour as the chip and is distinguished by CASE and SLANT, not by going fainter — the
   faint tones on this palette are what the 27 Jul accessibility pass had to climb out of. */
.lab .qual{font-style:italic;text-transform:lowercase;letter-spacing:.04em}
.lab .qual::before{content:"·";font-style:normal;margin:0 5px 0 4px;opacity:.6}
/* ⛔ THE SECONDARY GENRES GET NO SEPARATE TREATMENT, and that is a measured decision rather than
   an omission. They were first given a dashed border to mark them as secondary — but --border on
   --card measures 1.33:1, so solid-vs-dashed was a distinction no reader could actually see, and
   the alternatives (a lighter tone, lower opacity) all buy the distinction by dropping the text
   under the 4.5:1 floor the 27 Jul pass exists to hold. The primary shelf is already marked, twice
   and legibly: it comes FIRST, and it is the only chip carrying the style qualifier. Three chips
   reading as three shelves is also simply true — these books do sit on all three. */

/* --- /books filters ---------------------------------------------------------------- */
.filters{align-items:center;gap:10px 8px}
.fgroup{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.flab{font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--faint);
  margin-right:2px}
.filters+.shelf{margin-top:8px}
.shelf .sh h3 a:hover,.shelf .sh .shx a:hover,.shelf .sh .pen a:hover,
.bk .bkt a:hover{color:var(--amberdark)}

/* --- franchise page ---------------------------------------------------------------- */
.fhero{background-size:cover;background-position:center;position:relative}
.fhinner{background:linear-gradient(to top,rgba(12,10,8,.86) 0%,rgba(12,10,8,.5) 55%,rgba(12,10,8,.3) 100%);
  padding:96px 0 40px;text-align:center;color:#f2e7d2}
.fhero .wm{color:#f4ead6;line-height:1.05;display:block;margin-bottom:14px}
.ftag{font-style:italic;font-size:20px;color:#e9c98f;margin-bottom:10px}
.fmeta{font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:#cdbfa4}
.fmeta a:hover{color:#fff}
.worlds.one{grid-template-columns:1fr;max-width:520px;margin:0 auto}

/* --- the greyed "still to come" shelf ----------------------------------------------- */
.shelf.coming .sh{border-bottom-style:dashed}
.greylist{display:flex;flex-wrap:wrap;gap:10px}
.greyed{font-family:var(--serif);font-size:18px;color:var(--faint);background:var(--alt);
  border:1px dashed var(--border);border-radius:4px;padding:7px 15px;cursor:default}
.greynote{font-size:13px;color:var(--faint);margin-top:14px;max-width:60ch}

@media(max-width:760px){.fhinner{padding:64px 0 32px}.ftag{font-size:17px}}

/* ===================================================================================
   1 · THE ACCESSIBILITY FLOOR (27 Jul 2026)
   Every ratio below is measured against the REAL palette, not estimated. AA wants 4.5:1
   for normal text; nothing on this site is large enough to claim the 3:1 allowance.
   =================================================================================== */
:root{
  /* --faint was #9a8f7d: 2.78:1 on --bg, 2.59:1 on --alt, 3.05:1 on --card — and it carried
     .eyebrow 11px, .meta 12.5px, .bm 12px, .legal 12.5px and the footer headings. All of the
     small print on the site was below the floor.  #776b5b -> bg 4.54, card 4.98. */
  --faint:#776b5b;
  /* --amberdark was #9a5f13: 4.25:1 on --alt, which is where the menu hovers live.
     #8a540f -> alt 5.09, bg 5.46, card 6.00. Every other amberdark use gets better too. */
  --amberdark:#8a540f;
  /* the franchise accent, resolved per franchise below; amber is the house default */
  --accent:var(--amber); --accent-rgb:200,134,44;
}
/* --alt is a darker ground, so --faint is re-resolved darker ON it rather than darkening the
   one token until it collapses into --muted:  #706555 -> 4.64:1 on --alt. */
.sec.alt{--faint:#706555}
/* the unreleased series names are meaningful, not chrome — muted, 4.92:1 on --alt */
.greyed{color:var(--muted)}
/* on --deep: #8a7c63 measured 4.47 (12px) and the same value was the input placeholder.
   #a2937a -> 6.07:1 on --deep, 5.54:1 on the input's own #241d15. */
.news small,.news input::placeholder{color:#a2937a}

/* --- "Coming soon" stops being chrome ---------------------------------------------
   On a pre-launch site this is the most decision-relevant string on a book card: it is the
   answer to "can I have this today?". It was 12px of the faintest tone in the palette.
   A bordered pill in --muted (5.79:1 on --card, 5.28:1 on --bg). */
.pill{display:inline-block;font-size:11px;line-height:1.55;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);border:1px solid var(--border);
  background:var(--card);border-radius:20px;padding:2px 10px;font-style:normal;white-space:nowrap}
.pill.free{color:var(--amberdark);border-color:var(--amber)}   /* 6.00:1 on --card */
.bk .bm{margin-top:7px}
.sec.alt .pill,.news .pill{background:var(--bg)}

/* --- headings: one level at a time -------------------------------------------------
   80 of 81 pages skipped a level, 57 of them purely because the shared footer put <h4> under
   the page <h1>. The footer headings are <h2 class=fh> now; the visual treatment is identical. */
.fcols .fh{font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--muted);
  margin-bottom:12px;font-weight:600}
/* the series title on a shelf is the section heading (was <h3> under an <h1>) */
.shelf .sh .shx{font-family:var(--serif);font-size:23px;font-weight:600;text-align:left;
  margin-bottom:0}
/* a book card's title (was <h4> under an <h2>) */
.bk .bkt{font-family:var(--serif);font-size:17px;font-weight:600;line-height:1.2}
.bk.sm .bkt{font-size:15px}
/* an author / free-book card's own title (was <h3> under the page <h1> on /authors and /free) */
.pen-card .pct{font-family:var(--serif);font-size:24px;font-weight:600;text-align:left;
  margin-bottom:0}
/* (`.feature .ft` lived here — the home page's single featured book. The feature was retired
   27 Jul 2026 for the four-up free-book row above, so the rule is gone with the element it
   styled. `.feature` itself is still declared in the base stylesheet, which is design-source.) */

/* --- tap targets: 44px minimum on anything you can press ---------------------------
   Nothing on the site reached it. The nav buttons matter most — 55x23.2 was the entire mobile
   menu affordance. Height comes from PADDING, never from font-size: the type stays as designed. */
.nt,nav.links>a{min-height:44px;display:inline-flex;align-items:center}
.brand{min-height:44px}
.btn{padding:11px 18px}                    /* 14px x 1.6 + 22 + 2 = 46.4 */
.mgbtn{font-size:13px;padding:11px 14px}   /* 13px x 1.6 + 22 + 2 = 44.8 */
.chip{padding:11px 16px;min-height:44px}
.mf,.ms{padding-top:11px;padding-bottom:11px}
.bk .bkt a{display:inline-block;min-height:44px;padding:10px 0}
.bk .cover{margin-bottom:0}
.fcols a{padding:11px 0;margin-bottom:0}   /* 14px x 1.6 + 22 = 44.4 */
/* the shelf/card title links measured 30-31px; padding takes them past 44 without moving the type */
.shelf .sh .shx a,.pen-card .pct a{display:inline-block;padding:7px 0}
/* the franchise·author byline under a shelf head — 15px tall, in a line of text. Padded out and
   pulled back by the same amount, so the target grows and the baseline does not move. */
.shelf .sh .pen a,.fmeta a,.meta a{display:inline-block;padding:15px 4px;margin:-15px -4px}

/* measured over the REAL key art (worst pixel behind the type, not an average): the franchise
   tagline sat at 4.18:1 on the Wireless War hero, where the art goes lightest. */
.ftag{color:#f2dcb0}

/* ===================================================================================
   2 · THE FOUR WORLDS — an accent, never a reskin
   rushlight.css has declared these four colours since day one and used them zero times, so
   all four worlds rendered in the same cream card with the same amber hover.
   ⛔ Rushlight is the landlord and the franchises are tenants: the accent may tint a rule, a
   border or a scrim. No franchise gets its own background, and NO TEXT is set in an accent —
   Kept Flame's #b5732a is 3.69:1 on --card, below the floor for body copy. .btn.amber is
   deliberately untouched: it passes at 5.45:1 and stays that way on all four pages.
   =================================================================================== */
[data-franchise="the-endurance"]   {--accent:#3d4b5a;--accent-rgb:61,75,90}    /* 8.55:1 on card */
[data-franchise="the-wireless-war"]{--accent:#8a6f47;--accent-rgb:138,111,71}  /* 4.53:1 */
[data-franchise="the-wayhouses"]   {--accent:#5c7355;--accent-rgb:92,115,85}   /* 4.98:1 */
[data-franchise="the-kept-flame"]  {--accent:#b5732a;--accent-rgb:181,115,42}  /* 3.69:1 — non-text only */
.rule{background:var(--accent)}
.world .inner{border-top:3px solid var(--accent)}
.world:hover{border-color:var(--accent)}
/* the franchise hero: the house scrim, with this world's colour warming it */
body[data-franchise] .fhinner{background:
  linear-gradient(to top,rgba(12,10,8,.86) 0%,rgba(12,10,8,.5) 55%,rgba(12,10,8,.3) 100%),
  linear-gradient(to top,rgba(var(--accent-rgb),.55) 0%,rgba(var(--accent-rgb),.18) 100%)}
/* the franchise page's own CTA block */
body[data-franchise] .news{border-top:3px solid var(--accent)}

/* ===================================================================================
   3 · THE CONVERSION PATH — /free, and the classes that were never defined
   =================================================================================== */
/* .tag had NO rule outside `.world .tag`, so the magnet's title — the actual name of the free
   book, on the only live conversion page — rendered as unstyled body copy. */
.tag{font-family:var(--serif);font-style:italic;font-size:18px;color:var(--amberdark);
  margin-bottom:8px}
/* likewise .shelf-line.sub on /authors: the author's SHELF and the franchise's GENRE are
   different tiers (see PEN_SHELF) and were rendering identically, which made the distinction
   the two lines exist to draw invisible. */
.pen-card .shelf-line{margin:2px 0 4px}
.pen-card .shelf-line.sub{font-size:11px;letter-spacing:.04em;text-transform:none;
  color:var(--muted);font-style:italic;margin:0 0 12px}
/* the free-book cards: a cover, because a cover is what a reader recognises as a book */
.free-card{display:grid;grid-template-columns:104px 1fr;gap:20px;align-items:start}
.fc-art{display:block}
.fc-cover{width:104px}
.fc-text .tag{margin-top:2px}
/* where the magnet is not in the catalogue there is no cover to show and none is invented —
   a typographic plate instead. CSS, so it costs no bytes and cannot 404. */
/* the accent band is a BACKGROUND layer, not a border-top. With no side borders a
   border-top curls around the 3px radius and terminates against nothing, leaving a
   small tail at each top corner; a background band is clipped by the radius and
   follows the curve. The radius stays — it matches .cover (rushlight.css:52) and the
   real cover images the plate stands beside. */
.plate{aspect-ratio:5/8;border-radius:3px;color:#f2e7d2;
  background:linear-gradient(var(--accent) 0 3px,transparent 3px) var(--deep);
  display:flex;flex-direction:column;justify-content:space-between;padding:14px 12px;
  box-shadow:0 6px 22px rgba(30,22,10,.16)}
.plate .pt{font-size:15px;line-height:1.15;font-weight:600}
.plate .pb{font-size:7.5px;letter-spacing:.16em;text-transform:uppercase;color:#c9b998}
/* ⛔ SCOPED TO .free-card. Unscoped, this capped EVERY .plate at 104px — including the
   full-width plate in the home page's four-up free-book row, which would have made the one
   world without a catalogue cover a quarter the size of its three peers on a phone. */
@media(max-width:420px){.free-card{grid-template-columns:1fr}
  .free-card .fc-cover,.free-card .plate{max-width:104px}}

/* theme.css — the Kept Flame spoke's third stylesheet layer.
   ================================================================================================
   ⛔ THE CASCADE IS  _brand/mockups/rushlight.css  +  _tools/website/extra.css  +  THIS FILE,
   concatenated in that order, and the order is load-bearing (hub plan §12.2). extra.css holds every
   measured accessibility correction as an override of the base palette; this file comes last, so
   anything it repaints reverts a correction that was measured, not guessed. build_site.py asserts
   the byte order on every build.

   ⛔ WHAT THIS FILE DELIBERATELY DOES NOT DO — 1: repaint a ground.
   The spoke plan §5.2 sets this franchise's palette dial to "lamp/terracotta/dark stone", and §6.4
   permits a spoke to repaint ("on its own domain a franchise IS the house"). That permission is
   declined here, as it was on both sibling spokes, and for the same measured reason: every contrast
   ratio in extra.css is taken against exactly three grounds (--bg #f5efe3, --alt #efe7d6,
   --card #fcfaf4) and annotated with its number. Changing a ground silently invalidates ~20
   measurements at once and NO build check would catch it.

   Declining costs this franchise less than any other, because the dial is already most of the way
   satisfied by the house: --bg #f5efe3 is warm lamp-lit paper, --amberdark #9a5f13 is a flame
   colour, and the franchise accent is a terracotta. "Dark stone" is the only element genuinely
   absent, and it is present where it belongs — the hero scrim over the key art (extra.css line 369,
   rgba(12,10,8,.86)), which is the one surface on the site that is meant to read as a night street
   in Ephesus rather than as a page.

   ⛔ WHAT THIS FILE DELIBERATELY DOES NOT DO — 2: touch the accent.
   `[data-franchise="the-kept-flame"]` in extra.css sets --accent #b5732a and annotates it
   "3.69:1 — non-text only". The temptation on a spoke is to darken it, because here the accent is
   the SITE's colour rather than one card on a shelf. Resisted, because 3.69:1 is not a failure that
   needs fixing: WCAG 1.4.11 asks 3:1 of non-text contrast, and every use of --accent in this
   cascade IS non-text — `.rule` (a 1px bar), `.world .inner` and `body[data-franchise] .news`
   (borders), the hero gradient. Nothing sets text in it. Darkening a value that already passes,
   to satisfy a threshold that does not apply to any of its uses, would be a change with a number
   attached and no reader behind it. ⚑ If a future rule ever sets TEXT in --accent, that rule is
   wrong on this franchise and must darken the colour it uses locally — do not re-open this. */

/* --- the display face: Cinzel, and only where a display face belongs -------------------------
   ⛔ THIS FRANCHISE'S DISPLAY FACE WAS LOCKED IN PRINT BEFORE THE SITE EXISTED — Cinzel, the
   Trajan-esque monumental capital, on the cover banner (`_brand/ref-cover-typography.md` §4;
   `ref-brand-kept-flame.md` §5). rushlight.css already sets the hero wordmark in it
   (`.wm-keptflame`, line 83), which is why the SITES profile loads Cinzel site-wide: without that
   the site would set its own name in the cover face on the home page and in Georgia on the other
   21, because the header brand block renders everywhere and the hero wordmark renders once.

   ⛔ AND NOWHERE ELSE. Cinzel is not a text face — it is capitals, it has no useful lowercase, and
   an entity title like "The Asia Coastal Road — The Seven-City Circuit" set in it becomes a
   monument rather than a heading. So it takes the two places the site says its OWN NAME and stops:
   the header and the footer. Every other heading stays EB Garamond, which is the other half of the
   locked cover pairing anyway (banner Cinzel + title EB Garamond), so the site reproduces the
   book's type system rather than picking one face from it.

   Sized DOWN from the house 23px, not up. Capitals read visually larger than mixed case at equal
   size, and this string is 14 characters; at 23px with tracking it out-measures the nav beside it
   and forces a wrap at the widths where the nav is already tightest. 18px + .1em lands it a few
   pixels narrower than the EB Garamond mixed case it replaces. */
.brand .name{font-family:'Cinzel','Trajan Pro',Georgia,serif;font-size:18px;
  letter-spacing:.1em;text-transform:uppercase}
.fbrand .name{font-family:'Cinzel','Trajan Pro',Georgia,serif;font-size:16px;
  letter-spacing:.1em;text-transform:uppercase}

/* --- the spoke's brand block -----------------------------------------------------------------
   ⛔ THE SAME TWO FIXES BOTH SIBLING SPOKES CARRY, and this is now the THIRD copy of them — see the
   note at the foot of this file. They are spoke CHROME, not this franchise's taste: the hub's
   `.brand .sub` is the word "BOOKS" at .34em tracking, but a spoke's sub-line is a whole sentence
   ("A Rushlight Books series") and at that tracking it runs wider than the name above it and reads
   as an unrelated strapline rather than as attribution. */
.brand .sub{letter-spacing:.1em;font-size:10.5px;color:var(--muted);text-transform:none}
/* `.fcols a{display:block}` (base, line 133) exists for the footer's three LINK COLUMNS and also
   caught the imprint link inside the brand paragraph, which is prose: "Published by / Rushlight
   Books / : clean, character-first fiction…". Restored to inline, where it reads as the sentence it
   is. It stays inline at 18px rather than being padded to the estate's 44px tap floor, because
   WCAG 2.5.8's "inline" exception covers a target inside a block of text — padding it would put a
   44px box inside a 13px paragraph and re-break the sentence this rule exists to fix. */
footer .fbrand p a{display:inline;padding:0;border-bottom:1px solid var(--border)}
footer .fbrand p a:hover{border-bottom-color:var(--amberdark)}

/* --- world index cards -----------------------------------------------------------------------
   The spoke's typed indexes carry 240 characters of `public_md` where the hub's folded section
   carries 130, so a card is a paragraph and not a caption. Matched to the Endurance spoke rather
   than the Wireless War's larger settings: this is the other adult literary site, and the density
   dial for both is "high" (spoke plan §5.2). */
.wgrid .wcard p{font-size:14.5px;line-height:1.62}
.wgrid .wcard h2{font-size:21px;margin-bottom:6px}
/* The spoke's world cards sit under a section <h2>, so their titles are <h3> — one level at a time,
   no skips. extra.css styles `.wcard h4` (the hub's depth) and `.wcard h2` (an index card) but
   never h3, so these would fall through to a size chosen by Chrome rather than by the type scale.
   Matched to `.wcard h4`, which is the same object at the same depth on the hub. */
.wcard h3{font-family:var(--serif);font-size:19px;font-weight:600;line-height:1.25;
  margin-bottom:6px;text-align:left}

/* --- the spoiler floor -----------------------------------------------------------------------
   ⛔ THIS IS THE ONE SPOKE WHERE THIS RULE ACTUALLY RENDERS AT SCALE, and that is why it is styled
   with more care than the Endurance copy it derives from. The Kept Flame world is not flat:
   10 of the 16 shipping entities carry `spoiler_floor: book-1` and one carries `book-2` (which puts
   it above the released floor, so it is suppressed rather than gated). So on most pages of this
   site the reader meets this line before the prose.

   That frequency changes the design problem. A note that appears once is an alert; a note that
   appears on two thirds of pages is FURNITURE, and furniture that shouts becomes noise the reader
   learns to skip — including on the page where it matters. So it is set quiet and structural: a
   left rule in the franchise accent (non-text, which is all that colour may be), muted text, and no
   background panel. It reads as a byline, which is what it is — an honest statement of who the page
   was written for, not a warning. ⛔ Do not give it a tinted panel or an icon.

   ⚑ ITS WORDING IS NOT A CSS PROBLEM AND IS FLAGGED, NOT FIXED: spoiler_note() says "Written for
   readers who have finished Book 1", and on this site Book 1 is `coming-soon`. That is honest about
   the page's depth but odd about the shelf, and rewriting reader-facing copy is Margot's desk with
   a named review step (spoke plan §5.4), not the build's. */
.spoilerfloor{font-size:13px;color:var(--muted);border-left:2px solid var(--accent);
  padding:2px 0 2px 12px;margin:0 0 18px}

/* ⛔ NO TAP-TARGET RULES HERE, DELIBERATELY — extra.css already sets the 44px floor on every control
   this site uses, each annotated with the measurement that produced it. A duplicate here would be a
   second, unmeasured copy of a rule that is already right, and the next person to change the floor
   would change only one of the two.

   ⚑ KNOWN DUPLICATION, RECORDED SO IT IS NOT REDISCOVERED THREE MORE TIMES. Four rules in this file
   — `.brand .sub`, `footer .fbrand p a`(+hover), `.wcard h3` — are byte-identical across all three
   spoke themes, because they fix the SPOKE CHROME rather than express a franchise. Three copies of
   one rule is the same class of defect the whole spoke rebuild exists to remove, one layer down: a
   correction applied to one copy silently leaves the other two wrong. The fix is a shared
   `_tools/website/spoke.css` emitted into the cascade when `SITE["franchise"]` is set, between
   extra.css and this file — which is a change to how the stylesheet is assembled, and belongs on
   its own evidence rather than inside a build that is standing a new site up. The two `.wgrid`
   rules are NOT part of this: they legitimately differ (this site and Endurance are dense and
   literary; the Wireless War is deliberately larger, because it is the children's line). */
