/* ==========================================================================
   Donnie P. — donnie-p.com
   One stylesheet for the whole site.

   Everything here is derived from the printed jacket rather than invented
   alongside it, so the site reads as an extension of the book a visitor is
   about to buy. The palette hexes below were sampled directly off the cover
   scan (see tools/build-assets.py), not eyeballed.

   Organisation:
     1. Fonts
     2. Tokens
     3. Reset and base
     4. Layout primitives
     5. Ornament and rules
     6. Header
     7. Hero
     8. Buttons
     9. Sections: hook, duality, excerpt, author, buy, signup
    10. Forms
    11. Footer
    12. Motion
    13. Focus and reduced motion
   ========================================================================== */


/* --------------------------------------------------------------------------
   1. Fonts

   Self-hosted rather than loaded from Google's CDN. Two reasons: the render
   isn't gated on a third-party DNS lookup and TLS handshake (which is most of
   what stands between us and the LCP budget in section 7), and there's no
   third-party request to disclose in the privacy policy. Both files are
   variable, so one 38KB and one 48KB download covers every weight used.
   -------------------------------------------------------------------------- */

/* format('woff2'), not format('woff2-variations'). The latter is legacy syntax
   that only ever mattered for old Safari, and declaring it here stops Chrome
   matching the <link rel="preload"> to this request — so the font gets fetched,
   warned about, and fetched again. Variable-axis support comes from the file
   itself, not from the format hint. */
@font-face {
  font-family: 'Playfair Display';
  src: url('../fonts/playfair-var.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('../fonts/inter-var.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}


/* --------------------------------------------------------------------------
   2. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Ground. The jacket's black is very slightly warm (#121011 sampled), so
     pure #000 would look colder than the book. Three steps only, because more
     than three creates visible banding between sections on an OLED phone. */
  --ink:        #0E0D0E;
  --ink-2:      #131214;   /* alternating section ground */
  --ink-3:      #1A1819;   /* cards, inputs, raised surfaces */

  /* Gold. --gold is the jacket's mid-tone and the workhorse: it clears
     4.5:1 against --ink at 9.50:1, so unlike the usual gold-on-black problem
     it is safe at body size and needs no lightening. --gold-bright is the
     metallic highlight from the title's gradient. --gold-deep is border-only
     and never carries text. */
  --gold:        #D4B078;
  --gold-bright: #F0D6A0;
  --gold-deep:   #8A6E3E;

  /* Text. No pure white anywhere, per the design system. Contrast against
     --ink: paper 16.9:1, paper-2 9.2:1, paper-3 5.3:1 — all pass AA body. */
  --paper:    #F2EFE9;
  --paper-2:  #B9B2A6;
  --paper-3:  #8C8579;

  --font-display: 'Playfair Display', 'Times New Roman', Times, serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /* Fluid type. Body lands at 17px on a phone and 19px on a wide screen,
     which is the floor the design system sets. */
  --t-body:      clamp(1.0625rem, 0.35vw + 1rem, 1.1875rem);
  --t-body-lg:   clamp(1.1875rem, 0.6vw + 1.05rem, 1.375rem);
  --t-small:     0.9375rem;
  --t-eyebrow:   0.6875rem;
  --t-display-m: clamp(1.5rem, 3.4vw + 0.8rem, 2.125rem);
  --t-display-l: clamp(1.875rem, 4.4vw + 0.9rem, 3rem);
  --t-display-xl: clamp(2.875rem, 11vw, 6.75rem);

  /* Rhythm. One scale, used everywhere, so vertical spacing stays coherent
     without per-section one-offs. */
  --gutter: 1.375rem;
  --wrap: 68.75rem;          /* 1100px */
  --measure: 36rem;          /* readable line length for long-form prose */
  --section-y: clamp(4rem, 11vw, 8.5rem);

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

@media (min-width: 48rem) {
  :root { --gutter: 2.5rem; }
}


/* --------------------------------------------------------------------------
   3. Reset and base
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;

  /* Guards against a stray wide element causing sideways scroll on a phone.
     Deliberately `clip` on html rather than `overflow-x: hidden` on body:
     hidden on body computes its overflow-y to `auto`, which makes body a scroll
     container in its own right — a known source of broken page scrolling and
     dead position:sticky. `clip` creates no scroll container, so overflow-y
     here stays `visible` and the document scrolls normally. */
  overflow-x: clip;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.72;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.12;
  margin: 0;
  letter-spacing: -0.012em;
}

