/*
 * Living Books hub. Palette, type, spacing and motion tokens are the
 * InTheValley brand set (inthevalley/website/styles/globals.css), which the
 * Second Look app shares: paper and ink, valley slate as the one marker,
 * Newsreader / Instrument Sans / IBM Plex Mono. Contrast on paper is noted
 * beside each text token.
 */

@font-face {
  font-family: "Newsreader";
  src: url("/fonts/Newsreader-Variable.ttf") format("truetype");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Newsreader";
  src: url("/fonts/Newsreader-Italic-Variable.ttf") format("truetype");
  font-weight: 200 800;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: "Instrument Sans";
  src: url("/fonts/InstrumentSans-Variable.ttf") format("truetype");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --paper: #f3efe6;
  --paper-deep: #e8e2d4;
  --ink: #16150f; /* 15.94 */
  --stone: #5c584f; /* 6.17 */
  --accent: #4b6880; /* 5.10: markers, links, figures */
  --rule: #cec7b8; /* hairlines only */
  --live: #3f6b4a; /* 5.6: the status dot and its word */
  --mist: #a39d90; /* 6.78 on ink: quieter text on the ink band only */

  --serif: "Newsreader", Georgia, serif;
  --sans: "Instrument Sans", system-ui, sans-serif;
  --mono: "IBM Plex Mono", ui-monospace, monospace;

  --s2: 8px;
  --s3: 16px;
  --s4: 24px;
  --s5: 32px;
  --s6: 48px;
  --s7: 64px;
  --s8: 96px;
  --s9: 128px;

  --ease-out: cubic-bezier(0.2, 0, 0, 1);
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
  --motion-control: 120ms;
  --motion-panel: 240ms;
  --motion-reveal: 520ms;
  --motion-flip: 600ms;
  --motion-open: 900ms;
  --stagger: 90ms;

  --page: 1280px;
  --gutter: 16px;
  --cover-w: min(300px, 72vw);
}

@media (min-width: 640px) {
  :root { --gutter: 32px; --cover-w: 280px; }
}

