/* ═══════════════════════════════════════════════════════════
   The library — rows of covers over six free-book directories

   The row-of-posters layout is borrowed openly from the streaming
   services, because it is the right shape for the problem: a
   catalogue too big to list, browsed sideways by category, with
   one thing featured at the top.

   Two things it does that a streaming service does not:

   · every tile says whether the text is actually retrievable
     (.tile .tier), because half these directories are catalogues
     that only link out, and a poster wall that hides that is
     a poster wall that wastes your time
   · covers are missing for most records, so .ph paints a
     deterministic one from the title rather than a grey box
   ═══════════════════════════════════════════════════════════ */

/* Browsing runs wider than prose does. Every other page on the site
   is a 1240px column because it is mostly argument; this one is
   mostly posters, and a poster wall in a 1240px column spends two
   tiles' worth of screen on gutters. So the whole page — billboard,
   rows, sections, footer — shares one wider column, and the reading
   matter inside it stays capped in ch where it needs to be. */
body{--max:1680px}

/* ═══════════ masthead ═══════════
   This page is the only one whose header sits over a billboard,
   so it clears the dock and then gets out of the way — the rows
   are what people came for and every pixel above them is a tax. */
.mast{position:relative;overflow:hidden;
  padding:clamp(108px,13vh,152px) 0 clamp(20px,2.6vw,34px)}
.mast .h1{margin:22px 0 0;max-width:16ch}
.mast .lede{margin-top:18px;max-width:68ch}