p { margin: 0 0 1.15em; }
p:last-child { margin-bottom: 0; }

a { color: var(--gold); text-decoration-color: rgba(212, 176, 120, 0.4); text-underline-offset: 0.22em; }
a:hover { color: var(--gold-bright); text-decoration-color: currentColor; }

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

/* Screen-reader-only, used for the skip link and for form labels that the
   design shows as placeholders. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -110%);
  z-index: 100;
  background: var(--gold);
  color: var(--ink);
  padding: 0.7rem 1.4rem;
  font-size: var(--t-small);
  font-weight: 500;
  text-decoration: none;
  transition: transform 0.2s var(--ease);
}
.skip-link:focus { transform: translate(-50%, 0); color: var(--ink); }


/* --------------------------------------------------------------------------
   4. Layout primitives
   -------------------------------------------------------------------------- */

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

.wrap--narrow { max-width: calc(var(--measure) + var(--gutter) * 2); }

.section { padding-block: var(--section-y); position: relative; }
.section--alt { background: var(--ink-2); }
.section--tight { padding-block: clamp(2.75rem, 7vw, 5rem); }

/* Eyebrow: the letterspaced gold caps from the jacket's tagline block. Small
   text, so it leans on --gold's 9.5:1 rather than a lightened variant. */
.eyebrow {
  font-family: var(--font-body);
  font-size: var(--t-eyebrow);
  font-weight: 500;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0 0 1.35rem;
  line-height: 1.5;
}

.lede {
  font-size: var(--t-body-lg);
  color: var(--paper);
  line-height: 1.62;
}

.prose { max-width: var(--measure); }
.prose p { color: var(--paper-2); }
.prose p:first-of-type { color: var(--paper); }

.dim { color: var(--paper-2); }
.center { text-align: center; }
.center .prose, .center .lede { margin-inline: auto; }


/* --------------------------------------------------------------------------
   5. Ornament and rules

   The fleur and the knot are cut from the jacket and used only as section
   punctuation — never more than one per screenful, or they stop reading as
   ornament and start reading as pattern.
   -------------------------------------------------------------------------- */

.orn {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  justify-content: center;
  margin-block: 0 2.5rem;
}

.orn::before, .orn::after {
  content: '';
  height: 1px;
  width: min(6.5rem, 16vw);
  background: linear-gradient(90deg, transparent, var(--gold-deep));
}
.orn::after { background: linear-gradient(270deg, transparent, var(--gold-deep)); }

/* The fleur is roughly square so height sizing suits it. The knot is more
   than twice as wide as it is tall, so height sizing shrinks it to nothing —
   it gets sized on the width instead. */
.orn img { width: auto; height: 2rem; opacity: 0.95; }
.orn--knot img { width: 5.5rem; height: auto; }

/* A bare hairline, for dividing list items rather than sections. */
.hair { height: 1px; background: rgba(138, 110, 62, 0.34); border: 0; margin: 0; }


/* --------------------------------------------------------------------------
   6. Header

   Transparent over the hero, and it gains a ground only once the page has
   scrolled, so nothing competes with the cover reveal on arrival.
   -------------------------------------------------------------------------- */

.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  transition: background-color 0.35s var(--ease), border-color 0.35s var(--ease);
  border-bottom: 1px solid transparent;
}

.site-header.is-stuck {
  background: rgba(14, 13, 14, 0.92);
  backdrop-filter: blur(10px);
  border-bottom-color: rgba(138, 110, 62, 0.28);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 3.5rem;
}

.wordmark {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
  text-decoration: none;
  white-space: nowrap;
}
.wordmark:hover { color: var(--gold-bright); }

.nav-toggle {
  appearance: none;
  background: none;
  border: 1px solid rgba(138, 110, 62, 0.5);
  color: var(--gold);
  font-family: var(--font-body);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 0.6rem 0.9rem;
  cursor: pointer;
  line-height: 1;
}

.nav-list {
  display: flex;
  align-items: center;
  gap: clamp(1.1rem, 2.4vw, 2rem);
}

.nav-list a {
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--paper-2);
  text-decoration: none;
  white-space: nowrap;
}
.nav-list a:hover { color: var(--gold); }
.nav-list a[aria-current='page'] { color: var(--gold); }

