/* ==========================================================================
   THE KNOWLEDGE — bookstore stylesheet
   Light, editorial, fast. No framework.

   Typefaces
     Logo       Cinzel 700 — Roman inscriptional capitals, the letters carved
                over library doors.
     Headlines  Playfair Display 600, roman and italic — a high-contrast
                book-face that reads as literary without turning fussy.
     Everything else stays on fonts already in the device: Georgia for small
     serif text, the system UI face for body copy. Only three files are
     downloaded, Latin-only, 60 KB together, from this site's own server.

   Cinzel © The Cinzel Project Authors. Playfair Display © The Playfair Project
   Authors. Both under the SIL Open Font License 1.1 — free to use, embed and
   serve, including commercially.
   ========================================================================== */

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

/* Stand-ins sized to match, so the moment the real font arrives the text does
   not jump. Without these a swap re-wraps the headline and the page shifts —
   exactly the kind of small jolt that makes a site feel cheap. */
@font-face {
  font-family: 'Cinzel Fallback';
  src: local('Georgia'), local('Times New Roman');
  size-adjust: 104%;
  ascent-override: 88%;
}
@font-face {
  font-family: 'Playfair Fallback';
  src: local('Georgia'), local('Times New Roman');
  size-adjust: 108%;
  ascent-override: 98%;
}

:root {
  /* Brand */
  --accent:        #e8534d;
  --accent-dark:   #d13f39;
  --accent-soft:   #fdeceb;
  --accent-line:   rgba(232, 83, 77, 0.28);

  /* Surfaces */
  --bg:            #ffffff;
  --bg-alt:        #f7f7f8;
  --bg-sunken:     #f0f0f2;
  --card:          #ffffff;

  /* Ink */
  --ink:           #1a1a1c;
  --ink-2:         #5c5c66;
  --ink-3:         #8b8b96;
  --line:          #e6e6ea;
  --line-strong:   #d4d4dc;

  --ok:            #1f8a52;
  --warn:          #b4690e;

  /* Type */
  --serif:   Georgia, 'Iowan Old Style', 'Palatino Linotype', 'Times New Roman', serif;
  --sans:    system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --display: 'Playfair Display', 'Playfair Fallback', Georgia, serif;
  --mark:    'Cinzel', 'Cinzel Fallback', Georgia, serif;

  /* Space */
  --wrap:   1180px;
  --gutter: 20px;
  --tap:    44px;

  --radius:    4px;
  --radius-lg: 8px;

  --shadow-sm: 0 1px 3px rgba(16, 16, 24, 0.06);
  --shadow:    0 6px 22px rgba(16, 16, 24, 0.09);
  --shadow-lg: 0 18px 48px rgba(16, 16, 24, 0.14);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);

  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
}

/* ------------------------------------------------- page transitions -- */

/* Cross-document view transitions.

   Without them every tap tears the page down to white and paints the next one
   from nothing — that blank frame is the "flash" people feel, and it has
   nothing to do with speed. With them the browser holds the old page on screen
   until the new one is ready, then hands over.

   The hand-over itself: the old page settles back and fades; the new one rises
   a few pixels into place as it appears. Two short, overlapping motions read
   as one continuous surface rather than a cut. The timings are deliberately
   unequal — leaving should be quick, arriving should be unhurried.

   Chrome, Edge and Safari 18.2+ do this. Firefox does not support it yet and
   simply navigates as before; nothing breaks. */
