/* --------------------------------------------------------------------
   library.css — the library screen and its three views.

   Shelf, grid and list share one card markup and differ only in layout, so
   switching views never rebuilds the DOM from scratch — library.js swaps a
   class on the container.
   -------------------------------------------------------------------- */

/* ---------------------------------------------------------------- */
/* Chrome                                                            */
/* ---------------------------------------------------------------- */

/*
 * Three columns, not a flex row.
 *
 * The search box is meant to sit in the middle of the bar, and `margin: auto`
 * on it could not do that while the actions on the right also claimed the
 * spare space — the two autos split what was left and the box landed
 * left-of-centre. A grid whose outer columns are equal puts the middle column
 * in the middle whatever is in the other two, which is the only arrangement
 * that stays centred as the wordmark or the buttons change width.
 */
/*
 * Wordmark, then the search box beside it, then the buttons pushed to the far
 * end — the order of nearly every app with a name and a search box.
 *
 * It was three equal columns with the box centred in the middle one. Centred,
 * with the text centred inside it too, it looked like a heading rather than a
 * field you type into; on the left, smaller, with a magnifier and the text
 * starting from the left edge, it looks like what it is.
 */
.lib-bar {
  flex: none;
  display: grid;
  grid-template-columns: auto minmax(0, 300px) 1fr;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--bar-h);
  padding: var(--sp-2) var(--sp-4);
  border-bottom: 1px solid var(--border);
  background: var(--bg-2);
}
/*
 * The wordmark: `taper`, lowercase, with the flame standing on the t.
 *
 * The flame is positioned in `em`, against the wordmark's own font size, so
 * the two scale together and stay locked at any size. Nothing is erased to
 * make room — the space above a lowercase t's ascender is empty in every
 * serif worth using, which is the reason the name and the mark fit at all.
 *
 * Measured against Literata at 1.2rem: the ascender ends at about 0.72em above
 * the baseline, and the flame's base sits there. If the typeface changes, this
 * is the number to re-measure.
 */
.wordmark {
  /* The name of the thing, and the only place the mark appears at rest. It was
     sized like a label; it is the masthead. */
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: var(--wordmark-tracking);
  flex: none;
  position: relative;
  display: inline-block;
  text-transform: lowercase;
  line-height: 1;
}
.wordmark-word { color: var(--fg); }

/*
 * These four numbers are measured, not guessed.
 *
 * Rendered Literata 600 to a canvas and read the glyph back: the t's stem
 * centre sits 0.185em from the start of the word, and its ascender tips out
 * 0.675em above the baseline. With `line-height: 1` the baseline falls 0.932em
 * below the top of the box, which puts the tip at 0.257em from the top.
 *
 * A flame 0.37em wide therefore starts at left: 0 and is exactly centred on
 * the stem — and sitting its base on the tip puts its top at 0.257 − 0.37.
 *
 * If the typeface changes, re-measure. An eyeballed offset looks fine at one
 * size and drifts at every other, because the error scales with the type.
 */
.wordmark-flame {
  position: absolute;
  left: 0;
  top: -0.113em;
  width: 0.37em;
  height: 0.37em;
  pointer-events: none;
}
.wordmark-flame .flame { display: block; width: 100%; height: 100%; }
/*
 * The search box, beside the wordmark with a clear gap between them.
 *
 * It sat in the middle of the bar once because, pressed against the mark, the
 * two read as one thing. The gap is what separates them now — a field set a
 * little apart, not a word that runs on into a box.
 */
.lib-search { min-width: 0; margin-inline-start: var(--sp-4); }
.lib-search .input { min-height: 32px; font-size: var(--step--1); }
/* `justify-content`, not `margin-left: auto` — the grid already decides where
   this column is, and an auto margin inside it would fight the centring. */
.lib-bar-actions { display: flex; align-items: center; gap: var(--sp-2); justify-content: flex-end; }

.lib-body { flex: 1; display: flex; min-height: 0; }

.lib-rail {
  flex: none;
  width: var(--rail-w);
  border-right: 1px solid var(--border);
  padding: var(--sp-3) var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  overflow-y: auto;
}
.rail-list { display: flex; flex-direction: column; gap: 1px; }
.rail-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  min-height: var(--tap);
  padding: 0 var(--sp-2);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  text-align: left;
  font-size: var(--step--1);
}
.rail-item:hover { background: var(--bg-3); }
.rail-item[aria-current="true"] { background: var(--bg-3); font-weight: 600; }
.rail-item .label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-item .count { color: var(--fg-dim); font-variant-numeric: tabular-nums; font-size: var(--step--2); }
.rail-item .edit { opacity: 0; flex: none; }
.rail-item:hover .edit, .rail-item:focus-within .edit { opacity: 1; }
.rail-sep { height: 1px; background: var(--border); margin: var(--sp-2) var(--sp-2); }
.rail-add {
  margin-top: var(--sp-2);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--fg-dim);
  min-height: var(--tap);
  font-size: var(--step--1);
}
.rail-add:hover { color: var(--fg); border-color: var(--fg-dim); }