/* Mobile: the nav becomes a full-height panel. Below 56rem the toggle shows
   and the list is hidden until opened. */
@media (max-width: 55.999rem) {
  .nav-list {
    position: fixed;
    inset: 3.5rem 0 auto 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: 1rem var(--gutter) 2rem;
    background: rgba(14, 13, 14, 0.98);
    backdrop-filter: blur(10px);
    border-bottom: 1px solid rgba(138, 110, 62, 0.28);
    transform: translateY(-0.5rem);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.25s var(--ease), transform 0.25s var(--ease), visibility 0.25s;
  }
  .nav-list[data-open='true'] {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }
  .nav-list li { width: 100%; }
  .nav-list a {
    display: block;
    padding: 0.95rem 0;
    font-size: 0.9375rem;
    border-bottom: 1px solid rgba(138, 110, 62, 0.18);
  }
  .nav-list li:last-child a { border-bottom: 0; }
}

@media (min-width: 56rem) {
  .nav-toggle { display: none; }
  .site-header .wrap { min-height: 4.5rem; }
}


/* --------------------------------------------------------------------------
   7. Hero

   The thesis of the page: the jacket, lit, standing on the waterfront it
   depicts. Sized so the primary CTA is above the fold on a 375x667 phone —
   the smallest device the design system asks us to hold to.
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: 5rem 3rem;
  overflow: hidden;
  isolation: isolate;
}

/* The skyline plate, cut from the bottom of the jacket. Anchored to the
   bottom edge and dimmed hard: it is ground, not subject. */