@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: tk-leave 0.22s cubic-bezier(0.4, 0, 1, 1) both;
}
::view-transition-new(root) {
  animation: tk-arrive 0.42s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes tk-leave {
  to { opacity: 0; transform: scale(0.992); }
}
@keyframes tk-arrive {
  from { opacity: 0; transform: translateY(10px); }
}

/* The header morphs rather than fades — it stays put while the page changes
   beneath it, which is most of what makes this feel like one app. */
::view-transition-group(site-header) { animation-duration: 0.42s; }
::view-transition-old(site-header),
::view-transition-new(site-header) { animation: none; mix-blend-mode: normal; }

/* The header is identical on every page, so morph it instead of fading it —
   it stays visually anchored while the body changes underneath. */
.site-header { view-transition-name: site-header; }

/* A tapped book cover grows into the detail page's big cover rather than the
   two pages simply cross-fading — the jacket you touched travels with you.

   Only ONE element may carry a given name per page, so the grid side is left
   unnamed and site.js assigns `--vt: bookcover` to the single card being opened
   at the moment of the click. With JavaScript off nothing is ever named, the
   morph is skipped, and the plain cross-fade above still runs. */
.cover-link  { view-transition-name: var(--vt, none); }
.detail-cover { view-transition-name: bookcover; }

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

/* --------------------------------------------------------------- reset -- */

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

html {
  overflow-x: clip;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 90px;
  /* The colour shown in the instant before a page paints. Left unset it is
     the browser's white — and the flash of it between two pages is visible
     even when everything else is quick. */
  background: var(--bg);
}

body {
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--bg);
  overflow-x: clip;
  -webkit-font-smoothing: antialiased;
  padding-left: var(--safe-l);
  padding-right: var(--safe-r);
}

img, svg, video { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: none; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }

h1, h2, h3, h4 { font-family: var(--serif); font-weight: 700; line-height: 1.2; letter-spacing: -0.01em; }

/* Page-level headings get the book-face. Small headings (card titles, h3)
   stay on Georgia: Playfair's thin hairlines are beautiful at 40px and spidery
   at 15px, and Georgia was drawn for exactly those small screen sizes. */
h1, h2 { font-family: var(--display); font-weight: 600; letter-spacing: -0.005em; }

:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius);
}

::selection { background: var(--accent); color: #fff; }

.sr-only {
  position: absolute !important;
  width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: 16px; top: -80px; z-index: 300;
  background: var(--accent); color: #fff;
  padding: 10px 18px; border-radius: var(--radius); font-weight: 600;
  transition: top 0.2s var(--ease);
}
.skip-link:focus { top: 12px; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--gutter); }

/* -------------------------------------------------------------- header -- */

.announce {
  background: var(--ink);
  color: #fff;
  text-align: center;
  font-size: 0.82rem;
  letter-spacing: 0.03em;
  padding: 8px var(--gutter);
}
.announce a { color: #fff; text-decoration: underline; }

.site-header {
  position: sticky; top: 0; z-index: 100;
  background: rgba(255, 255, 255, 0.94);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--line);
}
@supports not (backdrop-filter: blur(1px)) { .site-header { background: #fff; } }

.header-bar {
  max-width: var(--wrap); margin: 0 auto;
  padding: 0 var(--gutter);
  min-height: 74px;
  display: flex; align-items: center; gap: 24px;
}

.brand {
  font-family: var(--mark);
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  white-space: nowrap;
  display: flex; align-items: center; gap: 10px;
  /* The text is only ~29px tall, which is under the size a thumb reliably
     hits. The padding makes the target a full 44px without moving the
     letters, because the header centres it either way. */
  min-height: var(--tap);
}
.brand img { width: 34px; height: 34px; border-radius: var(--radius); }

.main-nav { margin-left: auto; }
.main-nav ul { display: flex; align-items: center; gap: 30px; }

.main-nav a {
  font-size: 0.83rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-2);
  /* Tall enough to hit, and the flex centring keeps the row looking the same */
  display: flex; align-items: center;
  min-height: 40px;
  padding: 4px 0;
  position: relative;
  transition: color 0.18s var(--ease);
}
.main-nav a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 8px;
  height: 2px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.22s var(--ease);
}
.main-nav a:hover { color: var(--ink); }
.main-nav a:hover::after { transform: scaleX(1); }
.main-nav a.current { color: var(--accent); }
.main-nav a.current::after { transform: scaleX(1); }

