/* style.css — all the visuals.
   A reading journal: one classic serif (EB Garamond), a quiet left column, and the books grouped
   under big grey year headings. Each book has its cover (with a spine shadow so it looks like a
   real book), then the title, author, rating stars and the date finished.
   Colours live in CSS variables on :root, so changing a colour means changing it in one place. */

/* Handwriting fonts by Khurasan (free versions), kept in fonts/. The one named in --hand is used in
   the book pop-up for the answers under Finished, Times read, Pages and Published, and for the text
   under Impressions. To switch, change --hand in :root. */
@font-face {
  font-family: "Royalty Free";
  src: url("fonts/royalty-free.ttf") format("truetype");
  font-display: swap;
}

@font-face {
  font-family: "High Empathy";
  src: url("fonts/high-empathy.ttf") format("truetype");
  font-display: swap;
}

@font-face {
  font-family: "Mayonice";
  src: url("fonts/mayonice.ttf") format("truetype");
  font-display: swap;
}

@font-face {
  font-family: "DS Gabriele";
  src: url("fonts/ds-gabriele.ttf") format("truetype");
  font-display: swap;
}

@font-face {
  font-family: "Biedermeier Kursiv";
  src: url("fonts/biedermeier-kursiv.ttf") format("truetype");
  font-display: swap;
}

@font-face {
  font-family: "WC Mano Negra";
  src: url("fonts/wc-mano-negra.ttf") format("truetype");
  font-display: swap;
}

:root {
  --hand-ink: #000c47; /* deep navy, like a ballpoint pen: the colour of all the handwriting */
  --hand: "WC Mano Negra"; /* the handwriting font on the book page (or "Biedermeier Kursiv", "DS Gabriele", "Mayonice", "High Empathy", "Royalty Free") */

  /* The four main colours */
  --bg: #fff5dd;         /* warm cream page */
  --text: #636b2f;       /* olive green ink: main text, the line above each year */
  --accent: #6b5e3b;     /* olive-bronze: authors, rating dots, hover, selected */
  --line: #e8e0d1;       /* sand: hairlines, empty cover boxes */

  /* In-between shades, made by blending the accent or text into the background,
     so everything stays in the same colour family */
  --text-soft: #626a4c;  /* intro paragraph and edit-mode text (text, a little lighter) */
  --author: var(--accent);
  --muted: #887d5f;      /* small labels, unselected options (accent at 80%) */
  --faint: #9d947b;      /* dates (accent at 65%) */
  --year: #a59c84;       /* the chart bars (accent at 60%) */
  --red: #cb2b31;        /* the red of the stamp logo: the big year numbers, "Reading now", "Find" and the rating stars */
  --field-line: #d6cbb5; /* underline of the search box and dropdowns (a darker sand) */

  /* Worn look on the covers (see "Covers" below) */
  --film-grain: 0.10; /* fine paper grain: 0 = off, 0.2 = stronger */
  --wear: 1;          /* scuffs, creases and rubbed edges: 0 = off, 0.5 = half as strong */

  --cover-w: 200px;      /* every cover is this wide (the "bigger" size from the design) */
  --serif: "EB Garamond", Garamond, "Times New Roman", serif;
  color-scheme: light;
}

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

/* Make the "hidden" attribute always win, even over elements styled with display: flex */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--serif);
  font-size: 19px;
  line-height: 1.5;
  font-variant-numeric: oldstyle-nums; /* numbers that sit in the text like lowercase letters, as in old books */
}

/* Stop the page behind a pop-up from scrolling while it's open */
body:has(dialog[open]) { overflow: hidden; }

/* No built-in margins; spacing is added deliberately instead */
h1, h2, h3, p, ul, dl, dd { margin: 0; }
h1, h2, h3 { font-weight: 400; }
ul { padding: 0; list-style: none; }

/* Buttons and fields use the page font instead of the browser's default */
button, input { font: inherit; color: inherit; }
button { cursor: pointer; }

:focus-visible { outline: 1px solid var(--accent); outline-offset: 4px; }

/* Small spaced-out capitals used for every label */
.small-label {
  display: block;
  margin-bottom: 6px;
  color: var(--muted);
  font-size: 13px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-variant-numeric: lining-nums;
}

/* "Reading now" and "Find" in the left column are red; small labels elsewhere stay grey */
.side .small-label { color: var(--red); }