.hero__plate {
  position: absolute;
  inset: auto 0 0 0;
  z-index: -2;
  /* width:100% is not redundant with left:0/right:0. For a replaced element,
     width:auto resolves to the intrinsic width (1055px here) and the right
     offset is then ignored as over-constrained — which left a hard vertical
     edge partway across any viewport wider than the image. */
  width: 100%;
  height: min(50%, 27rem);
  object-fit: cover;
  object-position: 50% 100%;
  /* Held well back. At full strength the CN Tower cuts straight through the
     tagline block and the type stops being readable — the plate has to lose
     the fight with the words. */
  opacity: 0.4;
  mask-image: linear-gradient(to top, #000 18%, rgba(0, 0, 0, 0.4) 58%, transparent 100%);
}

/* Warm pool of light behind the cover, so the render has something to sit in
   rather than floating on flat black. */
.hero::before {
  content: '';
  position: absolute;
  z-index: -1;
  left: 50%;
  bottom: 12%;
  width: min(46rem, 130vw);
  aspect-ratio: 1;
  transform: translateX(-50%);
  background: radial-gradient(circle, rgba(212, 176, 120, 0.11) 0%, transparent 62%);
  pointer-events: none;
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(1.5rem, 4vw, 2.5rem);
}

/* The cover render. A 4-degree Y rotation plus a lit fore-edge reads as a
   physical book without a bitmap mockup, which keeps the weight down and
   stays sharp at any DPR. */
.cover {
  position: relative;
  width: clamp(8.75rem, 34vw, 15rem);
  flex: none;
  perspective: 80rem;
}

.cover__img {
  width: 100%;
  border-radius: 1px 3px 3px 1px;
  transform: rotateY(-4deg);
  transform-origin: left center;
  box-shadow:
    0 0.15rem 0.5rem rgba(0, 0, 0, 0.5),
    0 1.5rem 3rem rgba(0, 0, 0, 0.65),
    0 3rem 5rem rgba(0, 0, 0, 0.45);
  transition: transform 0.7s var(--ease);
}

/* Fore-edge: the pages, catching the same warm light as the radial pool. */
.cover::after {
  content: '';
  position: absolute;
  top: 1.2%;
  right: -0.32rem;
  width: 0.34rem;
  height: 97%;
  border-radius: 0 2px 2px 0;
  background: linear-gradient(90deg, #6b5a42 0%, #cbb794 34%, #8a7758 70%, #40372a 100%);
  transform: rotateY(-4deg) translateZ(-0.1rem);
  transform-origin: left center;
  opacity: 0.85;
}

.hero__title {
  font-size: var(--t-display-xl);
  font-weight: 600;
  line-height: 0.94;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  margin: 0;

  /* The jacket's title is a metallic gradient, not a flat gold. Reproducing
     it is the one place this design spends its boldness. The declaration
     order matters: flat gold lands first as the fallback, then the clip
     overrides it only where it is supported. */
  color: var(--gold-bright);
  background: linear-gradient(176deg,
      #B98F4A 0%, #F4D69C 30%, #FFF0CE 44%, #E8C685 62%, #A87F3F 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero__title { color: transparent; }
}

/* The h1 carries no bottom margin of its own, so the rhythm inside the hero
   body is set explicitly here rather than inherited from the flex gap, which
   only separates the jacket from the type block. */
.hero__lines {
  display: flex;
  flex-direction: column;
  margin: 1.15rem 0 0;
  gap: 0.5rem;
  font-size: clamp(0.6875rem, 1.5vw, 0.8125rem);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper-2);
  line-height: 1.6;
  max-width: 34rem;
}

/* Only the last of the three jacket lines is the hook; the other two are
   supporting and step back a level. */
.hero__lines li:last-child { color: var(--paper); letter-spacing: 0.16em; }

.hero__lines li + li { position: relative; padding-top: 0.5rem; }
.hero__lines li + li::before {
  content: '';
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: 1.1rem; height: 1px;
  background: var(--gold-deep);
}

.hero__actions {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin: clamp(1.5rem, 4vw, 2.25rem) auto 0;
  width: 100%;
  max-width: 20rem;
}

/* Scroll cue. Deliberately quiet — a hairline that breathes, not a bouncing
   chevron. Hidden when the viewport is too short to spare the room. */
.hero__cue {
  position: absolute;
  left: 50%;
  bottom: 1.6rem;
  transform: translateX(-50%);
  width: 1px;
  height: 3rem;
  background: linear-gradient(to bottom, transparent, var(--gold-deep));
}

@media (max-height: 43rem) {
  .hero { min-height: auto; padding-block: 5.5rem 3rem; }
  .hero__cue { display: none; }
}

/* From here the hero can afford a two-column arrangement: jacket left,
   type right, which is closer to how the book sits on a shelf. */
@media (min-width: 56rem) {
  .hero__inner {
    flex-direction: row;
    align-items: center;
    text-align: left;
    gap: clamp(3rem, 6vw, 5.5rem);
  }
  .hero__body { max-width: 34rem; }
  .hero__lines { align-items: flex-start; }
  .hero__lines li + li::before { left: 0; transform: none; }
  .hero__actions { flex-direction: row; max-width: none; margin-inline: 0; }
  .cover { width: clamp(15rem, 22vw, 19rem); }
  .cover__img:hover { transform: rotateY(-1.5deg) translateY(-0.4rem); }
}


/* --------------------------------------------------------------------------
   8. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 3rem;
  padding: 0.85rem 1.75rem;
  font-family: var(--font-body);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  text-align: center;
  text-decoration: none;
  border: 1px solid var(--gold);
  cursor: pointer;
  transition: background-color 0.3s var(--ease), color 0.3s var(--ease),
              border-color 0.3s var(--ease), transform 0.3s var(--ease);
}

/* Filled gold with near-black text: 9.5:1, and the only filled element on
   the page, so it is unambiguously the primary action. */
.btn--primary { background: var(--gold); color: var(--ink); }
.btn--primary:hover { background: var(--gold-bright); border-color: var(--gold-bright); color: var(--ink); transform: translateY(-1px); }

.btn--ghost { background: transparent; color: var(--gold); }
.btn--ghost:hover { background: rgba(212, 176, 120, 0.1); color: var(--gold-bright); border-color: var(--gold-bright); }

.btn--block { width: 100%; }

.btn[aria-disabled='true'] {
  border-color: rgba(138, 110, 62, 0.45);
  background: transparent;
  color: var(--paper-3);
  cursor: not-allowed;
  pointer-events: none;
}

/* Text link with a gold rule that draws in on hover. Used for the quiet
   "next step" at the end of a section. */
.link-more {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  font-size: var(--t-small);
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--gold);
  text-decoration: none;
}
.link-more::after {
  content: '';
  width: 1.5rem;
  height: 1px;
  background: currentColor;
  transition: width 0.3s var(--ease);
}
.link-more:hover::after { width: 2.6rem; }


/* --------------------------------------------------------------------------
   9a. The hook
   -------------------------------------------------------------------------- */

.hook {
  font-family: var(--font-display);
  font-size: var(--t-display-m);
  font-weight: 400;
  line-height: 1.36;
  letter-spacing: -0.008em;
  color: var(--paper);
  max-width: 30rem;
  text-wrap: balance;
}

/* Centring is opt-in via a .center ancestor. Baked into .hook itself, the
   max-width plus auto margins silently centred every left-aligned heading that
   used this class inside a normal column. */
.center .hook { margin-inline: auto; }

.hook em { font-style: italic; color: var(--gold-bright); }


/* --------------------------------------------------------------------------
   9b. Duality — the signature element

   The book states its own thesis on page 15: "In one, he was becoming
   Donnie P. ... In the other, he was simply Uncle Don." Two panels, one
   gold hairline between them, and on a phone the rule turns horizontal and
   the panels stack. This is what replaces the jacket's five memoir pillars,
   which describe a different book than the one in the manuscript.
   -------------------------------------------------------------------------- */

.duality {
  display: grid;
  gap: 0;
  border-block: 1px solid rgba(138, 110, 62, 0.34);
}

.duality__panel {
  padding-block: clamp(2.25rem, 6vw, 3.5rem);
  position: relative;
}

/* The dividing rule. Horizontal while stacked; the vertical rule arrives
   with the two-column layout below. */
.duality__panel + .duality__panel { border-top: 1px solid rgba(138, 110, 62, 0.34); }

.duality__name {
  font-family: var(--font-display);
  font-size: clamp(1.625rem, 6vw, 2.375rem);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1;
  margin: 0 0 0.5rem;
}

.duality__panel--public .duality__name { color: var(--gold); }
.duality__panel--private .duality__name { color: var(--paper); }

.duality__who {
  font-size: var(--t-eyebrow);
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--paper-3);
  margin: 0 0 1.35rem;
}