.nav-toggle {
  display: none;
  width: var(--tap); height: var(--tap);
  margin-left: auto;
  align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.nav-toggle span {
  display: block; width: 18px; height: 2px; background: var(--ink);
  position: relative; border-radius: 2px;
}
.nav-toggle span::before,
.nav-toggle span::after {
  content: ''; position: absolute; left: 0; width: 18px; height: 2px;
  background: var(--ink); border-radius: 2px;
}
.nav-toggle span::before { top: -6px; }
.nav-toggle span::after  { top: 6px; }

/* ---------------------------------------------------------------- hero -- */

.hero {
  background:
    linear-gradient(105deg, #e7ece4 0%, #eceaf1 46%, #efe7ea 100%);
  overflow: hidden;
}

.hero-inner {
  max-width: var(--wrap); margin: 0 auto;
  padding: 0 var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: center;
  gap: 40px;
  min-height: 460px;
}

.hero-eyebrow {
  color: var(--accent);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-bottom: 16px;
}

/* The one line on the site that should feel written rather than typeset.
   Italic book-face, a touch larger, and ink with the faintest warmth — a
   near-black that is kinder to read than pure #000 at this size. */
.hero h1 {
  font-family: var(--display);
  font-style: italic;
  font-weight: 600;
  font-size: clamp(2.1rem, 5vw, 3.35rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: #1f1b24;
  margin-bottom: 20px;
  white-space: pre-line;
  text-wrap: balance;
}

.hero p {
  color: var(--ink-2);
  font-size: 1.02rem;
  max-width: 44ch;
  margin-bottom: 28px;
}

.hero-media { position: relative; }
.hero-media img {
  width: 100%; height: 100%;
  max-height: 460px;
  object-fit: cover;
  object-position: center;
}

/* Placeholder when no hero image is set */
.hero-media .hero-fallback {
  display: grid; place-items: center;
  min-height: 300px;
  font-family: var(--serif);
  font-size: 1rem;
  color: var(--ink-3);
}

/* ------------------------------------------------------------- buttons -- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--tap);
  padding: 12px 26px;
  border-radius: var(--radius);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-align: center;
  transition: background 0.18s var(--ease), color 0.18s var(--ease),
              border-color 0.18s var(--ease), transform 0.12s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent-dark); color: #fff; }

.btn-outline {
  background: transparent; color: var(--ink);
  border: 1px solid var(--line-strong);
}
.btn-outline:hover { border-color: var(--ink); }

.btn-ghost { background: var(--bg-alt); color: var(--ink); }
.btn-ghost:hover { background: var(--bg-sunken); }

.btn-wa { background: #25d366; color: #fff; }
.btn-wa:hover { background: #1db954; color: #fff; }

/* -------------------------------------------------------------- filter -- */

.filter-bar { background: #fff; border-bottom: 1px solid var(--line); }

.filter-inner {
  max-width: var(--wrap); margin: 0 auto;
  padding: 24px var(--gutter);
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 14px;
}

.field {
  position: relative;
  display: flex; align-items: center;
}
.field select,
.field input {
  width: 100%;
  min-height: var(--tap);
  padding: 11px 38px 11px 16px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink);
  font-size: 0.92rem;
  appearance: none;
  -webkit-appearance: none;
}
.field input { padding-right: 16px; }
.field select:focus, .field input:focus { border-color: var(--accent); outline: none; }

.field::after {
  content: '';
  position: absolute; right: 16px; top: 50%;
  width: 8px; height: 8px;
  border-right: 2px solid var(--ink-3);
  border-bottom: 2px solid var(--ink-3);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}
.field.no-caret::after { display: none; }

/* -------------------------------------------------------------- layout -- */

.section { padding: 64px 0; }
.section-alt { background: var(--bg-alt); }

.section-head { text-align: center; margin-bottom: 40px; }

.section-head h2 {
  font-size: clamp(1.5rem, 3.2vw, 2rem);
  margin-bottom: 12px;
}
.section-head h2::after {
  content: ''; display: block;
  width: 56px; height: 2px; margin: 12px auto 0;
  background: var(--accent);
}
.section-head p { color: var(--ink-2); max-width: 56ch; margin: 0 auto; }

/* --------------------------------------------------------- book cards -- */

.book-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(214px, 1fr));
  gap: 22px;
  /* Each card is exactly as tall as it needs to be. Stretching them to match
     the tallest title in the row would open a dead band between a short title
     and its price — and the covers, which are what the eye actually lines up
     on, are a fixed 2:3 and stay level either way. */
  align-items: start;
}

/* The card is a single object: cover, title, price. `isolation` gives it its
   own stacking context so the hover sheen and the stretched link never fight
   with anything outside the card. */
.book-card {
  position: relative;
  isolation: isolate;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 12px;
  overflow: hidden;
  display: flex; flex-direction: column;
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease),
              border-color 0.3s var(--ease);
}