@media (min-width: 1280px) {
  :root { --gutter: 48px; --cover-w: 300px; }
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100vh;
  background:
    radial-gradient(1200px 600px at 50% 62%, var(--paper-deep), transparent 70%),
    var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.55;
  /* ITV body rendering (styles/globals.css): without it macOS draws the same 500 weight heavier */
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* One box: header, body and footer share the same container and edges. */
.page {
  max-width: var(--page);
  margin: 0 auto;
  padding: 0 var(--gutter);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Header, the InTheValley website's (inthevalley/website globals.css .site-header):
   a centered row at least 80px tall with 16px above and below, the rule across
   the whole window, the outlined Newsreader SemiBold wordmark at 26px tall
   (scripts/make_wordmark.py, same recipe and vertical box as the InTheValley and
   leocelis.com marks), and the link in sans 500 at 15px, ink at rest, a 1px
   underline 3px under on hover and keyboard focus. */
.masthead {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  min-height: 80px;
  padding: var(--s3) 0;
}

/* The rule runs edge to edge like ITV's, from inside the page box: a 1px bar
   whose shadow extends sideways, clipped to its own height (no sideways scroll). */
.masthead::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: var(--rule);
  box-shadow: 0 0 0 100vmax var(--rule);
  clip-path: inset(0 -100vmax);
}

.wordmark {
  display: block;
  line-height: 0;
}

.wordmark img {
  display: block;
  height: 22px; /* a step under the ITV wordmark (26px) */
  width: auto;
}

.masthead__note {
  font-family: var(--sans);
  font-weight: 500;
  font-size: 15px;
  line-height: 1.4;
  color: var(--ink);
  text-decoration: none;
}

.masthead__note:hover,
.masthead__note:focus-visible {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

main { flex: 1; }

/* The title block sits a fixed space under the rule, never on it, and is
   centred over the shelf: one axis for the text and the two covers, so no
   side of the screen is a blank band beside a short block (brand 8.1). */
.intro {
  max-width: 64rem;
  margin: 0 auto;
  padding-top: var(--s6);
  text-align: center;
}

h1 {
  margin: 0 auto;
  max-width: 46rem;
  font-size: clamp(34px, 4.4vw, 54px);
  line-height: 1.08;
  text-wrap: balance;
  font-weight: 500;
  letter-spacing: -0.02em;
}

/* How it works: the one ink band, edge to edge, as on leocelis.com (.proof)
   and the ITV site (.section--ink). The content keeps the header's box; the
   shadow paints the ink past it and clip-path stops a sideways scroll. Paper
   15.94:1 for titles, mist 6.78:1 for the quieter lines and the hairlines; the
   accent (3.13:1 on ink) is never text here. Four steps, one column on phones,
   two from 640px, four from 1024px, text left-aligned (ITV guide 8.1). */
.how {
  margin-top: var(--s7);
  /* A little more ink under the steps than above the title. */
  padding: var(--s7) 0 var(--s8);
  background: var(--ink);
  color: var(--paper);
  box-shadow: 0 0 0 100vmax var(--ink);
  clip-path: inset(0 -100vmax);
}

.how__title {
  /* ITV's section label (.label, on ink in mist): the sans at 15px, never a
     mono all-caps code-style eyebrow (Second Look 2: mono is for a number). */
  margin: 0 0 var(--s6);
  font-family: var(--sans);
  font-size: 15px;
  font-weight: 500;
  line-height: 1.45;
  letter-spacing: 0.02em;
  color: var(--mist);
}

/* ITV's numbered points (.columns): no rule over each point, hairlines
   separate sections only (brand 8.1); the serif's own figures set large as
   the marker. On ink the accent is never text (3.13:1), so the figure is
   mist (6.78:1). Number, title and text share rows across the list, so a
   title that wraps pushes every text down with it and the texts start level
   (ITV-BF-194). */
.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s6) var(--s6);
  text-align: left;
}

@media (min-width: 640px) {
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .step {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 3;
    row-gap: var(--s2);
    align-content: start;
  }
}

/* Four columns only once each card text fits in three lines;
   below 1280 the columns are too narrow, so two columns hold. */