/* Text that acts as a link or button: italic and underlined */
.link {
  padding: 0;
  color: var(--author);
  background: none;
  border: 0;
  font-size: 17px;
  font-style: italic;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.link:hover { color: var(--accent); }

.author { display: block; color: var(--author); font-style: italic; }

/* ---------- Page layout ----------
   Two columns: the left column and the books on the right. On phones they stack. */

.page {
  display: grid;
  grid-template-columns: minmax(220px, 300px) minmax(0, 1fr);
  gap: 40px clamp(40px, 6vw, 112px);
  max-width: 1600px;
  margin: 0 auto;
  padding: 64px clamp(20px, 4.5vw, 72px) 120px;
}

.shelf { min-width: 0; }

.side {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 40px;

  /* Sticky: the left column stays put while the books scroll. align-self: start stops the grid
     from stretching the column to the full page height (a full-height column would have nowhere
     to stick). If the column is taller than the window (lots of "Reading now" books), it scrolls by
     itself. Its box reaches the very top of the window (the 64px space is padding inside it, pulled
     up over the page's own top padding), so its content slides off the window's edge just like the
     books, instead of vanishing along an invisible line 64px down. */
  position: sticky;
  top: 0;
  align-self: start;
  max-height: 100vh;
  margin-top: -64px;
  padding: 64px 0 48px; /* 48px at the bottom: room below the last item when scrolled to the end */
  overflow-y: auto;
  scrollbar-width: none; /* hide that scrollbar; the column still scrolls with the mouse wheel */
}

/* ---------- Left column ---------- */

.intro { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; }
h1 { font-size: 38px; line-height: 1.1; }

/* The stamp logo to the left of the title, about as tall as a stamp on an envelope.
   width: auto keeps its shape (it's 2.5 times taller than wide). */
.title-row { display: flex; align-items: center; gap: 18px; }
.logo { display: block; flex: none; width: auto; height: 120px; }
.intro p { color: var(--text-soft); }

/* Find box, then the two small dropdowns, then how many books are showing */
.filters { display: flex; flex-direction: column; gap: 10px; width: 100%; font-size: 17px; }

.find { display: block; margin-bottom: 6px; }

/* "Order by [when I read it]": a grey label and a dropdown that looks like underlined text */
.filter { display: flex; align-items: baseline; gap: 10px; }
.filter-label { color: var(--muted); }

.filter select {
  min-width: 0;
  padding: 0 2px 1px 0;
  color: var(--text);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--field-line);
  border-radius: 0;
  font: inherit;
  cursor: pointer;
}

.filter select:focus { outline: none; border-bottom-color: var(--accent); }

.find input {
  width: 100%;
  padding: 4px 0;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--field-line);
  border-radius: 0;
  font-size: 17px;
}

.find input:focus { outline: none; border-bottom-color: var(--accent); }
input::placeholder { color: var(--faint); font-style: italic; }

/* The book count, and "show everything" when a filter is on. Flex with a column gap means the space
   between them only appears when they share a line; if it wraps, the link starts at the left edge. */
.status-line { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 8px; margin-top: 4px; color: var(--muted); font-size: 16px; font-style: italic; }
.status.error { color: var(--accent); font-style: normal; }
.status-line .link { font-size: 16px; }

/* "reading stats": a small outlined button at the bottom of the left column */
.stats-button {
  padding: 6px 16px;
  color: var(--text);
  background: none;
  border: 1px solid var(--text);
  border-radius: 999px;
  font-size: 16px;
  font-style: italic;
  transition: background-color 0.2s, color 0.2s;
}

.stats-button:hover { color: var(--bg); background: var(--text); }
.side > .stats-button { margin-top: -14px; } /* "reading stats" sits a little closer to the filters above it */

/* Stats pop-up: label / number pairs in two columns */
.stats {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 6px 24px;
  margin: 24px 0 36px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.stats dt { color: var(--muted); }

/* Books-per-year chart: small bars, tap one to show only that year */
.chart {
  display: flex;
  align-items: flex-end; /* bars grow upward from a shared baseline */
  gap: 2px;
  overflow-x: auto;      /* many years? scroll sideways */
  border-bottom: 1px solid var(--line);
}

.bar {
  flex: 1 0 26px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 0 0 4px;
  color: var(--muted);
  background: none;
  border: 0;
  font-size: 13px;
}

.bar:disabled { cursor: default; }
.bar-label { white-space: nowrap; }

/* --value is set from JavaScript: 1 for the busiest year, 0.5 for half as many books, etc. */
.bar-fill {
  width: 12px;
  height: calc(var(--value) * 100px);
  min-height: 1px;
  background: var(--year);
}

.bar:disabled .bar-fill { background: var(--line); }
.bar:hover:not(:disabled) .bar-fill,
.bar[aria-pressed="true"] .bar-fill { background: var(--accent); }
.bar[aria-pressed="true"] { color: var(--accent); }

/* Reading now: small cover beside the title */
.reading-list li + li { margin-top: 12px; }

.mini-book {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
  font-size: 17px;
}

.mini-book .cover { flex: none; width: 72px; } /* the Reading now cover */
.mini-title { display: block; line-height: 1.2; }
.mini-book:hover .mini-title { color: var(--accent); }

/* ---------- The books ---------- */

.shelf {
  display: flex;
  flex-direction: column;
  gap: 72px;
  padding-top: 6px;
}

/* One section per year: the year on the left, that year's books in a grid on the right */
.year {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 48px;
  align-items: flex-start;
}

.year-label { flex: 0 0 100px; display: flex; flex-direction: column; gap: 4px; }

.year-heading {
  color: var(--red);
  font-size: 40px;
  line-height: 1;
  font-variant-numeric: lining-nums;
}

/* When the books aren't split into years, the heading is words ("A to Z"), so it's smaller */
.year-heading.is-text { font-size: 26px; line-height: 1.1; }

.year-count { color: var(--muted); font-size: 16px; font-style: italic; }

/* On wide screens the year label stays in view while its books scroll past, then leaves with
   the last row of that year (sticky only works inside its own year section). Narrower than
   1150px the label drops above the books, where a sticky label would slide over the covers. */
@media (min-width: 1150px) {
  .year-label { position: sticky; top: 64px; }
}

/* The grid of books for one year. auto-fill fits as many cover-width columns as there's room for. */
.year-books {
  flex: 999 1 520px; /* takes all the room the year label doesn't need */
  min-width: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--cover-w), 1fr));
  gap: 48px 40px;
  padding-top: 32px;
  border-top: 1px solid var(--text);
}