/* A hairline of brand colour draws itself across the top edge on hover.
   It reads as the card "waking up" without moving any text. */
.book-card::before {
  content: '';
  position: absolute; inset: 0 0 auto 0; height: 3px; z-index: 4;
  background: linear-gradient(90deg, var(--accent), #f39a72);
  transform: scaleX(0); transform-origin: left center;
  transition: transform 0.4s var(--ease);
}

/* Everything below is hover-only. Guarded, because a touchscreen has no hover:
   without this a tapped card keeps its lifted "hovered" look until you tap
   something else, which looks broken rather than lively. */
@media (hover: hover) and (pointer: fine) {
  .book-card:hover {
    transform: translateY(-6px);
    box-shadow: 0 16px 34px rgba(16, 16, 24, 0.13);
    border-color: transparent;
  }
  .book-card:hover::before { transform: scaleX(1); }
  .book-card:hover .cover img { transform: scale(1.06); }
  .book-card:hover .cover::after { transform: translateX(120%); }
  .book-card:hover .book-title a { color: var(--accent); }

  /* A bundle opens out a little, like picking up the set */
  .book-card:hover .cover-stack .cs:nth-child(1) { transform: translate(-50%, -50%) translateX(-100%) rotate(-13deg); }
  .book-card:hover .cover-stack .cs:nth-child(2) { transform: translate(-50%, -50%) translateX(-33%) rotate(-4.5deg); }
  .book-card:hover .cover-stack .cs:nth-child(3) { transform: translate(-50%, -50%) translateX(33%)  rotate(4.5deg); }
  .book-card:hover .cover-stack .cs:nth-child(4) { transform: translate(-50%, -50%) translateX(100%) rotate(13deg); }
}

/* Finger down = the card gives a little. On a phone this is the whole feel of
   the thing: it answers before the next page has even started loading. */
.book-card:active {
  transform: scale(0.978);
  transition-duration: 0.09s;
}

.cover-link { display: block; position: relative; background: var(--bg-sunken); }

/* Fixed aspect box: the image slot holds its size before the picture arrives,
   so the grid never jumps as covers load. */
.cover {
  display: block;            /* a <span> is inline, and inline boxes ignore aspect-ratio */
  position: relative;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  background: var(--bg-sunken);
}
.cover img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.55s var(--ease);
}

/* A slow band of light crosses the jacket on hover — the way a real cover
   catches the light when you tilt it. One transform, no repaint. */
.cover::after {
  content: '';
  position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(105deg,
              rgba(255,255,255,0) 38%,
              rgba(255,255,255,0.26) 50%,
              rgba(255,255,255,0) 62%);
  transform: translateX(-120%);
  transition: transform 0.75s var(--ease);
  pointer-events: none;
}

/* Cover placeholder — a typeset jacket, so a book waiting for its artwork
   still looks like a book and not like a missing file. */
.cover-ph {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; justify-content: center;
  padding: 26px 20px;
  text-align: center;
  color: #fff;
  background:
    radial-gradient(120% 90% at 22% 8%, rgba(255,255,255,0.16), transparent 58%),
    linear-gradient(160deg, #23263a 0%, #343a55 52%, #22253a 100%);
}
/* The blind-stamped border a hardback has pressed into its boards */
.cover-ph::before {
  content: '';
  position: absolute; inset: 11px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 2px;
}
.cover-ph b {
  font-family: var(--serif);
  font-size: 1.04rem; line-height: 1.28;
  display: block;
  text-wrap: balance;
}
/* A short rule under the title, the way a title page sets it */
.cover-ph b::after {
  content: '';
  display: block; width: 30px; height: 2px;
  margin: 12px auto 0;
  background: var(--accent);
}
.cover-ph i {
  position: absolute; left: 0; right: 0; bottom: 24px;
  font-style: normal;
  font-size: 0.56rem; letter-spacing: 0.2em; text-transform: uppercase;
  opacity: 0.6;
}

/* ---- bundles: several books sold as one ---- */

/* Double width, so a set reads as bigger than the single titles beside it. */
.book-wide { grid-column: span 2; }
/* The cover has to get wider too, or a two-column card becomes a tower. */
.book-wide .cover { aspect-ratio: 3 / 2; }

/* The books fanned out like a set stood on a shelf. Each tile keeps a book's
   2:3 shape and is sized as a share of the card, so the same rule works in a
   narrow tile and a wide one. */
.cover-stack {
  position: absolute; inset: 0; display: block;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(255,255,255,0.12), transparent 60%),
    linear-gradient(160deg, #1d2030 0%, #2e3350 55%, #1b1e2c 100%);
}
.cover-stack .cs {
  position: absolute; top: 50%; left: 50%;
  width: 30%; aspect-ratio: 2 / 3;
  border-radius: 3px; overflow: hidden;
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.45);
  transition: transform 0.45s var(--ease);
}
.cover-stack .cs > img { width: 100%; height: 100%; object-fit: cover; }