@media (min-width: 1280px) {
  .steps { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.step {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.step__n {
  font-family: var(--serif);
  font-size: 40px;
  font-weight: 400;
  line-height: 1;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--mist);
}

.step__title {
  margin: var(--s2) 0 0;
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 600;
  line-height: 1.3;
  font-variation-settings: "opsz" 24;
  text-wrap: balance;
  color: var(--paper);
}

.step__text {
  margin: 0;
  font-family: var(--serif);
  font-size: 18px;
  line-height: 1.55;
  color: var(--mist);
  text-wrap: pretty;
}

/* Workflow line: a mist hairline joins 01 -> 04 with a dot at each step,
   the way the pipeline hands the book along. A
   diagram may draw once, the first time it scrolls into view, then stay; the words
   never move. No icons on steps. The drawn state is the default,
   so no script, no observer or reduced motion all show the finished line.
   Phones: the line runs down the left of the stacked steps. From 640px it runs
   across, 24px above the numbers, through each column gap to the next step. */
.step { position: relative; }
.step::before,
.step::after {
  content: "";
  position: absolute;
  pointer-events: none;
}
.step::after {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--mist);
}
@media (max-width: 639.98px) {
  .step { padding-left: var(--s5); }
  .step::after { left: 0; top: 17px; }
  .step::before {
    left: 3px;
    top: 20px;
    width: 1px;
    height: calc(100% + var(--s6));
    background: var(--mist);
    transform-origin: top;
  }
  .step:last-child::before { display: none; }
}
@media (min-width: 640px) {
  .step::after { left: 0; top: calc(-1 * var(--s4) - 3px); }
  .step::before {
    left: 0;
    top: calc(-1 * var(--s4));
    width: calc(100% + var(--s6));
    height: 1px;
    background: var(--mist);
    transform-origin: left;
  }
  /* Two columns: each row's line ends at its second step. */
  .step:nth-child(2n)::before { width: 100%; }
}
@media (min-width: 1280px) {
  .step:nth-child(2n)::before { width: calc(100% + var(--s6)); }
  .step:last-child::before { width: 100%; }
}

/* Armed (script, before first view): line undrawn, dots and figures at 60%.
   Mist at 60% on ink is 3.2:1, over the 3:1 floor for 40px figures, so every
   step reads before anything moves. */
.how.is-armed .step::before { transform: scale(0, 1); }
@media (max-width: 639.98px) {
  .how.is-armed .step::before { transform: scale(1, 0); }
}
.how.is-armed .step::after,
.how.is-armed .step__n { opacity: 0.6; }

/* Drawn: each segment eases out in 360ms, one after another 300ms apart, so the
   whole line lands in about 1.2s; a figure lifts as its segment starts. Once. */
.how.is-armed.is-drawn .step::before {
  transform: none;
  transition: transform 360ms var(--ease-out) calc(var(--i, 0) * 300ms);
}
.how.is-armed.is-drawn .step::after,
.how.is-armed.is-drawn .step__n {
  opacity: 1;
  transition: opacity 200ms var(--ease-out) calc(var(--i, 0) * 300ms);
}
.step:nth-child(2) { --i: 1; }
.step:nth-child(3) { --i: 2; }
.step:nth-child(4) { --i: 3; }

@media (prefers-reduced-motion: reduce) {
  .how.is-armed .step::before { transform: none; }
  .how.is-armed .step::after,
  .how.is-armed .step__n { opacity: 1; }
}

/* ── Shelf ──────────────────────────────────────────────────────────────── */

/* The two books stand together on the shelf's centre line, a fixed gap apart,
   instead of each centred in half the page (which pushed them to the edges). */
.shelf {
  display: grid;
  grid-template-columns: var(--cover-w);
  justify-content: center;
  gap: var(--s8);
  padding: var(--s7) 0 var(--s6);
}

@media (min-width: 768px) {
  .shelf {
    grid-template-columns: repeat(2, var(--cover-w));
    column-gap: clamp(var(--s6), 8vw, var(--s9));
  }
}

.book {
  width: var(--cover-w);
  opacity: 0;
  transform: translateY(28px);
  animation: rise var(--motion-reveal) var(--ease-out) forwards;
  animation-delay: calc(var(--i, 0) * var(--stagger) + 160ms);
}

.intro > * {
  opacity: 0;
  transform: translateY(14px);
  animation: rise var(--motion-reveal) var(--ease-out) forwards;
}
.intro > :nth-child(2) { animation-delay: 60ms; }
.intro > :nth-child(3) { animation-delay: 120ms; }

@keyframes rise {
  to { opacity: 1; transform: none; }
}

.book__link {
  display: block;
  color: inherit;
  text-decoration: none;
  perspective: 1400px;
  border-radius: 4px;
}

.book__link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 10px;
}

/* The book: a cover hinged on its spine, a block of pages behind it. */
.book__object {
  position: relative;
  aspect-ratio: 2 / 3;
  transform-style: preserve-3d;
  transform: rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform var(--motion-panel) var(--ease-out);
  animation: float 7s var(--ease-in-out) infinite;
  animation-delay: calc(var(--i, 0) * -2.3s);
}

@keyframes float {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 -8px; }
}

/* Soft contact shadow under the book, breathing with the float. */
.book__object::after {
  content: "";
  position: absolute;
  left: 8%;
  right: 8%;
  bottom: -28px;
  height: 22px;
  background: radial-gradient(closest-side, rgba(22, 21, 15, 0.28), transparent);
  transform: translateZ(-40px);
  pointer-events: none;
}