/* One book: a button holding the cover and the text underneath */
.entry {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  max-width: var(--cover-w);
  padding: 0;
  background: none;
  border: 0;
  text-align: left;
}

.entry-info { display: flex; flex-direction: column; gap: 2px; }
.entry-title { font-size: 22px; line-height: 1.2; }
.entry:hover .entry-title { color: var(--accent); }

/* The rating stars (the class is still called "dots" from when they were circles) */
.dots {
  display: block;
  padding-top: 4px;
  color: var(--red); /* the stamp-logo red, on the shelf and on the book page */
  font-size: 14px;
  letter-spacing: 0.1em;
}

.dots.unrated { font-size: 12px; letter-spacing: 0.16em; }

.entry-date { padding-top: 8px; color: var(--faint); font-size: 14px; font-style: italic; }

/* ---------- Covers ---------- */

/* Made to look like a real book: barely rounded on the spine side, more on the page side,
   a soft shadow underneath, and a dark band down the left edge for the spine. */
.cover {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 2 / 3; /* every cover is the same shape, whatever the image size */
  overflow: hidden;
  background: var(--line);
  border-radius: 2px 5px 5px 2px;
  box-shadow: 0 2px 4px rgb(71 81 47 / 0.1), 0 18px 36px -12px rgb(71 81 47 / 0.3);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

/* ---- Worn look: covers that look like real, well-handled books ----
   Three layers:
   1. film colour: the cover image gets a hint of warmth, without losing its colour (.cover img, below)
   2. paper grain: fine noise over the whole cover (.cover::before)
   3. wear and tear: scuffs, creases and rubbed edges and corners (.cover::after)
   Both ::before and ::after are see-through boxes laid on top of the cover, and
   pointer-events: none lets clicks pass straight through them to the book. */

/* Film grain. The noise pattern is a tiny SVG "drawing" written right here in the CSS:
   feTurbulence makes random noise, feColorMatrix turns it grey. It repeats every 160px.
   mix-blend-mode: multiply only darkens, so the grain looks printed into the cover. */
.cover::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px;
  opacity: var(--film-grain);
  mix-blend-mode: multiply;
  pointer-events: none;
}

/* Wear and tear, drawn on top of the image (anything drawn on .cover itself would be hidden behind it):
   - scuffs, dust and creases from textures/wear.webp (made by scripts/make_wear_texture.py).
     The texture is 3 times the cover's size, and --wear-x / --wear-y (set by app.js from the Book Id)
     choose which part of it each book shows, so every book has its own scuffs.
   - rubbed corners: four small pale glows, where the colour of a cover wears off first
   - rubbed edges: a thin pale line and glow just inside the edge
   - the spine band down the left edge */
.cover::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(circle at 0 0, rgb(255 251 242 / 0.45), transparent 10px),
    radial-gradient(circle at 100% 0, rgb(255 251 242 / 0.45), transparent 10px),
    radial-gradient(circle at 0 100%, rgb(255 251 242 / 0.45), transparent 10px),
    radial-gradient(circle at 100% 100%, rgb(255 251 242 / 0.45), transparent 10px),
    url("textures/wear.webp") var(--wear-x, 50%) var(--wear-y, 50%) / 300% 300% no-repeat;
  box-shadow:
    inset 6px 0 0 rgb(71 81 47 / 0.18),     /* spine */
    inset 0 0 0 1px rgb(255 251 242 / 0.3), /* rubbed edge line */
    inset 0 0 8px rgb(255 251 242 / 0.25);  /* rubbed edge glow */
  opacity: var(--wear);
  pointer-events: none;
}

.cover img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover; /* fill the 2:3 box, cropping the edges instead of squashing */
  /* Film colour: just a hint of warmth (sepia), with colour and contrast nudged slightly UP
     so the covers stay bright and stand out from the off-white page. */
  filter: sepia(0.05) saturate(1.02) contrast(1.04);
}

/* Hover (mouse only): the book lifts slightly (in the grid the whole stack of pages lifts with it) */
@media (hover: hover) {
  .entry:hover .page-stack,
  .mini-book:hover .cover {
    transform: translateY(-4px);
  }

  .entry:hover .cover,
  .mini-book:hover .cover {
    box-shadow: 0 2px 4px rgb(71 81 47 / 0.1), 0 26px 40px -14px rgb(71 81 47 / 0.35);
  }
}