/* Books in a fan overlap, so each one shows only a strip of itself. A title
   centred in that strip gets sliced in half and reads as a mistake — so the
   stacked jackets carry no lettering, just the blind-stamped board. The set's
   own name is under the card and every title is listed on its page. A real
   uploaded cover, of course, shows its own artwork. */
.cover-stack .cs .cover-ph { padding: 0; }
.cover-stack .cs .cover-ph::before { inset: 5px; }
.cover-stack .cs .cover-ph b { display: none; }

/* Percentages in a transform are of the element's own width, so the fan keeps
   its proportions at every card size. */
.cover-stack .cs:nth-child(1) { transform: translate(-50%, -50%) translateX(-88%) rotate(-10deg); z-index: 1; }
.cover-stack .cs:nth-child(2) { transform: translate(-50%, -50%) translateX(-29%) rotate(-3.5deg); z-index: 2; }
.cover-stack .cs:nth-child(3) { transform: translate(-50%, -50%) translateX(29%)  rotate(3.5deg);  z-index: 3; }
.cover-stack .cs:nth-child(4) { transform: translate(-50%, -50%) translateX(88%)  rotate(10deg);   z-index: 4; }

/* One, two or three books: re-centre the fan instead of leaving it lopsided */
.cover-stack .cs:only-child { transform: translate(-50%, -50%) rotate(-2deg); width: 38%; }
.cover-stack .cs:first-child:nth-last-child(2)       { transform: translate(-50%, -50%) translateX(-48%) rotate(-6deg); }
.cover-stack .cs:first-child:nth-last-child(2) ~ .cs { transform: translate(-50%, -50%) translateX(48%)  rotate(6deg); }
.cover-stack .cs:first-child:nth-last-child(3)             { transform: translate(-50%, -50%) translateX(-66%) rotate(-8deg); }
.cover-stack .cs:first-child:nth-last-child(3) ~ .cs       { transform: translate(-50%, -50%) rotate(0deg); }
.cover-stack .cs:first-child:nth-last-child(3) ~ .cs ~ .cs { transform: translate(-50%, -50%) translateX(66%) rotate(8deg); }

.badge {
  position: absolute; top: 10px; right: 10px; z-index: 3;
  background: rgba(26, 26, 28, 0.9);
  color: #fff;
  font-size: 0.63rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  padding: 5px 10px; border-radius: 100px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
}
.badge-sale { background: var(--accent); }

/* The bundle count sits on the left so it never collides with a sale badge */
.badge-set {
  right: auto; left: 10px;
  background: #fff; color: var(--ink);
}

.book-body {
  padding: 13px 13px 15px;
  text-align: center;
  display: flex; flex-direction: column; gap: 3px;
}

.book-title {
  font-family: var(--serif);
  font-size: 1rem;
  line-height: 1.32;
  /* Two lines at most. A title long enough to run to three would push the
     price further from it on that one card and break the rhythm of the row. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.book-title a { transition: color 0.2s var(--ease); }

/* The stretched link: one anchor, whole card clickable. */
.book-title a::after {
  content: '';
  position: absolute; inset: 0; z-index: 5;
}

.price {
  font-size: 1.06rem;
  font-weight: 700;
  color: var(--ink);
  display: flex; align-items: baseline; justify-content: center; gap: 7px;
}
.price .now { color: var(--accent); }
.price del { color: var(--ink-3); font-weight: 400; font-size: 0.85rem; }

/* ------------------------------------------------------ category shelf -- */

.cat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 14px;
  /* Small tiles keep their own height instead of stretching to match the big
     one, which would leave them mostly empty. */
  align-items: start;
}