.book__pages {
  position: absolute;
  inset: 2% 0 2% 2%;
  border-radius: 2px 6px 6px 2px;
  background:
    repeating-linear-gradient(90deg, #fbf8f1 0 2px, #e9e3d5 2px 3px) right / 14px 100% no-repeat,
    #fbf8f1;
  box-shadow: 0 18px 40px -18px rgba(22, 21, 15, 0.45);
  transform: translateZ(-6px);
}

/* Inside page: what the reader gets, in a short paragraph, revealed as the cover swings open.
   Ink on paper, same tokens as the page; clipped to the page, never overflowing. */
.book__page-text {
  position: absolute;
  inset: 0 14px 0 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 14px;
  padding: 14% 12% 14% 14%;
  overflow: hidden;
  text-align: left;
}

.book__page-kicker {
  margin: 0;
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--stone);
}

.book__page-quote {
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(14px, 1.15vw, 17px);
  line-height: 1.5;
  color: var(--ink);
}

.book__page-sign {
  margin: 0;
  font-family: var(--sans);
  font-size: 13px;
  color: var(--stone);
}

.book__cover {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 2px 6px 6px 2px;
  background: #2b2a6e;
  transform-origin: left center;
  transform: rotateY(0deg);
  transition: transform var(--motion-open) var(--ease-in-out);
  backface-visibility: hidden;
  box-shadow:
    0 30px 60px -24px rgba(22, 21, 15, 0.55),
    0 2px 6px rgba(22, 21, 15, 0.18);
}

/* A hairline crease where the cover meets the spine. */
.book__cover::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 10px;
  width: 2px;
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.28), rgba(255, 255, 255, 0.18));
  z-index: 2;
}