.duality__panel p { color: var(--paper-2); max-width: 27rem; }

@media (min-width: 48rem) {
  /* No column-gap: the rule lives on the second panel's left border and each
     panel supplies its own inner padding, so the gutter is symmetrical and
     the rule sits exactly in the middle of it. A grid gap here would push the
     rule off-centre. */
  .duality { grid-template-columns: 1fr 1fr; }
  .duality__panel--public { padding-right: clamp(2.5rem, 6vw, 5rem); }
  .duality__panel + .duality__panel {
    border-top: 0;
    border-left: 1px solid rgba(138, 110, 62, 0.34);
    padding-left: clamp(2.5rem, 6vw, 5rem);
  }
}


/* --------------------------------------------------------------------------
   9c. Excerpt

   Set as a book page, not as web copy: a longer measure, first-line indents
   after the opening paragraph, and a fade into the call to keep reading.
   -------------------------------------------------------------------------- */

.excerpt {
  position: relative;
  max-width: 34rem;
  margin-inline: auto;
}

.excerpt__body {
  font-family: var(--font-display);
  font-size: var(--t-body-lg);
  line-height: 1.66;
  color: var(--paper);
}

.excerpt__body p + p { text-indent: 1.6em; margin-top: -0.35em; }

/* Dialogue and single-line beats in the manuscript sit on their own without
   an indent — the fiction's rhythm depends on it. */
.excerpt__body p.beat { text-indent: 0; margin-top: 0.9em; }

/* The cut has to land at roughly 200 words, which on a phone's narrow measure
   is a much taller box than on desktop. Sized for the phone and reduced once
   the lines get longer. */
.excerpt--fade .excerpt__body {
  /* Reaches full transparency at 94% rather than 100%, so the bottom strip is
     genuinely blank. The button below is pulled up into that strip, and if the
     text were still faintly legible there the two would collide. */
  mask-image: linear-gradient(to bottom, #000 60%, transparent 94%);
  max-height: 44rem;
  overflow: hidden;
}

@media (min-width: 48rem) {
  /* Wider measure fits more words per line, so less height is needed to reach
     roughly the same 200-word cut. */
  .excerpt--fade .excerpt__body { max-height: 36rem; }
}

.excerpt__after {
  margin-top: -3.25rem;
  position: relative;
  text-align: center;
}

/* Sits above the extract, where a chapter heading belongs. Below it, it read
   as a caption for the button. */
.excerpt__chapter {
  font-family: var(--font-display);
  font-size: var(--t-display-m);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0 0 clamp(1.75rem, 4vw, 2.5rem);
}


/* --------------------------------------------------------------------------
   9d. Author
   -------------------------------------------------------------------------- */

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

.author-portrait {
  position: relative;
  max-width: 20rem;
  margin-inline: auto;
  width: 100%;
}

.author-portrait img {
  width: 100%;
  filter: grayscale(1) contrast(1.04);
}

/* A single gold hairline along the base, tying the photograph to the jacket's
   rules rather than framing it on all four sides. */
.author-portrait::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 1px;
  background: linear-gradient(90deg, var(--gold), transparent 85%);
}