.cat-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 22px 20px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease),
              transform 0.18s var(--ease);
  position: relative;
  overflow: hidden;
}

/* Accent edge that fills in on hover — a small cue that this is a doorway */
.cat-card::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 0.22s var(--ease);
}

.cat-card:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow);
  transform: translateY(-2px);
}
.cat-card:hover::before { transform: scaleY(1); }

.cat-meta { display: flex; flex-direction: column; gap: 4px; }

.cat-name {
  font-family: var(--serif);
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--ink);
}

.cat-count {
  font-size: 0.82rem;
  color: var(--ink-3);
}

.cat-blurb {
  font-size: 0.87rem;
  line-height: 1.55;
  color: var(--ink-2);
  margin-top: 4px;
}

/* ---- featured category: double width, with artwork ---- */

.cat-big {
  grid-column: span 2;
  padding: 0;
  gap: 0;
}
.cat-big .cat-meta { padding: 18px 20px 20px; }
.cat-big .cat-name { font-size: 1.3rem; }

.cat-art {
  display: block;
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--bg-sunken);
}
.cat-art > img { width: 100%; height: 100%; object-fit: cover; }

/* Four covers, two by two. No image is generated — the existing cover files
   are simply laid out in a grid and cropped by the tile. */
.cat-collage {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: 2px;
  background: var(--line);
}
.cat-collage .cc {
  position: relative;
  overflow: hidden;
  background: #2d3142;
}
.cat-collage .cc img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Placeholder tile for a book with no cover yet */
.cat-collage .cc-ph {
  display: grid;
  place-items: center;
  padding: 8px;
  background: linear-gradient(150deg, #2d3142, #454a63);
}
.cat-collage .cc-ph b {
  font-family: var(--serif);
  font-size: 0.72rem;
  line-height: 1.25;
  color: #fff;
  text-align: center;
  opacity: 0.9;
}

/* A category with fewer than four books still fills the tile neatly */
.cat-collage .cc:only-child { grid-column: span 2; grid-row: span 2; }
.cat-collage .cc:first-child:nth-last-child(2),
.cat-collage .cc:first-child:nth-last-child(2) ~ .cc { grid-row: span 2; }
.cat-collage .cc:first-child:nth-last-child(3) { grid-row: span 2; }

.cat-big:hover .cat-art > img { transform: scale(1.03); }
.cat-art > img { transition: transform 0.4s var(--ease); }

.detail-cat a { color: inherit; display: inline-block; padding: 7px 0; }
.detail-cat a:hover { text-decoration: underline; text-underline-offset: 3px; }

@media (max-width: 760px) {
  .cat-grid { grid-template-columns: repeat(2, 1fr); gap: 11px; }
  .cat-card { padding: 16px 14px; }
  .cat-name { font-size: 0.98rem; }

  /* The featured tile keeps its prominence by going full width */
  .cat-big { grid-column: 1 / -1; padding: 0; }
  .cat-big .cat-meta { padding: 14px 16px 16px; }
  .cat-big .cat-name { font-size: 1.12rem; }
  .cat-art { aspect-ratio: 2 / 1; }
}

@media (max-width: 400px) {
  .cat-grid { grid-template-columns: 1fr; }
  .cat-big { grid-column: 1 / -1; }
}

/* --------------------------------------------------------- book detail -- */

.book-detail {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: 48px;
  align-items: start;
}

.detail-cover {
  position: sticky; top: 100px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow);
  aspect-ratio: 2 / 3;
  background: var(--bg-sunken);
}
.detail-cover img { width: 100%; height: 100%; object-fit: cover; }

.detail-cover.cover-set { aspect-ratio: 4 / 3; }

/* Sample pages from inside the book. A row that scrolls sideways on a phone
   keeps them big enough to actually read, which a shrunken grid would not. */