#browse{padding:clamp(16px,2.2vw,30px) 0 clamp(34px,4.4vw,64px)}
main.wrap>section:not(#browse){padding-block:clamp(52px,6.4vw,104px)}

/* ═══════════ billboard ═══════════ */
.bill{position:relative;overflow:hidden;border-radius:var(--r-2xl);
  border:1px solid var(--bd);margin-top:clamp(20px,3vw,34px);
  min-height:clamp(340px,34vw,540px);display:flex;align-items:flex-end;
  background:linear-gradient(150deg,rgba(124,92,255,.16),rgba(34,211,238,.07) 60%,transparent)}
.bill .art{position:absolute;inset:0;opacity:.5;
  background-size:cover;background-position:center 22%;filter:saturate(.75)}
.bill .scrim{position:absolute;inset:0;
  background:linear-gradient(95deg,rgba(8,8,11,.96) 0%,rgba(8,8,11,.82) 42%,rgba(8,8,11,.25) 100%),
             linear-gradient(0deg,rgba(8,8,11,.9),transparent 55%)}
.bill .in{position:relative;padding:clamp(26px,3.6vw,52px);max-width:64ch}
.bill .k{font:500 9.5px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;
  color:var(--v-lt);display:block;margin-bottom:14px}
.bill h2{font-size:clamp(30px,4.4vw,54px);line-height:1.03;letter-spacing:-.033em;
  font-weight:520;margin:0}
.bill .by{margin-top:10px;font-family:var(--serif);font-size:clamp(15px,1.5vw,19px);
  color:var(--fg-2);font-style:italic}
.bill p{margin-top:16px;font-size:15px;line-height:1.66;color:var(--fg-2);max-width:56ch}
.bill .acts{margin-top:26px;display:flex;gap:11px;flex-wrap:wrap;align-items:center}
.bill .meta{margin-top:18px;display:flex;gap:18px;flex-wrap:wrap;
  font:500 10px/1.6 var(--mono);letter-spacing:.13em;text-transform:uppercase;color:var(--fg-3)}
.bill .meta b{color:var(--fg);font-weight:500}
@media (max-width:700px){.bill .scrim{background:linear-gradient(0deg,rgba(8,8,11,.96) 20%,rgba(8,8,11,.6))}}

/* ═══════════ reach line ═══════════ */
.reach{margin-top:22px;display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.reach .r{padding:18px 20px;border-radius:var(--r);border:1px solid var(--bd);
  background:var(--surf)}
.reach .r b{display:block;font-size:clamp(22px,2.6vw,31px);letter-spacing:-.03em;
  font-weight:520;color:var(--fg);font-variant-numeric:tabular-nums}
.reach .r span{display:block;margin-top:5px;font:500 9.5px/1.6 var(--mono);
  letter-spacing:.14em;text-transform:uppercase;color:var(--fg-3)}
.reach .r em{display:block;margin-top:9px;font-style:normal;font-size:13px;
  line-height:1.55;color:var(--fg-2)}
@media (max-width:820px){.reach{grid-template-columns:1fr}}

/* ═══════════ search ═══════════ */
.searchbar{margin-top:26px;display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.searchbar .fld{flex:1;min-width:260px;position:relative;display:flex;align-items:center}
.searchbar input{width:100%;padding:15px 16px 15px 46px;border-radius:var(--r);
  border:1px solid var(--bd-2);background:var(--bg-2);color:var(--fg);
  font:400 15px/1.4 var(--sans);outline:none;transition:border-color .25s var(--e)}
.searchbar input:focus{border-color:var(--v)}
.searchbar input::placeholder{color:var(--fg-3)}
.searchbar .fld svg{position:absolute;left:16px;width:18px;height:18px;stroke:var(--fg-3);
  fill:none;stroke-width:2;pointer-events:none}
.chips{display:flex;gap:7px;flex-wrap:wrap;margin-top:14px}
.chips button{padding:7px 14px;border-radius:999px;border:1px solid var(--bd);
  background:var(--surf);color:var(--fg-2);font-size:13px;cursor:pointer;
  transition:.22s var(--e)}
.chips button:hover{border-color:var(--bd-3);color:var(--fg)}
.chips button.on{background:var(--v);border-color:var(--v);color:#fff}

/* ═══════════ rows ═══════════
   One knob sets the whole wall. Posters are the content here, so
   they are sized to fill the row rather than to leave room for a
   caption underneath — the title lives on the poster instead. */
#rows{--tile:clamp(152px,16.5vw,252px);--gap:clamp(7px,.62vw,12px)}
/* The strip's own padding is the headroom for the hover lift, so the
   row margin only has to separate one heading from the row above it.
   The padding reset is not decoration: a row is a <section>, and
   core.css gives every section a 128px block padding meant for the
   argument pages. Left in, it puts most of a screen of nothing
   between two shelves and the wall stops reading as a wall. */
.row{margin-top:clamp(2px,.5vw,10px);padding-block:0}
.row:first-child{margin-top:0}
.row .rhd{display:flex;align-items:baseline;gap:14px;margin-bottom:2px;flex-wrap:wrap}
.row .rhd h3{font-size:clamp(17px,1.5vw,22px);letter-spacing:-.028em;font-weight:560;margin:0}
.row .rhd .n{font:500 10px/1 var(--mono);letter-spacing:.13em;text-transform:uppercase;
  color:var(--fg-3)}
.row .rhd .src{margin-left:auto;font:500 9.5px/1 var(--mono);letter-spacing:.13em;
  text-transform:uppercase;color:var(--fg-3)}
.row .rhd .src a{color:var(--fg-3)}
.row .rhd .src a:hover{color:var(--v-lt)}
.rail{position:relative}
/* No scrollbar: the arrows and the half-cut poster at the right
   edge say "there is more" better than a 6px trough does. The
   padding is headroom for the hover lift, not a gutter. */
.strip{display:flex;gap:var(--gap);overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth;
  scroll-snap-type:x proximity;padding:18px 7px;margin-inline:-7px;
  scrollbar-width:none;-ms-overflow-style:none}
.strip::-webkit-scrollbar{display:none}
.strip.wrapped{flex-wrap:wrap;overflow:visible;row-gap:clamp(14px,1.4vw,22px)}
.nav{position:absolute;top:18px;bottom:18px;width:clamp(46px,4.4vw,72px);
  display:grid;place-items:center;border:0;cursor:pointer;z-index:4;
  opacity:0;transition:opacity .25s var(--e);
  background:linear-gradient(90deg,rgba(8,8,11,.93) 56%,rgba(8,8,11,0))}
.nav.r{right:0;transform:scaleX(-1)}
.nav.l{left:0}
/* `hidden` on a button loses to display:grid, so an arrow with
   nowhere to go would still fade in on hover without this. */
.nav[hidden]{display:none}
.rail:hover .nav,.nav:focus-visible{opacity:1}
.nav svg{width:clamp(24px,2.2vw,32px);height:clamp(24px,2.2vw,32px);
  stroke:var(--fg);fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.nav:disabled{opacity:0!important;pointer-events:none}
@media (hover:none){.nav{display:none}}

/* ═══════════ tiles ═══════════ */
.tile{flex:0 0 auto;width:var(--tile);scroll-snap-align:start;cursor:pointer;
  background:none;border:0;padding:0;text-align:left;color:inherit;position:relative;
  transition:transform .3s var(--e),z-index 0s}
.tile:hover,.tile:focus-visible{transform:translateY(-6px) scale(1.045);z-index:3}
.tile .art{position:relative;width:100%;aspect-ratio:2/3;border-radius:var(--r-xs);
  overflow:hidden;border:1px solid var(--bd);background:var(--bg-2);
  transition:border-color .3s var(--e),box-shadow .3s var(--e)}
.tile:hover .art,.tile:focus-visible .art{border-color:var(--bd-3);
  box-shadow:0 16px 34px -18px rgba(0,0,0,.95)}
.tile .art img{width:100%;height:100%;object-fit:cover;display:block}
/* Deterministic stand-in cover: most catalogue records have none.
   Not scoped to .tile — the detail sheet paints the same one. */
.ph{width:100%;height:100%;display:flex;flex-direction:column;justify-content:flex-end;
  padding:clamp(14px,1.2vw,20px) clamp(13px,1.1vw,18px);gap:7px}
.ph b{font-family:var(--serif);font-size:clamp(15px,1.25vw,20px);line-height:1.2;
  font-weight:400;color:rgba(255,255,255,.94);display:-webkit-box;-webkit-line-clamp:5;
  -webkit-box-orient:vertical;overflow:hidden}
.ph i{font-style:normal;font-size:clamp(11px,.85vw,13px);color:rgba(255,255,255,.62);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* A real cover rarely has the title legible at this size, so the
   poster carries its own caption. Tiles that fell back to .ph have
   the title printed on them already and get .nocov instead. */
.tile .cap{position:absolute;inset:auto 0 0 0;pointer-events:none;
  padding:38px clamp(11px,.9vw,15px) clamp(10px,.8vw,14px);
  background:linear-gradient(0deg,rgba(6,6,9,.94) 16%,rgba(6,6,9,.6) 52%,transparent);
  opacity:.94;transition:opacity .3s var(--e)}
.tile:hover .cap,.tile:focus-visible .cap{opacity:1}
.tile.nocov .cap{display:none}
.tile .cap b{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;font-size:clamp(12.5px,1vw,15px);line-height:1.3;font-weight:560;
  letter-spacing:-.014em;color:#fff}
.tile .cap i{display:block;margin-top:3px;font-style:normal;
  font-size:clamp(11px,.8vw,12.5px);color:rgba(255,255,255,.6);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tile .tier{position:absolute;top:9px;left:9px;padding:5px 9px;border-radius:6px;
  font:500 clamp(8.5px,.68vw,10px)/1 var(--mono);letter-spacing:.11em;text-transform:uppercase;
  backdrop-filter:blur(8px);z-index:2}
.tile .tier.read{background:rgba(180,255,57,.9);color:#12180A}
.tile .tier.catalogue{background:rgba(9,9,13,.76);color:var(--fg-2);
  border:1px solid var(--bd-2)}
.tile .tier.here{background:var(--v);color:#fff}
.tile.skel .art{background:linear-gradient(100deg,var(--bg-2),var(--bg-3),var(--bg-2));
  background-size:220% 100%;animation:shim 1.5s linear infinite}
@keyframes shim{to{background-position:-220% 0}}
.rowmsg{padding:26px 20px;border-radius:var(--r);border:1px dashed var(--bd-2);
  font-size:13.5px;line-height:1.6;color:var(--fg-3)}
.rowmsg b{color:var(--fg-2);font-weight:530}

/* ═══════════ detail sheet ═══════════ */
.modal{position:fixed;inset:0;z-index:300;display:none;place-items:center;
  padding:clamp(14px,3vw,40px);background:rgba(4,4,7,.76);backdrop-filter:blur(10px)}
.modal.on{display:grid}
.modal .box{width:min(880px,100%);max-height:88vh;overflow-y:auto;border-radius:var(--r-xl);
  border:1px solid var(--bd-2);background:var(--bg-1);position:relative;
  animation:pop .34s var(--e)}
@keyframes pop{from{opacity:0;transform:translateY(14px) scale(.985)}}
.modal .top{position:relative;padding:clamp(24px,3vw,38px);display:flex;gap:clamp(18px,2.4vw,30px);
  flex-wrap:wrap;border-bottom:1px solid var(--bd)}
.modal .top .art{flex:0 0 auto;width:158px;height:237px;border-radius:var(--r-xs);
  overflow:hidden;border:1px solid var(--bd);background:var(--bg-2)}
.modal .top .art img{width:100%;height:100%;object-fit:cover;display:block}
.modal .who{flex:1;min-width:250px}
.modal .who h3{font-size:clamp(22px,2.6vw,30px);line-height:1.12;letter-spacing:-.028em;
  font-weight:520;margin:0}
.modal .who .by{margin-top:8px;font-family:var(--serif);font-style:italic;font-size:16px;
  color:var(--fg-2)}
.modal .who .tags{margin-top:14px;display:flex;gap:8px;flex-wrap:wrap}
.modal .who .tags span{padding:5px 11px;border-radius:999px;border:1px solid var(--bd);
  background:var(--surf);font:500 9.5px/1.5 var(--mono);letter-spacing:.11em;
  text-transform:uppercase;color:var(--fg-3)}
.modal .who .tags span.read{border-color:rgba(180,255,57,.36);color:var(--lime)}
.modal .body{padding:clamp(24px,3vw,38px)}
.modal .body h4{font-size:12px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  font-family:var(--mono);color:var(--fg-3);margin:0 0 14px}
.modal .body>p{font-size:14.5px;line-height:1.66;color:var(--fg-2);margin:0 0 22px}
.modal .x{position:absolute;top:14px;right:14px;width:36px;height:36px;border-radius:50%;
  display:grid;place-items:center;border:1px solid var(--bd);background:rgba(9,9,13,.7);
  cursor:pointer;z-index:2}
.modal .x svg{width:15px;height:15px;stroke:var(--fg-2);fill:none;stroke-width:2.2;
  stroke-linecap:round}
.modal .x:hover{border-color:var(--bd-3)}
.modal .x:hover svg{stroke:var(--fg)}

/* preset grid inside the sheet */
.pgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.pset{text-align:left;padding:15px 15px 17px;border-radius:var(--r);cursor:pointer;
  border:1px solid var(--bd);background:var(--surf);color:inherit;
  transition:.24s var(--e);display:flex;flex-direction:column;gap:5px}
.pset:hover{border-color:var(--v);background:rgba(124,92,255,.08)}
.pset .n{font-size:14.5px;font-weight:530;letter-spacing:-.014em}
.pset .k{font:500 9px/1.4 var(--mono);letter-spacing:.12em;text-transform:uppercase;
  color:var(--v-lt)}
.pset .d{font-size:12.5px;line-height:1.5;color:var(--fg-3);margin-top:3px}
.pset .w{margin-top:8px;font:500 10px/1 var(--mono);letter-spacing:.1em;color:var(--fg-2)}
@media (max-width:760px){.pgrid{grid-template-columns:1fr}}
/* On the page the six presets get a wider column than they do inside
   the sheet, so they lay out to fit rather than to a fixed three. */
#pgrid{grid-template-columns:repeat(auto-fit,minmax(252px,1fr))}
@media (max-width:760px){#pgrid{grid-template-columns:1fr}}

.sheetacts{margin-top:24px;padding-top:22px;border-top:1px solid var(--bd);
  display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.stat{margin-top:18px;padding:14px 16px;border-radius:var(--r);font-size:13.5px;
  line-height:1.6;border:1px solid var(--bd);background:var(--surf);color:var(--fg-2)}
.stat b{color:var(--fg);font-weight:530}
.stat.bad{border-color:rgba(251,113,133,.34);background:rgba(251,113,133,.07)}
.stat.good{border-color:rgba(180,255,57,.3);background:rgba(180,255,57,.06)}
.stat a{color:var(--v-lt)}
.spin{display:inline-block;width:13px;height:13px;border-radius:50%;vertical-align:-1px;
  border:2px solid var(--bd-3);border-top-color:var(--v-lt);margin-right:8px;
  animation:sp .7s linear infinite}
@keyframes sp{to{transform:rotate(360deg)}}

/* ═══════════ what we won't claim ═══════════ */
.honest{border-radius:var(--r-xl);border:1px solid var(--bd);padding:clamp(26px,3vw,42px);
  background:linear-gradient(160deg,rgba(255,176,32,.07),rgba(255,255,255,.012))}
.honest .h4{margin-bottom:14px;color:#FFCE6E}
.honest p{font-size:14.5px;line-height:1.7;color:var(--fg-2);max-width:78ch}
.honest p + p{margin-top:14px}
.honest strong{color:var(--fg);font-weight:530}
.honest code{font:500 12.5px/1 var(--mono);padding:2px 6px;border-radius:5px;
  background:var(--surf);border:1px solid var(--bd);color:var(--fg-2)}

/* ═══════════ the directories ═══════════ */
.dirs{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.dir{padding:22px 20px 24px;border-radius:var(--r-lg);border:1px solid var(--bd);
  background:var(--surf)}
.dir .hd{display:flex;align-items:center;gap:10px}
.dir h4{margin:0;font-size:16px;font-weight:530;letter-spacing:-.02em}
.dir .st{width:8px;height:8px;border-radius:50%;background:var(--fg-4);flex-shrink:0;
  margin-left:auto}
.dir .st.ok{background:var(--lime)}
.dir .st.no{background:var(--rose)}
.dir p{margin:11px 0 0;font-size:13.5px;line-height:1.6;color:var(--fg-2)}
.dir .fq{margin-top:14px;padding-top:13px;border-top:1px solid var(--bd);
  display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;
  font:500 9.5px/1.5 var(--mono);letter-spacing:.12em;text-transform:uppercase;
  color:var(--fg-3)}
.dir .fq b{color:var(--fg);font-weight:500}
@media (max-width:1000px){.dirs{grid-template-columns:repeat(2,1fr)}}
@media (max-width:680px){.dirs{grid-template-columns:1fr}}
/* seven directories in a wider column: 4+3 beats 3+3+1 */
@media (min-width:1320px){.dirs{grid-template-columns:repeat(4,1fr)}}

@media (prefers-reduced-motion:reduce){
  .tile,.tile:hover,.modal .box{transition:none;animation:none;transform:none}
  .strip{scroll-behavior:auto}
  .tile.skel .art{animation:none}
}

/* ═══════════ embedded in the app ═══════════
   trainer.html frames this page as its Library tab. The app already
   supplies a header, a footer and the argument for why any of this
   exists, so the embedded copy drops all three and keeps only the
   part that is actually a library: the billboard, the search, and
   the rows. It also drops the 1680px column — inside the app there
   is no prose to keep at a readable measure, so the poster wall
   runs the full width of the window the way a television does. */
html.embed,html.embed body{background:var(--bg)}
html.embed body{--max:none;--pad:clamp(14px,1.5vw,30px)}

/* website furniture the app provides itself */
html.embed .dock,
html.embed .sheet,
html.embed #prog,
html.embed footer{display:none}

/* the essay around the rows: the app's Docs tab is where that lives */
html.embed .mast .tag,
html.embed .mast .h1,
html.embed .mast .lede,
html.embed .reach,
html.embed #presets,
html.embed #tiers,
html.embed #sources,
html.embed #honest,
html.embed #cta{display:none}

/* no fixed dock to clear, so the billboard starts at the top edge */
html.embed .mast{padding:clamp(12px,1.4vw,22px) 0 0}
html.embed .bill{margin-top:0;min-height:clamp(300px,26vw,460px)}
html.embed #browse{padding:clamp(16px,1.8vw,26px) 0 clamp(30px,3.4vw,56px)}

/* A window twice as wide as a phone should show twice the shelf, not
   the same shelf twice the size — so the tile only grows a little and
   the row grows instead. */
html.embed #rows{--tile:clamp(146px,11.5vw,236px)}