.signature {
  width: min(13rem, 60%);
  margin-top: 1.75rem;
  opacity: 0.9;
}

@media (min-width: 48rem) {
  .author-row { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }
  /* Capped at the source width. The tall portrait is cut from a jacket panel
     and only 476px wide, so letting it fill its column upscales it by about a
     quarter and visibly softens the face. A higher-resolution original would
     let this cap come off — see the asset checklist in README.md. */
  .author-portrait { margin-inline: 0; max-width: 476px; }
  .author-row--reverse .author-portrait { order: 2; margin-inline: auto; }
}


/* --------------------------------------------------------------------------
   9e. Specs and formats
   -------------------------------------------------------------------------- */

.specs { border-top: 1px solid rgba(138, 110, 62, 0.34); }

.specs__row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.5rem;
  padding-block: 0.85rem;
  border-bottom: 1px solid rgba(138, 110, 62, 0.18);
  font-size: var(--t-small);
}

.specs__key {
  flex: 0 0 9rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: var(--t-eyebrow);
  color: var(--gold);
  padding-top: 0.2em;
}

.specs__val { color: var(--paper-2); }

/* Anything still waiting on the author or publisher is marked, not faked. */
.specs__val[data-pending] { color: var(--paper-3); font-style: italic; }

.formats { display: grid; gap: 0.75rem; }

.format {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.05rem 1.35rem;
  background: var(--ink-3);
  border: 1px solid rgba(138, 110, 62, 0.28);
}

.format__name {
  font-family: var(--font-display);
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: 0.03em;
  color: var(--paper);
}

.format__meta { font-size: var(--t-small); color: var(--paper-3); }

@media (min-width: 40rem) {
  .formats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}


/* --------------------------------------------------------------------------
   9f. Chapter / theme list
   -------------------------------------------------------------------------- */

.themes { display: grid; gap: 0; }

.theme {
  display: grid;
  gap: 0.5rem 1.75rem;
  padding-block: clamp(1.35rem, 3.5vw, 2rem);
  border-top: 1px solid rgba(138, 110, 62, 0.22);
}
.themes .theme:last-child { border-bottom: 1px solid rgba(138, 110, 62, 0.22); }

.theme__title {
  font-family: var(--font-display);
  font-size: 1.1875rem;
  font-weight: 500;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0;
}

.theme p { color: var(--paper-2); margin: 0; max-width: 42rem; }

@media (min-width: 48rem) {
  .theme { grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr); align-items: start; }
}


/* --------------------------------------------------------------------------
   9g. Press kit downloads
   -------------------------------------------------------------------------- */

.downloads { display: grid; gap: 0.75rem; }

.download {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  padding: 1.05rem 1.35rem;
  background: var(--ink-3);
  border: 1px solid rgba(138, 110, 62, 0.28);
  text-decoration: none;
  transition: border-color 0.3s var(--ease), background-color 0.3s var(--ease);
}
.download:hover { border-color: var(--gold); background: #201d1e; }

.download__name { color: var(--paper); font-weight: 500; font-size: var(--t-small); }
.download__meta { color: var(--paper-3); font-size: var(--t-eyebrow); letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap; }


/* --------------------------------------------------------------------------
   10. Forms
   -------------------------------------------------------------------------- */

.field { display: grid; gap: 0.45rem; margin-bottom: 1.1rem; }

.field > label {
  font-size: var(--t-eyebrow);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--gold);
}

.field input, .field textarea, .field select {
  width: 100%;
  font-family: var(--font-body);
  font-size: 1rem;              /* 16px minimum, or iOS zooms on focus */
  color: var(--paper);
  background: var(--ink-3);
  border: 1px solid rgba(138, 110, 62, 0.4);
  padding: 0.85rem 1rem;
  min-height: 3rem;
  transition: border-color 0.25s var(--ease), background-color 0.25s var(--ease);
}

.field textarea { min-height: 9rem; resize: vertical; line-height: 1.6; }