.previews {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
  margin-top: 16px;
}
.previews a {
  display: block;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  background: var(--bg-sunken);
  aspect-ratio: 5 / 7;
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.previews img { width: 100%; height: 100%; object-fit: cover; }

@media (hover: hover) and (pointer: fine) {
  .previews a:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
}

@media (max-width: 560px) {
  .previews {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    -webkit-overflow-scrolling: touch;
  }
  .previews a { flex: 0 0 62%; scroll-snap-align: start; }
}

.detail-h2 {
  font-size: 1.15rem;
  margin: 30px 0 4px;
  padding-top: 22px;
  border-top: 1px solid var(--line);
}

/* The typeset jacket again, at the size a book page deserves */
.detail-cover .cover-ph { padding: 44px 32px; }
.detail-cover .cover-ph::before { inset: 16px; }
.detail-cover .cover-ph b { font-size: 1.75rem; }
.detail-cover .cover-ph b::after { width: 44px; margin-top: 18px; }
.detail-cover .cover-ph i { bottom: 34px; font-size: 0.68rem; }

.detail-cat {
  font-size: 0.72rem; font-weight: 700;
  letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 4px;   /* the link's own padding adds the rest */
}

.book-detail h1 { font-size: clamp(1.6rem, 3.6vw, 2.35rem); margin-bottom: 8px; }
.detail-sub { font-size: 1.05rem; color: var(--ink-2); margin-bottom: 18px; }

.detail-price {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 18px 0; margin: 18px 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.detail-price .now { font-size: 1.85rem; font-weight: 700; color: var(--accent); font-family: var(--serif); }
.detail-price del { color: var(--ink-3); font-size: 1.05rem; }
.detail-price .off {
  background: var(--accent-soft); color: var(--accent-dark);
  font-size: 0.76rem; font-weight: 700;
  padding: 3px 10px; border-radius: 20px;
}

.buy-row { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 22px; }
.buy-row .btn { flex: 1 1 200px; }

.spec-list {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 14px 22px;
  padding: 20px; margin-bottom: 26px;
  background: var(--bg-alt);
  border-radius: var(--radius-lg);
}
.spec-list div { font-size: 0.86rem; }
.spec-list dt { color: var(--ink-3); font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase; }
.spec-list dd { font-weight: 600; margin-top: 2px; }

.prose { color: var(--ink-2); }
.prose p { margin-bottom: 15px; }
.prose p:last-child { margin-bottom: 0; }
.prose h2, .prose h3 { color: var(--ink); margin: 26px 0 10px; font-size: 1.12rem; }
.prose strong { color: var(--ink); }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }

.checks { margin: 22px 0; display: grid; gap: 10px; }
.checks li {
  position: relative; padding-left: 28px;
  font-size: 0.93rem; color: var(--ink-2);
}
.checks li::before {
  content: ''; position: absolute; left: 4px; top: 8px;
  width: 11px; height: 6px;
  border-left: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
  transform: rotate(-45deg);
}

.crumbs {
  font-size: 0.8rem; color: var(--ink-3);
  padding: 12px 0 0;
}
/* Breadcrumb text is 17px tall — too small to hit on a phone. The padding
   grows the target to 33px; the row above was trimmed by the same amount so
   nothing below it moves. */
.crumbs a { display: inline-block; padding: 8px 0; }
.crumbs a:hover { color: var(--accent); }
.crumbs span { margin: 0 7px; }

/* ------------------------------------------------------- simple pages -- */

.page-head { padding: 52px 0 10px; text-align: center; }
.page-head h1 { font-size: clamp(1.7rem, 4vw, 2.5rem); margin-bottom: 10px; }
.page-head p { color: var(--ink-2); max-width: 60ch; margin: 0 auto; }

/* An article says when it was written. A reader deciding whether a guide is
   still current looks for this before anything else. */
.page-date {
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 12px;
}

.link-list { display: grid; gap: 12px; margin-top: 26px; }
.link-list a {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 16px 20px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease);
}
.link-list a:hover { border-color: var(--accent); box-shadow: var(--shadow-sm); }
.link-list strong { font-weight: 600; }
.link-list small { color: var(--ink-3); display: block; font-weight: 400; }
.link-list .arrow { color: var(--accent); font-size: 1.1rem; flex-shrink: 0; }