.lib-main { flex: 1; min-width: 0; overflow-y: auto; padding: var(--sp-4); position: relative; }

.lib-filters {
  display: flex;
  align-items: flex-end;
  gap: var(--sp-3);
  flex-wrap: wrap;
  margin-bottom: var(--sp-4);
}
.lib-filters .field { flex: 0 1 auto; min-width: 120px; }
.lib-count { margin-left: auto; color: var(--fg-dim); font-size: var(--step--1); }
/* Beside the count, at the end of the row (5 Oct). */
.lib-view-switch { flex: none; }

/* Brother Davis's button lives in the bar, except on a phone, where the bar
   is full and it moves down beside the count. See index.html. */
.lib-davis-narrow { display: none; flex: none; }
@media (max-width: 480px) {
  #library-davis { display: none; }
  .lib-davis-narrow { display: inline-flex; }
}

/* ---------------------------------------------------------------- */
/* Drop target                                                       */
/* ---------------------------------------------------------------- */

.drop-hint {
  position: absolute;
  inset: var(--sp-2);
  z-index: 20;
  display: none;
  place-items: center;
  border: 2px dashed var(--accent);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg) 88%, var(--accent));
  font-size: var(--step-1);
  pointer-events: none;
}
.lib-main.is-dragover .drop-hint,
#screen-library.is-dragover .drop-hint { display: grid; }

.lib-empty { text-align: center; padding: var(--sp-6) var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2); }
.lib-empty-title { font-size: var(--step-2); font-weight: 600; }

/* ---------------------------------------------------------------- */
/* Cover art                                                         */
/* ---------------------------------------------------------------- */

.cover {
  position: relative;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-3);
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.18);
}
.cover img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Painted from the book id, so a given book is always the same colour. */
.cover-fallback {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 10% 9%;
  background: linear-gradient(160deg, var(--ph-bg), var(--ph-alt));
  color: var(--ph-fg);
}
.cover-fallback .t { font-size: 0.82rem; line-height: 1.25; font-weight: 600; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; }
.cover-fallback .a { font-size: 0.68rem; opacity: 0.78; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

/* ---------------------------------------------------------------- */
/* Shared card pieces                                                */
/* ---------------------------------------------------------------- */

.card { position: relative; background: transparent; border: 0; color: inherit; text-align: start; padding: 0; }

/* Stretched over the whole card so the card is one large target, while the
   card's own contents stay ordinary block elements. */
.card-hit { position: absolute; inset: 0; z-index: 1; border: 0; padding: 0; background: transparent; border-radius: var(--radius-sm); }
.card-hit:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.card-title { font-size: var(--step--1); font-weight: 600; line-height: 1.3; overflow-wrap: anywhere; }
.card-author { font-size: var(--step--2); color: var(--fg-dim); }
.card-meta { font-size: var(--step--2); color: var(--fg-dim); font-variant-numeric: tabular-nums; }

.progress { height: 3px; border-radius: 2px; background: var(--bg-3); overflow: hidden; }
.progress > i { display: block; height: 100%; background: var(--accent); }

.badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: var(--step--2);
  padding: 1px 5px;
  border-radius: 3px;
  border: 1px solid var(--border);
  color: var(--fg-dim);
  white-space: nowrap;
}
.badge-verified { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 45%, var(--border)); }
.badge-probable { color: var(--fg-dim); }
.badge-suspicious { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--border)); }
.badge-note { color: var(--fg-dim); }
/* Above the card's own click area, so it can be pressed. See `badge` in library.js. */
.badge-button {
  position: relative;
  z-index: 2;
  background: transparent;
  font: inherit;
  font-size: var(--step--2);
  cursor: pointer;
}

.card-menu {
  position: absolute;
  top: var(--sp-1);
  right: var(--sp-1);
  z-index: 2;
  width: 26px;
  height: 26px;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg-2);
  color: var(--fg);
  opacity: 0;
  line-height: 1;
}
/* `.row:focus-within` too: in the list view a ⋯ reached with the Tab key was
   focused but invisible, focus ring and all. */
.card:hover .card-menu, .card:focus-within .card-menu,
.row:hover .card-menu, .row:focus-within .card-menu { opacity: 1; }
[data-epaper] .card-menu { opacity: 1; }

/* ---------------------------------------------------------------- */
/* Grid view                                                         */
/* ---------------------------------------------------------------- */

.lib-view.view-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: var(--sp-5) var(--sp-4);
}
.view-grid .card { display: flex; flex-direction: column; gap: var(--sp-2); }
.view-grid .cover { width: 100%; }
.view-grid .card:hover .cover { box-shadow: 0 4px 14px rgb(0 0 0 / 0.25); }
.view-grid .card-body { display: flex; flex-direction: column; gap: 3px; }
/* A badge is a small tag, not a row. In the card's column it was stretched to
   the full width of the card and read as an empty text box. */