.field input:hover, .field textarea:hover, .field select:hover { border-color: rgba(212, 176, 120, 0.65); }
.field input:focus, .field textarea:focus, .field select:focus { border-color: var(--gold); background: #211e1f; }

.field input::placeholder, .field textarea::placeholder { color: var(--paper-3); }

/* Honeypot. Off-screen rather than display:none — some bots skip hidden
   fields but will happily fill one that is merely positioned away. */
.field--trap {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.75rem;
  align-items: start;
  margin-bottom: 1.35rem;
}

.consent input[type='checkbox'] {
  width: 1.15rem; height: 1.15rem;
  margin-top: 0.28rem;
  accent-color: var(--gold);
  flex: none;
}

.consent label { font-size: var(--t-small); color: var(--paper-2); line-height: 1.55; }

.form-note { font-size: var(--t-small); color: var(--paper-3); margin-top: 1rem; }

/* Inline signup: one field and a button, side by side once there is room.
   Auto margins so the form sits centred under a centred heading rather than
   hugging the left edge of the column. */
.signup { display: grid; gap: 0.75rem; max-width: 30rem; margin-inline: auto; }
.signup__row { display: grid; gap: 0.75rem; }

@media (min-width: 34rem) {
  .signup__row { grid-template-columns: 1fr auto; align-items: start; }
}

/* Server responses. Both states are announced via aria-live in the markup. */
.form-msg {
  padding: 0.9rem 1.15rem;
  font-size: var(--t-small);
  border-left: 2px solid var(--gold);
  background: rgba(212, 176, 120, 0.08);
  color: var(--paper);
  margin-bottom: 1.25rem;
}
.form-msg--error { border-left-color: #E0846A; background: rgba(224, 132, 106, 0.09); }
.form-msg:empty { display: none; }


/* --------------------------------------------------------------------------
   11. Footer
   -------------------------------------------------------------------------- */

.site-footer {
  border-top: 1px solid rgba(138, 110, 62, 0.28);
  padding-block: clamp(2.75rem, 6vw, 4rem) 2rem;
  background: var(--ink-2);
}

.footer-top {
  display: grid;
  gap: 2.25rem;
  padding-bottom: 2.25rem;
  border-bottom: 1px solid rgba(138, 110, 62, 0.18);
}

.footer-nav { display: grid; gap: 0.7rem; }
.footer-nav a { font-size: var(--t-small); color: var(--paper-2); text-decoration: none; }
.footer-nav a:hover { color: var(--gold); }

.footer-head {
  font-size: var(--t-eyebrow);
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
  margin: 0 0 1.1rem;
}

.footer-mark img { width: 13.5rem; opacity: 0.85; }

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.5rem;
  justify-content: space-between;
  padding-top: 1.75rem;
  font-size: var(--t-small);
  color: var(--paper-3);
}

.footer-bottom a { color: var(--paper-3); text-decoration: none; }
.footer-bottom a:hover { color: var(--gold); }

@media (min-width: 48rem) {
  .footer-top { grid-template-columns: 1.4fr 1fr 1fr; gap: 3rem; }
}


/* --------------------------------------------------------------------------
   12. Motion

   One gesture, used consistently: fade and rise, 420ms, triggered on scroll.
   The .reveal class is inert until the observer in site.js adds .is-in, and
   the no-js fallback below makes sure content is never trapped invisible.
   -------------------------------------------------------------------------- */

.reveal {
  opacity: 0;
  transform: translateY(1.25rem);
  transition: opacity 0.42s var(--ease), transform 0.42s var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

/* Staggered children, for the two duality panels and the format cards. */
.reveal[data-delay='1'] { transition-delay: 0.09s; }
.reveal[data-delay='2'] { transition-delay: 0.18s; }
.reveal[data-delay='3'] { transition-delay: 0.27s; }

.no-js .reveal { opacity: 1; transform: none; }


/* --------------------------------------------------------------------------
   13. Focus and reduced motion
   -------------------------------------------------------------------------- */

:focus-visible {
  outline: 2px solid var(--gold-bright);
  outline-offset: 3px;
}

/* Inputs already carry a border, so the offset ring would double up. */
.field input:focus-visible,
.field textarea:focus-visible,
.field select:focus-visible {
  outline-offset: 1px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
  .cover__img:hover { transform: rotateY(-4deg); }
}