/* ---- Loose pages peeking out behind each cover ----
   Three "pages" sit behind the cover: .page-stack::before (nearest), ::after, and the .loose-page span
   (furthest back). Each is a box the size of the cover, pushed right so it sticks out by up to
   --overhang, and tilted by its own --tilt-… angle (all set per book by app.js).
   clip-path cuts each page into an imperfect shape: worn, rounded-off corners, a ragged right
   side and slightly uneven top and bottom edges, so they look like real paper, not rectangles. */
.page-stack {
  position: relative;
  display: block;
  isolation: isolate; /* keeps the pages' negative z-index inside this book, behind its cover */
  transition: transform 0.25s ease;
}

.page-stack::before,
.page-stack::after,
.loose-page {
  content: "";
  position: absolute;
  /* Plain old paper: a cream-to-yellowed gradient */
  background: linear-gradient(to right, #f1eadb, #e6dbc4);
  box-shadow: inset -1px 0 0 rgb(71 81 47 / 0.2), inset 0 -1px 0 rgb(71 81 47 / 0.12); /* page edges */
}

/* Nearest page: sticks out about half the overhang */
.page-stack::before {
  top: 2px;
  bottom: -1px;
  left: 6px;
  right: calc(var(--overhang, 12px) * -0.55);
  z-index: -1;
  transform: rotate(var(--tilt-1, 0deg));
  clip-path: polygon(0 0, 45% 0.3%, 96.5% 0, 99.2% 0.6%, 100% 2.5%, 99% 9%, 100% 18%, 98.6% 27%, 100% 39%, 99.2% 52%, 100% 64%, 98.8% 75%, 100% 87%, 99.6% 96.5%, 98.5% 99.3%, 96% 100%, 55% 99.6%, 0 100%);
}

/* Middle page: sticks out the full overhang, a little more yellowed */
.page-stack::after {
  top: 5px;
  bottom: 2px;
  left: 10px;
  right: calc(var(--overhang, 12px) * -1);
  z-index: -2;
  transform: rotate(var(--tilt-2, 0deg));
  background: linear-gradient(to right, #ebe2cf, #ddd0b4);
  clip-path: polygon(0 0.4%, 60% 0, 95% 0.5%, 98.6% 0.2%, 100% 3%, 98.4% 15%, 100% 24%, 99% 35%, 100% 47%, 98.5% 58%, 100% 70%, 99.1% 82%, 100% 91%, 98.9% 98%, 95.5% 100%, 40% 99.5%, 0 100%);
}

/* Furthest page: peeks out a little at the top and bottom as well */
.loose-page {
  top: -4px;
  bottom: -5px;
  left: 14px;
  right: calc(var(--overhang, 12px) * -0.8);
  z-index: -3;
  transform: rotate(var(--tilt-3, 0deg));
  background: linear-gradient(to right, #e9dfca, #d8c9ab);
  clip-path: polygon(0 1%, 50% 0, 95.5% 0.4%, 99.3% 1.8%, 100% 10%, 99% 21%, 100% 33%, 98.7% 44%, 100% 56%, 99.2% 68%, 100% 79%, 98.6% 90%, 99.5% 97.8%, 97% 99.6%, 93% 100%, 45% 99.2%, 0 99.6%);
}

/* No cover image: a coloured cover with the title at the top and the author at the bottom.
   The colours are set by app.js. */
.cover-placeholder {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  padding: 18px 16px 16px 22px;
}

.ph-title { font-size: 24px; line-height: 1.1; overflow-wrap: anywhere; }
.ph-author { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; opacity: 0.85; }

/* Small covers (Reading now) get small placeholder text */
.mini-book .cover-placeholder { padding: 6px 6px 6px 10px; }
.mini-book .ph-title { font-size: 11px; }
.mini-book .ph-author { display: none; }

/* ---------- Reading stats pop-up ---------- */

.dialog {
  width: min(720px, calc(100% - 32px));
  max-height: calc(100% - 32px);
  padding: 0; /* padding goes on .dialog-inner, so a click on the dialog element itself = a backdrop click */
  color: var(--text);
  background: var(--bg);
  border: 0;
  box-shadow: 0 30px 60px -20px rgb(71 81 47 / 0.35);
}

.dialog::backdrop { background: rgb(255 245 221 / 0.85); } /* the page colour, see-through */

.dialog-inner { position: relative; padding: 48px; }

.close {
  position: absolute;
  top: 20px;
  right: 24px;
  padding: 0;
  color: var(--muted);
  background: none;
  border: 0;
  font-size: 16px;
  font-style: italic;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.close:hover { color: var(--accent); }

.dialog-title { padding-right: 3em; font-size: 32px; line-height: 1.15; }


/* ---------- Book detail: full-screen pop-up ----------
   Laid out like the teabag pop-up on aqilazme.com: it fills the whole screen in the page colour
   and fades in. The big cover sits on the left and stays in view; the details scroll on the right. */

.detail {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  color: var(--text);
  background: var(--bg);
  border: 0;
  overflow-y: auto; /* the pop-up scrolls by itself if the review is long */
}

.detail::backdrop { background: transparent; } /* nothing shows behind a full-screen pop-up */

/* Entrance. The cover you clicked glides from the shelf into its place on the left (that's done
   in app.js, see openDetail), while the page colour fades in around it. Then the text drifts up,
   and the library card rises from behind the cover until its top peeks out. These CSS animations
   replay by themselves every time the pop-up opens. */
.detail[open] .detail-text { animation: detail-drift 0.6s ease 0.35s backwards; }
.detail[open] .detail-close { animation: detail-appear 0.4s ease 0.3s backwards; }

/* Leaving: app.js adds "is-leaving" when you close the pop-up.
   - The details and the × fade out.
   - The library card slides back down behind the book, the way it came out (if it was taken out,
     the book slides back up over it at the same time).
   Once the card is hidden behind the cover, app.js adds "is-gliding": the card is made invisible
   (so the leaving cover doesn't uncover it) and the cover glides back to the shelf.
   animation: none stops any entrance animation that might still be playing. */
.detail.is-leaving .detail-text,
.detail.is-leaving .detail-close {
  opacity: 0;
  animation: none;
  transition: opacity 0.2s ease;
}

.detail[open].is-leaving .library-card {
  animation: none;
  transform: translateY(var(--peek));
  transition: transform 0.25s cubic-bezier(0.4, 0, 0.6, 1);
}

/* [open] makes these rules win over the opening animation rules further down */
.detail[open].is-leaving .photo-slip {
  animation: photo-in 0.55s ease-in-out forwards; /* swings back up behind the book */
}

.detail.is-gliding .library-card,
.detail.is-gliding .photo-slip { opacity: 0; }
/* The card waits, invisible, until the cover has landed on top of it (the glide takes 0.55s);
   otherwise it would show by itself while the cover is still on its way. Then it rises into view. */
.detail[open] .library-card { animation: card-peek 0.7s cubic-bezier(0.2, 0.7, 0.2, 1) 0.55s backwards; }
/* The photo waits the same way, then slides out from behind the cover a moment after the card */
.detail[open] .photo-slip { animation: photo-out 0.9s cubic-bezier(0.3, 0.6, 0.3, 1) 0.7s backwards; }

@keyframes detail-drift { from { opacity: 0; transform: translateY(18px); } }
@keyframes detail-appear { from { opacity: 0; } }
/* 0%: invisible and fully behind the cover (this is also how it waits during the delay),
   1%: visible but still hidden behind the cover, 100%: risen to its resting peek */
/* The photo starts invisible and hidden behind the cover, then slides out to its place */
@keyframes photo-out {
  0% { opacity: 0; transform: var(--tucked); }
  1% { opacity: 1; transform: var(--tucked); }
  100% { transform: var(--slid); } /* swings down round its hinge */
}

@keyframes photo-in {
  0% { transform: var(--slid); }
  100% { transform: var(--tucked); } /* swings back up behind the book */
}

@keyframes card-peek {
  0% { opacity: 0; transform: translateY(var(--peek)); }
  1% { opacity: 1; transform: translateY(var(--peek)); }
}

/* Two columns: a 260px cover column and the text column, centred on the screen */
.detail-inner {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: 3.5rem;
  align-items: start;
  max-width: 1080px;
  margin: 0 auto;
  padding: 8vh 3rem 12vh;
}

/* Left column: the big cover, with the library card and photo tucked behind it.
   --peek is how much of the card shows above the cover. The column has that much space at the
   top, the card starts at the very top, and the cover sits over the rest of it. */
.detail-left {
  --peek: 42px;
  --cover-h: 390px; /* the cover's height (the column is 260px wide, covers are 2:3) */
  position: sticky; /* stays in view while a long review scrolls past */
  top: 8vh;
  padding-top: var(--peek);
  transition: padding-bottom 0.5s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.detail-cover {
  position: relative;
  z-index: 1; /* in front of the card */
  transform-origin: top left; /* the glide grows the cover from its top-left corner */
}

/* Card taken out (app.js adds .card-open when you click the card): the book slides down until about
   half the card shows. --card-h is the card's height (measured by app.js) and --card-out is how much
   of it to show. The column grows by the same amount at the bottom, so the book never covers
   anything below it (on phones, the text). */
.detail-left { --card-out: 0.5; }
.detail-cover > .page-stack { transition: transform 0.5s cubic-bezier(0.2, 0.7, 0.2, 1); }
.detail-left.card-open .detail-cover > .page-stack { transform: translateY(calc(var(--card-h, 300px) * var(--card-out) - var(--peek))); }
.detail-left.card-open { padding-bottom: calc(var(--card-h, 300px) * var(--card-out) - var(--peek)); }

/* Only the book picture moves down; its (invisible) box stays where it was, on top of the card.
   pointer-events: none lets the mouse go through that box to the card, so the card gets the
   pointer cursor and the clicks. The moved book itself can be clicked to tuck the card back in. */
.detail-left.card-open .detail-cover { pointer-events: none; }
.detail-left.card-open .detail-cover > .page-stack { pointer-events: auto; cursor: pointer; }

.detail-cover .cover {
  box-shadow: 0 2px 4px rgb(71 81 47 / 0.1), 0 40px 80px -20px rgb(71 81 47 / 0.45);
}

.detail-text { max-width: 620px; }

/* ---- The library card behind the book ----
   Based on an old library checkout card: a call number, typed AUTHOR and TITLE, and a table with
   a handwritten name and a stamped return date for every time I read the book (app.js fills it in).
   It sits behind the cover with only its top showing; hover to nudge it up, click to take it out. */
.library-card {
  position: absolute;
  z-index: 0; /* behind the cover */
  top: 0;
  left: 10px;
  right: 10px;
  padding: 12px 14px 16px;
  color: #2a2620;
  background: #f3ead3;
  border: 1px solid #d9c9a3;
  border-radius: 2px;
  box-shadow: 0 4px 12px -6px rgb(71 81 47 / 0.35);
  font-family: "Special Elite", "Courier New", monospace;
  font-size: 12px;
  line-height: 1.3;
  cursor: pointer;
  transition: transform 0.35s ease;
  /* Never taller than the cover: while it slides in and out behind the book (opening and closing
     the pop-up), it must stay completely hidden, or its bottom would poke out under the cover */
  max-height: var(--cover-h);
  overflow: hidden;
}

/* Hovering (or reaching it with the keyboard) nudges the card up, as if it's about to be pulled out */
@media (hover: hover) {
  .detail-left:not(.card-open) .library-card:hover { transform: translateY(-12px); }
}
.detail-left:not(.card-open) .library-card:focus-visible { transform: translateY(-12px); }

.card-callno { margin-bottom: 10px; white-space: pre-line; }

.card-line { display: flex; align-items: baseline; gap: 8px; margin-top: 6px; }

.card-label {
  flex: none;
  width: 42px;
  font-family: Georgia, serif;
  font-size: 9px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.card-value {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  border-bottom: 1px solid #2a2620;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.card-table {
  width: 100%;
  margin-top: 12px;
  border-collapse: collapse;
  table-layout: fixed;
}

.card-table th {
  padding: 4px 2px;
  border-top: 2px solid #2a2620;
  border-bottom: 1px solid #2a2620;
  font-family: Georgia, serif;
  font-size: 8.5px;
  font-weight: 500;
}

.card-table th:first-child { width: 56%; } /* the name column; the date column gets the rest */

.card-table td {
  height: 26px;
  padding: 0 2px;
  overflow: hidden;
  border-bottom: 1px solid #b9ad92;
  white-space: nowrap;
}

.card-table td + td,
.card-table th + th { border-left: 1px solid #2a2620; }

/* A rubber-stamped date: bold, slightly crooked, in old ink colours (set by app.js) */
.stamp {
  display: inline-block;
  font-family: "Courier New", monospace;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: -0.6px;
}

/* A handwritten name, in blue-black ink */
.hand { color: var(--hand-ink); font-family: "Homemade Apple", cursive; font-size: 11px; }

/* ---- A photo tucked in the book ----
   photos/<Book Id>.jpg, if there is one (app.js shows .photo-slip only when the photo loads).
   It's a little wider than the cover. Tucked in (--tucked), it's shrunk to fit hidden behind the
   cover; slid out (the transform below), it sits down and to the left, tilted, peeking out from
   under the book. It sits on a scrap of paper.
   --torn and --paper-torn are ragged outlines made by app.js from the Book Id, so every photo is
   torn differently. */
.photo-slip {
  position: absolute;
  z-index: 0; /* behind the cover, like the card */
  /* It swings out like a door. The hinge is its top-right corner, pinned near the bottom-right of
     the book (--hinge-inset from the book's right edge, --hinge-y down). Tucked, it's turned 90° so
     it stands upright, hidden behind the book; it swings down until it lies flat just beneath the
     book, reaching out past its lower-left corner, with only its top edge tucked under the book. */
  --photo-w: 275px;
  --hinge-inset: 18px;
  --hinge-y: calc(var(--peek) + var(--cover-h) - 30px);
  --tucked: rotate(90deg); /* upright, hidden behind the cover */
  --slid: rotate(-4deg);   /* swung down flat, slightly tilted (its resting place) */
  top: var(--hinge-y);
  left: calc(100% - var(--hinge-inset) - var(--photo-w)); /* so its top-right corner is the hinge */
  width: var(--photo-w);
  transform-origin: top right; /* the hinge */
  transform: var(--slid);
  filter: drop-shadow(0 6px 10px rgb(71 81 47 / 0.3)); /* a shadow that follows the torn edges */
}

/* The scrap of paper under the photo: a little bigger, tilted a bit differently, ragged edges */
.photo-paper {
  position: absolute;
  inset: -10px -14px -16px -8px;
  background: linear-gradient(160deg, #f3ecdc, #e2d5bb);
  clip-path: var(--paper-torn);
  transform: rotate(3deg);
}

/* The printed photo: a cream border, faded old colours, and torn edges */
.photo-print {
  position: relative;
  display: block;
  padding: 7px;
  background: #f5efe2;
  clip-path: var(--torn);
}

.photo-print img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  /* faded, warm, slightly washed-out: like a print left in the sun */
  filter: sepia(0.3) saturate(0.85) contrast(0.98) brightness(1.04);
}

/* Wear on top of the photo: the same scuffs as the book covers, plus darker edges (a vignette) */
.photo-print::after {
  content: "";
  position: absolute;
  inset: 7px;
  background: url("textures/wear.webp") 30% 60% / 250% 250% no-repeat;
  box-shadow: inset 0 0 22px rgb(60 45 25 / 0.35);
  opacity: 0.6;
  pointer-events: none;
}

/* A strip of tape over the top-left corner (the one that shows) */
.photo-tape {
  position: absolute;
  top: -9px;
  left: 4%;
  width: 70px;
  height: 22px;
  background: rgb(236 226 200 / 0.75);
  box-shadow: 0 1px 2px rgb(0 0 0 / 0.08);
  transform: rotate(-12deg);
  clip-path: polygon(0 8%, 6% 0, 14% 6%, 22% 0, 100% 3%, 96% 50%, 100% 97%, 90% 100%, 82% 94%, 0 100%, 4% 50%);
}

/* Room below the book for the photo, so it never covers anything underneath (on phones, the text) */
.detail-left.has-photo { padding-bottom: 225px; } /* room beneath the book for the photo */

/* Small capitals above the title, e.g. "Read in 2025" */
.detail-eyebrow {
  margin-bottom: 0.5rem;
  color: var(--red);
  font-size: 1.05rem;
  font-variant: small-caps;
  letter-spacing: 0.14em;
  text-transform: lowercase; /* small-caps turns lowercase letters into small capitals */
}

.detail-title { font-size: 3.2rem; line-height: 1.02; }

.detail-author { margin-top: 0.35rem; color: var(--accent); font-size: 1.25rem; font-style: italic; }

.detail-rating .dots { padding-top: 0.75rem; font-size: 17px; }

/* Finished / Times read / Pages / Published side by side: a hairline across the top, a thin
   upright line between each one, the label in red italics with its answer underneath */
.archive {
  display: flex;
  flex-wrap: wrap; /* on a narrow screen the last ones move to a second line instead of squashing */
  row-gap: 0.5rem;
  margin: 1.2rem 0 1rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--line);
}

.archive > div { flex: 1 1 auto; padding: 0 1.1rem; border-left: 1px solid var(--line); } /* the four share the full width evenly */
.archive > div:first-child { padding-left: 0; border-left: 0; }
.archive dt { color: var(--red); font-size: 1.1rem; font-style: italic; line-height: 1.3; }
.archive dd { margin-top: 0.05rem; color: var(--hand-ink); font-family: var(--hand), "EB Garamond", serif; font-size: 1.1rem; line-height: 1.2; } /* the handwriting font (--hand) */

/* "Impressions": a small red wax seal, then a large red italic heading with a thin red underline,
   centred. The review itself stays on the left. */
.notes { margin-top: 2rem; text-align: center; }

.notes-seal {
  display: block;
  width: 52px;
  height: auto;
  margin: 0 auto 0.6rem;
}

.notes-title {
  display: inline-block;
  margin-bottom: 1rem;
  padding-bottom: 0.2rem; /* the gap between the word and its underline */
  border-bottom: 1px solid var(--red);
  color: var(--red);
  font-size: 1.9rem;
  font-style: italic;
}

#detail-review {
  color: var(--hand-ink);
  text-align: left;
  font-family: var(--hand), "EB Garamond", serif;
  font-size: 1.15rem;
  line-height: 1.5;
  white-space: pre-line; /* turns the line breaks in the review into real line breaks */
}

#detail-review.empty { color: var(--faint); font-style: italic; }

/* A favourite passage, straight under the detail rows: the quote in plain text, with a big red
   opening quotation mark above it */
.excerpt { margin: 2.25rem 0 0; }

.excerpt-text {
  margin: 0;
  padding: 0;
  color: var(--text);
  font-size: 1.15rem; /* the same size as the text under Impressions */
  line-height: 1.6;
  white-space: pre-line; /* keeps the passage's line breaks */
}

/* Where the passage is from, e.g. "— p. 42" */
.excerpt-text::before {
  content: "“"; /* a curly opening quotation mark */
  display: block;
  margin-bottom: 0.1rem;
  color: var(--red);
  font-size: 3.4rem;
  font-style: normal;
  line-height: 0.75;
}

.excerpt-cite { margin-top: 0.6rem; color: var(--muted); font-size: 1rem; text-align: right; } /* sits at the bottom right */
.excerpt-cite:empty { display: none; }

/* Round close button in the top-right corner; it turns a quarter turn on hover */
.detail-close {
  position: fixed;
  top: 2rem;
  right: 2.4rem;
  z-index: 2;
  width: 42px;
  height: 42px;
  padding: 0;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--field-line);
  border-radius: 50%;
  font-size: 0; /* hide the "×" character: its position depends on the font, so the × is drawn below */
  transition: background-color 0.3s, color 0.3s, transform 0.3s;
}

/* The × itself: two thin lines crossing at the exact centre of the circle */
.detail-close::before,
.detail-close::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 1.5px;
  background: currentColor;
  transform: translate(-50%, -50%) rotate(45deg);
}

.detail-close::after { transform: translate(-50%, -50%) rotate(-45deg); }

.detail-close:hover { color: var(--bg); background: var(--accent); transform: rotate(90deg); }

/* ---------- Phones and small tablets ---------- */

@media (max-width: 860px) {
  /* One column: the left column's content on top, then the books */
  .page {
    grid-template-columns: minmax(0, 1fr);
    gap: 48px;
    padding-top: 40px;
  }

  /* On phones the left column sits above the books, so it scrolls away normally instead of sticking */
  .side { gap: 28px; position: static; max-height: none; margin-top: 0; padding: 0; overflow: visible; }

  /* The year sits above its books */
  .year-label { flex-basis: 100%; flex-direction: row; align-items: baseline; gap: 12px; }
  .year-heading { font-size: 34px; }
}

@media (max-width: 520px) {
  body { font-size: 17px; }
  h1 { font-size: 32px; }

  /* Two books side by side, slightly smaller covers */
  :root { --cover-w: 140px; }
  .year-books { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 36px 20px; }
  .entry { max-width: none; } /* fill the column on phones */
  .entry-title { font-size: 19px; }
  .ph-title { font-size: 18px; }
  .cover-placeholder { padding: 12px 10px 10px 16px; }
  .ph-author { font-size: 10px; }

  .dialog-inner { padding: 48px 20px 28px; }
  .dialog-title { font-size: 26px; padding-right: 0; }
}

/* Book detail on phones and small tablets: one column, the cover centred above the text */
@media (max-width: 760px) {
  .detail-inner { grid-template-columns: minmax(0, 1fr); gap: 2rem; padding: 6vh 1.25rem 10vh; }
  .detail-left { position: relative; top: 0; width: 160px; justify-self: center; } /* relative, not sticky: the card still positions itself inside the column */
  .detail-left.has-photo { padding-bottom: 135px; }
  .photo-slip { --photo-w: 170px; --hinge-inset: 20px; } /* smaller, so the book doesn't fill the whole first screen */
  .detail-left { --cover-h: 240px; } /* the column is 160px wide on phones */
  .detail-title { font-size: 2.3rem; }
  .notes-title { font-size: 1.7rem; }

  /* A more compact library card, so all of it (my name and the dates too) fits behind the smaller
     cover; taking it out slides the book further down, so the name and date rows show */
  .detail-left { --card-out: 0.58; } /* just far enough to show the first row (my name and date) */
  .detail-left.has-photo.card-open { padding-bottom: max(135px, calc(var(--card-h, 300px) * var(--card-out) - var(--peek))); } /* room for whichever is lower: the photo or the moved book */
  .library-card { left: 6px; right: 6px; padding: 8px 9px 10px; font-size: 10.5px; }
  .card-callno { margin-bottom: 6px; }
  .card-line { margin-top: 3px; gap: 5px; }
  .card-label { width: 34px; font-size: 8px; }
  .card-table { margin-top: 8px; }
  .card-table th { padding: 3px 1px; font-size: 7.5px; }
  .card-table td { height: 21px; }
  .hand { font-size: 9.5px; }
  .stamp { font-size: 8px; }

  /* Finished / Times read / Pages / Published as a neat 2 x 2 grid: two equal columns, with the
     thin upright line only between the left and right column */
  .archive { display: grid; grid-template-columns: 1fr 1fr; row-gap: 0.7rem; }
  .archive > div { padding: 0 0 0 1rem; }
  .archive > div:nth-child(odd) { padding-left: 0; border-left: 0; }
  .archive dt { font-size: 1rem; }
  .archive dd { font-size: 1.05rem; }
  .detail-close { top: 1rem; right: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ---------- Edit mode (only visible in a browser unlocked for editing) ---------- */
.editor { margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px dashed var(--field-line); }

.edit-form { display: flex; flex-direction: column; gap: 1.1rem; }
.edit-field { display: flex; flex-direction: column; gap: 6px; }
.edit-field em { text-transform: none; letter-spacing: 0; color: var(--faint); }

.edit-form textarea {
  width: 100%;
  padding: 10px 12px;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--field-line);
  border-radius: 4px;
  font: inherit;
  font-size: 1.05rem;
  line-height: 1.5;
  resize: vertical;
}

.edit-form textarea:focus { outline: none; border-color: var(--accent); }
.edit-form input[type="file"] { font: inherit; font-size: 0.95rem; color: var(--author); }
.edit-check { display: flex; align-items: center; gap: 8px; color: var(--author); font-size: 0.95rem; }

.edit-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 1.5rem; }
.edit-lock { margin-left: auto; font-size: 0.9rem; color: var(--faint); }
.edit-status { color: var(--author); font-style: italic; font-size: 0.95rem; }

.unlock-form { display: flex; flex-direction: column; align-items: flex-start; gap: 1rem; }
.unlock-text { color: var(--text-soft); }

.unlock-form input {
  width: 100%;
  padding: 8px 0;
  color: var(--text);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--field-line);
  font: inherit;
}

.unlock-form input:focus { outline: none; border-bottom-color: var(--accent); }