.view-grid .card-body .badge { align-self: flex-start; }

/* ---------------------------------------------------------------- */
/* List view                                                         */
/* ---------------------------------------------------------------- */

.lib-view.view-list { display: flex; flex-direction: column; }
.view-list .row {
  position: relative;
  display: grid;
  grid-template-columns: 34px minmax(0, 2.2fr) minmax(0, 1.4fr) 90px 110px;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--border);
  width: 100%;
  border-left: 0; border-right: 0; border-top: 0;
  background: transparent;
  color: inherit;
  text-align: left;
}
.view-list .row:hover { background: var(--bg-2); }
.view-list .cover { border-radius: 2px; }
.view-list .card-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.view-list .card-author { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.view-list .row-progress { display: flex; flex-direction: column; gap: 3px; }

@media (max-width: 720px) {
  .view-list .row { grid-template-columns: 30px minmax(0, 1fr) 64px; }
  .view-list .row .card-author { display: none; }
  .view-list .row .card-meta { display: none; }
}

/* ---------------------------------------------------------------- */
/* Shelf view                                                        */
/* ---------------------------------------------------------------- */

.lib-view.view-shelf { display: flex; flex-direction: column; gap: var(--sp-6); }

.shelf {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  padding: 0 var(--sp-2) var(--sp-2);
  border-bottom: 7px solid color-mix(in srgb, var(--fg) 22%, var(--bg-3));
  border-radius: 0 0 3px 3px;
  box-shadow: 0 7px 12px -8px rgb(0 0 0 / 0.45);
  overflow-x: auto;
  min-height: 210px;
}

/* Spines vary in width with the book's length, which is the one cue a real
   shelf gives you that a grid of identical tiles cannot. */
.spine {
  position: relative;
  flex: none;
  width: var(--spine-w, 34px);
  height: var(--spine-h, 190px);
  border: 0;
  border-radius: 2px 2px 0 0;
  background: linear-gradient(95deg, var(--ph-alt), var(--ph-bg) 18%, var(--ph-bg) 82%, var(--ph-alt));
  color: var(--ph-fg);
  padding: var(--sp-2) 3px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  box-shadow: inset -2px 0 4px rgb(0 0 0 / 0.25);
}
.spine:hover { transform: translateY(-6px); }
.spine .spine-text {
  writing-mode: vertical-rl;
  text-orientation: mixed;
  font-size: 0.72rem;
  font-weight: 600;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-height: 100%;
}
.spine .spine-author { font-weight: 400; opacity: 0.75; }
.spine .spine-progress {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background: rgb(255 255 255 / 0.22);
}
.spine .spine-progress > i { display: block; height: 100%; background: currentColor; opacity: 0.9; }

@media (prefers-reduced-motion: no-preference) {
  .spine { transition: transform var(--speed) var(--ease); }
  .view-grid .cover { transition: box-shadow var(--speed) var(--ease); }
}
[data-epaper] .spine:hover { transform: none; outline: 2px solid var(--fg); }

/* ---------------------------------------------------------------- */
/* Book details modal                                                */
/* ---------------------------------------------------------------- */

.detail { display: grid; grid-template-columns: 96px 1fr; gap: var(--sp-4); }
.detail dl { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--step--1); }
.detail dt { color: var(--fg-dim); }
.detail dd { margin: 0; }
.detail-notes { font-size: var(--step--1); color: var(--danger); display: flex; flex-direction: column; gap: var(--sp-1); }
.menu-list { display: flex; flex-direction: column; gap: 1px; }

/*
 * On a narrow window the rail goes, and collections come with the filters
 * instead — a select that says which collection is showing, with "New
 * collection…" and "Rename or delete…" in it. The rail used to go and take
 * collections with it: on a phone, or a laptop window at half the screen,
 * there was no way to see one, make one, or even tell that one was selected.
 */
.lib-collection { display: none; }

@media (max-width: 720px) {
  .lib-rail { display: none; }
  .lib-collection { display: flex; }
  /* Two to a row rather than one each, which pushed the first book a whole
     screen down on a phone. */
  .lib-filters .field { flex: 1 1 140px; min-width: 0; }
  /* Wordmark and buttons on one line, the search box on its own below. */
  .lib-bar { grid-template-columns: auto 1fr; }
  /* The buttons wrap rather than run off the edge. With "Practise · 3 due"
     showing they needed about 380 pixels, and on a phone the ⋯ menu — the only
     way into the menu — was pushed past the right edge of the screen. */
  .lib-bar-actions { flex-wrap: wrap; min-width: 0; row-gap: var(--sp-2); }
  .lib-search { order: 3; grid-column: 1 / -1; margin-inline-start: 0; }
  .detail { grid-template-columns: 1fr; }
}