.book__spine {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 16px;
  background: linear-gradient(90deg, #1b1a48, #2b2a6e);
  transform-origin: left center;
  transform: rotateY(-90deg);
  border-radius: 2px 0 0 2px;
}

.book__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Typographic cover, shown only when a book has no cover image yet. */
.book__type {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: var(--s3);
  padding: var(--s5);
  color: #f6f2e8;
  background: linear-gradient(160deg, #2b2a6e, #4a3f9a);
  text-align: center;
}
.book--no-cover .book__type { display: flex; }
.book--no-cover .book__img { display: none; }
.book__type-title { font-size: 28px; line-height: 1.12; font-weight: 600; }
.book__type-sub { font-style: italic; font-size: 17px; }

/* Light that follows the pointer across the cover. */
.book__sheen {
  position: absolute;
  inset: 0;
  z-index: 3;
  background: radial-gradient(
    420px circle at var(--sx, 30%) var(--sy, 20%),
    rgba(255, 255, 255, 0.22),
    transparent 46%
  );
  opacity: 0;
  transition: opacity var(--motion-panel) var(--ease-out);
  pointer-events: none;
}

.book__link:hover .book__sheen,
.book__link:focus-visible .book__sheen { opacity: 1; }

.book__link:hover .book__object { animation-play-state: paused; }

/* Hover: the cover swings open on its spine and shows the inside page; it
   closes when the pointer leaves. The lift keeps the pointer tilt (--rx/--ry).
   Pointer devices only, and never under reduced motion: there the cover stays
   shut until the click. The hovered book paints over its neighbour. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .book:hover { position: relative; z-index: 2; }
  .book__link:hover .book__object,
  .book__link:focus-visible .book__object {
    transform: rotateX(var(--rx, 0deg)) rotateY(calc(var(--ry, 0deg) - 14deg)) translateZ(40px) scale(1.04);
  }
  .book__link:hover .book__cover,
  .book__link:focus-visible .book__cover { transform: rotateY(-118deg); }
}

/* Opening (click): the cover swings fully back, then the book loads. */
.book--opening .book__object {
  animation: none;
  transform: rotateY(-14deg) translateZ(40px) scale(1.04);
  transition: transform var(--motion-open) var(--ease-in-out);
}
.book--opening .book__cover { transform: rotateY(-118deg); }

.shelf.is-opening .book:not(.book--opening) {
  opacity: 0.35;
  transition: opacity var(--motion-panel) var(--ease-out);
}

/* ── Meta under each cover ──────────────────────────────────────────────── */

.book__meta {
  padding-top: var(--s5);
  text-align: center;
}

.book__title {
  margin: 0;
  font-size: 22px;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.book__subtitle {
  margin: var(--s2) 0 0;
  font-style: italic;
  font-size: 18px;
  color: var(--stone);
}

.book__facts {
  margin: var(--s3) 0 0;
  font-family: var(--sans);
  font-size: 15px;
  color: var(--ink);
}

.book__status {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  margin: var(--s2) 0 0;
  font-family: var(--mono);
  font-size: 14px;
  color: var(--stone);
}

.dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--rule);
  flex: none;
}

.book--live .dot {
  background: var(--live);
  box-shadow: 0 0 0 0 rgba(63, 107, 74, 0.45);
  animation: pulse 2.4s var(--ease-out) infinite;
}

@keyframes pulse {
  0% { box-shadow: 0 0 0 0 rgba(63, 107, 74, 0.45); }
  70% { box-shadow: 0 0 0 8px rgba(63, 107, 74, 0); }
  100% { box-shadow: 0 0 0 0 rgba(63, 107, 74, 0); }
}

/* Skeletons: the final shape, so nothing shifts when the books arrive. */
.book--skeleton { opacity: 1; transform: none; animation: none; }
.book--skeleton .book__object { animation: none; }
.book--skeleton .book__cover {
  background: linear-gradient(100deg, var(--paper-deep) 30%, #efe9dc 50%, var(--paper-deep) 70%) 0 0 / 300% 100%;
  box-shadow: none;
  animation: shimmer 1.6s linear infinite;
}
.skel {
  display: block;
  margin: 0 auto;
  height: 18px;
  border-radius: 4px;
  background: var(--paper-deep);
}
.skel--title { width: 80%; height: 22px; }
.skel--line { width: 55%; margin-top: var(--s3); }

@keyframes shimmer {
  to { background-position: -300% 0; }
}

/* ── Footer: room above it, same box ────────────────────────────────────── */

.footer {
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  /* The ink band above replaces the hairline (as on the ITV site); the
     margin keeps room between the band and the footer. */
  margin-top: var(--s6);
  padding: var(--s4) 0 var(--s5);
  font-family: var(--sans);
  font-size: 15px;
  color: var(--stone);
}

.footer { align-items: center; flex-wrap: wrap; }
.footer__legal { margin: 0; }
/* Type and seal as the InTheValley footer: sans 500 at 15px / 1.4, 8px either
   side of the dot, seal 26px; colors stay the hub's own. */
.footer { font-weight: 500; line-height: 1.4; }
.footer__sep { margin: 0 8px; }
.footer__legal a { color: inherit; text-decoration: none; }
.footer__legal a:hover,
.footer__legal a:focus-visible { color: var(--ink); text-decoration: underline; }
/* Seal ring: stone on paper 6.6:1 (WCAG 1.4.11 needs 3:1), so the seal's light
   half does not melt into the page. */
.footer__seal { display: inline-flex; }
.footer__seal img { display: block; height: 26px; width: auto; border-radius: 4px; outline: 1px solid var(--stone); }

@media (prefers-reduced-motion: reduce) {
  .book, .intro > * { animation: none; opacity: 1; transform: none; }
  .book__object { animation: none; transition: none; }
  .book__cover { transition: none; }
  .book--live .dot { animation: none; }
  .book--skeleton .book__cover { animation: none; }
}