.contact-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px; margin-top: 30px;
}
.contact-card {
  padding: 24px; text-align: center;
  background: #fff; border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.contact-card h3 { font-size: 1rem; margin-bottom: 6px; }
.contact-card p { color: var(--ink-2); font-size: 0.9rem; margin-bottom: 14px; }

.empty {
  text-align: center; padding: 60px 20px; color: var(--ink-3);
  border: 1px dashed var(--line-strong); border-radius: var(--radius-lg);
}

/* -------------------------------------------------------------- footer -- */

.site-footer {
  background: var(--ink);
  color: #c9c9d2;
  padding: 54px 0 0;
  margin-top: 20px;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 40px;
}
/* No contact details set yet — two columns instead of a gap where the third
   would have been. */
.footer-grid-2 { grid-template-columns: 1.6fr 1fr; }

.site-footer h4 {
  color: #fff; font-family: var(--sans);
  font-size: 0.78rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  margin-bottom: 16px;
}
.site-footer .brand { color: #fff; margin-bottom: 14px; }
.site-footer p { font-size: 0.88rem; line-height: 1.7; max-width: 42ch; }
.site-footer li { font-size: 0.88rem; }

/* Footer links were 19px tall — a real miss-and-try-again on a phone.
   `inline-block` lets the padding count toward the hit area while the link
   still only underlines its own words, not the whole column. */
.site-footer li a {
  display: inline-block;
  padding: 8px 0;
  min-height: 36px;
}
.site-footer a:hover { color: #fff; }

.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  padding: 20px 0 calc(20px + var(--safe-b));
  font-size: 0.8rem;
  display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
}

/* ---------------------------------------------------------- responsive -- */

@media (max-width: 900px) {
  .hero-inner { grid-template-columns: 1fr; min-height: 0; padding-top: 44px; gap: 30px; }
  .hero-media { order: -1; margin: 0 calc(var(--gutter) * -1); }
  .hero-media img { max-height: 260px; }
  /* Keep the empty-state box short so the headline stays above the fold
     before a banner image has been uploaded. */
  .hero-media .hero-fallback { min-height: 140px; font-size: .88rem; }
  .hero p { max-width: none; }

  .filter-inner { grid-template-columns: 1fr; }
  .book-detail { grid-template-columns: 1fr; gap: 30px; }
  /* `relative`, not `static` — the placeholder inside is absolutely positioned,
     and a static parent would let it escape to the viewport. */
  .detail-cover { position: relative; top: auto; max-width: 260px; margin: 0 auto; }
  .footer-grid { grid-template-columns: 1fr; gap: 28px; }
}

@media (max-width: 760px) {
  .header-bar { min-height: 62px; }
  .brand { font-size: 1.08rem; letter-spacing: 0.12em; }

  .nav-toggle { display: flex; }

  .main-nav {
    position: fixed; inset: 62px 0 auto 0;
    background: #fff;
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow);
    margin: 0;
    max-height: 0; overflow: hidden;
    transition: max-height 0.28s var(--ease);
  }
  .main-nav.open { max-height: 70vh; overflow-y: auto; }
  .main-nav ul { flex-direction: column; align-items: stretch; gap: 0; padding: 6px 0; }
  .main-nav a {
    display: block; padding: 14px var(--gutter);
    font-size: 0.9rem; border-bottom: 1px solid var(--line);
  }
  .main-nav a::after { display: none; }
  .main-nav li:last-child a { border-bottom: none; }

  .section { padding: 44px 0; }
  .book-grid { grid-template-columns: repeat(2, 1fr); gap: 13px; }
  .book-body { padding: 11px 10px 13px; gap: 2px; }
  .book-title { font-size: 0.9rem; }
  .price { font-size: 0.98rem; gap: 6px; }
  .cover-ph { padding: 18px 14px; }
  .cover-ph b { font-size: 0.92rem; }
  .cover-ph b::after { margin-top: 9px; }
  .cover-ph i { bottom: 17px; }
  .buy-row .btn { flex: 1 1 100%; }
  .footer-bottom { justify-content: center; text-align: center; }
}

@media (max-width: 400px) {
  :root { --gutter: 14px; }
  .book-grid { gap: 10px; }
  .book-title { font-size: 0.85rem; }
  .price { font-size: 0.94rem; }
  .cover-ph b { font-size: 0.84rem; }
  .cover-ph::before { inset: 8px; }
}

/* ------------------------------------------------------- preferences -- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .book-card:hover { transform: none; }
}

@media print {
  .site-header, .site-footer, .filter-bar, .buy-row, .skip-link, .announce { display: none !important; }
  body { color: #000; }
  .book-detail { grid-template-columns: 1fr; }
}
