/* ===========================================================================
   FRONT PAGE - the components only this page prints.

   Loaded from pages/home.html's extra_css block, not from main.css, for the
   same reason features/discovery.css is: nothing here appears on any other page
   of the site, and a rule that ships on every page to be used on one is bytes
   every visitor pays for once and reads never.

   WHAT THIS FILE MAY AND MAY NOT DO. main.css owns the chrome, the layout and
   the shared components; its index at the top of that file is the contract, and
   nothing below re-states a selector from it. The three additions here are the
   captioned cover rail, the rail's history and most-popular blocks, and the
   genre block's sort tabs - components that did not exist, rather than
   components that existed and are being overridden.

   The two SIZE overrides that are not new components - the rail's cover width
   and .carousel-item's positioning context - are scoped under .carousel-rail so
   they cannot reach a carousel printed anywhere else.

   Every colour is a token. There is no orange and no teal written in this file:
   a theme owns which band is which hue, and a hardcoded one would make two
   sites on two presets look the same where they are meant to look unrelated
   (apps/publisher/tests/test_theming.py).
   =========================================================================== */


/* =========================================================== THE GRID === */

/* THE UPDATE GRID IS A TABLE OF ROWS, NOT A ROW OF EQUAL BOXES. Its cells are
   the same height by default - that is what a grid does - and on this page one
   of them is an ad: a 300x250 rectangle is the grid's first cell, so a stretched
   row makes every card beside it 250px tall and every cover in that row stretch
   with it. The card is exactly as tall as a cover, a title and three chapter
   lines, and it says so here.

   Scoped to the front page rather than declared on .card-grid, because it is the
   ad slot in the grid that makes this page different from the other three that
   print the same component. */
.page-home .card-grid { align-items: start; }


/* ====================================================== THE COVER RAIL === */

/* The wrapper the prev/next arrows are positioned against. It cannot be the
   scroller itself: an absolutely positioned child of an overflow box scrolls
   away with the content it exists to drive. js/site.js appends the two buttons
   here and reuses the continue-rail's own .crail__arrow - the two rails are the
   same object (a horizontal row of covers that may overflow) and giving one of
   them a second set of arrow styles would be two sources of truth for one
   control. */
.carousel-rail { position: relative; }

/* THE COVER SIZE IS THE TARGET, AND THE COUNT IS WHATEVER THE COLUMN HOLDS.
   The reference fits about eight of these across, but it is showing them the
   full width of the page; here the rail lives in the main column beside the
   three rail blocks, and eight cells in 764px is an 84px cover with a caption
   over two thirds of it - a thumbnail, not the dense-but-legible row the count
   was standing in for. The cover keeps the reference's size (~138px, which is
   also the width the cover ratio was cut for) and the rail shows five or six of
   them with the arrows reaching the rest, which is what the arrows are for.

   A percentage of the RAIL and not of the viewport: this column stops growing
   at the container's cap and the viewport does not, so a vw clamp goes on
   widening the covers after the box holding them has stopped - six cells at
   1280 would become four at 1600. The floor is the phone case, where the rail
   simply scrolls.

   Still a clamp and still no breakpoint - the count falls out of the width. */
.carousel-rail .carousel-item {
  flex: 0 0 clamp(112px, 18%, 148px);
  /* The caption is painted INSIDE the cover, so the item is what it is measured
     against. Without this it would escape to the nearest positioned ancestor,
     which is the whole rail. */
  position: relative;
}

/* THE CAPTION IS OVER THE ARTWORK, NOT UNDER IT, and that is a layout decision
   before it is a visual one. Under the cover, a title that wraps to two lines
   and one that does not give the row two heights - so the rail either reserves
   the taller and prints air under half its cards, or it jitters as the fonts
   land. Painted inside a fixed-ratio cover it costs no layout at all and the
   row is exactly as tall as a cover.

   It is a sibling of the cover link and not a child of it: both lines in here
   are anchors with different destinations, and an anchor inside an anchor is
   not a tree any parser will build.

   z-index 2 puts it over the cover's own hover transform and under .badges,
   which main.css already paints at the top of the stack. */
.carousel-cap {
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  left: 0;
  padding: var(--space-4) var(--space-2) var(--space-2);
  position: absolute;
  right: 0;
  z-index: 2;
}
/* The scrim as a pseudo element rather than a background on the box itself, so
   the ramp can start above the text and the text keeps its own padding. Same
   token and same reasoning as discovery.css's .trend-card__cover::before: two
   layers, the lower one composited twice at the bottom edge, faded out by a
   third of the way up so the cover is still a cover. A scrim is painted over
   artwork and has no contrast ratio of its own to hold, which is why the ink
   below is declared in a rule that states no background. */
.carousel-cap::before {
  background-image: var(--grad-cover-scrim);
  bottom: 0;
  content: "";
  left: 0;
  pointer-events: none;
  position: absolute;
  right: 0;
  top: -24px;
  z-index: -1;
}

/* Two lines maximum on the title and one on the chapter: the cap may not grow
   past the third of the cover it is allowed to cover, or it stops being a
   caption and starts being a card. */
.carousel-cap .carousel-title {
  -webkit-box-orient: vertical;
  display: -webkit-box;
  font-size: var(--fs-meta);
  -webkit-line-clamp: 2;
  line-height: var(--lh-body);
  margin: 0;
  overflow: hidden;
}
.carousel-cap .carousel-title a,
.carousel-cap .carousel-title a:visited,
.carousel-cap .carousel-chapter,
.carousel-cap .carousel-chapter:visited {
  color: var(--badge-ink);
  /* Not a theme colour and not a token: it is the shadow that keeps a light
     title legible on the one frame of artwork the scrim did not quite cover,
     and it is black in every theme because artwork is. */
  text-shadow: 0 1px 2px rgb(0 0 0 / 55%);
}
.carousel-cap .carousel-title a:hover,
.carousel-cap .carousel-chapter:hover {
  color: var(--badge-ink);
  text-decoration: underline;
}
.carousel-cap .carousel-chapter {
  font-size: var(--fs-meta);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* ================================================= RAIL: READING HISTORY === */

/* THE ONE PER-VISITOR BLOCK ON A PAGE NOBODY MAY PERSONALISE. The markup ships
   empty and hidden for everybody (components/sidebar_history.html) and
   js/site.js fills it from the same localStorage record the continue-reading
   rail is built from. Nothing here is served differently to anyone.

   AND IT MAY NOT SHIFT THE PAGE WHEN IT ARRIVES. base.html's pre-paint script
   has already stamped html[data-crail] from the mere presence of that key -
   before the first frame, which is the only place a box can be reserved for
   free - so the two rules below open the block at the height its rows come to
   while the document is still painting. site.js takes the stamp back off if the
   stored rows turn out not to survive its checks, and the reservation goes with
   it in the same task. This is exactly what interactions.css does for the
   horizontal rail; the only difference is which shape is being held open. */
/* BOTH elements, because both ship hidden and each hides a different half of
   the box. The wrapper carries the heading band and the section carries the
   list; reserving only the wrapper would hold 29px open for a 269px block and
   turn one shift into a smaller one. The empty panel is painted for the few
   frames between the first paint and js/site.js - which is exactly the trade
   features/interactions.css makes for the horizontal rail, and it is only ever
   made for a visitor who HAS history. */
html[data-crail] .hist-wrap[hidden],
html[data-crail] .hist[hidden] { display: block !important; }
/* Three rows, which is what site.js puts in this block. --hist-row is the row
   height written once and read twice - by the row itself, so a record with no
   cover in it is not a shorter row, and by the reservation, so the box that is
   held open is the box that gets filled. A local, not a token: it is a
   measurement of this component and no theme has an opinion about it. */
html[data-crail] .hist__list { min-height: calc(3 * var(--hist-row)); }

/* Measured against the reference, whose history thumbnails are markedly
   smaller than these were: this block is a list of NAMES with a picture as a
   cue, not a gallery, and at 44x70 it was taking a third of the rail for three
   rows. The row height comes down with it so the block does not keep the space
   it no longer needs. */
.hist { --hist-row: 62px; }
.hist__list { display: flex; flex-direction: column; }
.hist__row { border-top: 1px solid var(--border); }
.hist__row:first-child { border-top: 0; }

.hist__link {
  align-items: flex-start;
  min-height: var(--hist-row);
  border-radius: var(--r-card);
  display: flex;
  gap: var(--space-3);
  padding: var(--space-2);
  transition: background var(--t-fast);
}
.hist__link:hover { background: var(--surface-alt); text-decoration: none; }

/* The thumbnail is small and its box is reserved from the ratio token rather
   than from the file, so a row is the same height before and after the image
   decodes - and the same height for a row whose record never carried a cover. */
.hist__thumb {
  aspect-ratio: var(--cover-ratio);
  background: var(--surface-inset);
  border-radius: var(--r-card);
  flex: 0 0 34px;
  object-fit: cover;
}

.hist__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.hist__name {
  -webkit-box-orient: vertical;
  color: var(--text);
  display: -webkit-box;
  font-size: var(--fs-meta);
  font-weight: 600;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.hist__link:hover .hist__name { color: var(--link-hover); }
.hist__chapter {
  color: var(--link-chapter);
  font-size: var(--fs-meta);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}


/* =================================================== RAIL: MOST POPULAR === */

/* THE LADDER IS THE RANKING. Nine tinted steps, hot at the top and cool at the
   bottom, and then the plain plate every other ranked row on this site sits on.
   The tint is set from :nth-child and never from a class, because the position
   is already carried by the <ol> - a class per row would be a second source of
   truth for the same fact, and a row lifted out of an ordered list would carry
   a rank it no longer has.

   --rank-1 ... --rank-9 are not nine hexes anybody tuned. Every preset mixes
   them from three of its OWN chips (see the token files), so the ladder is each
   site's palette and it cannot fail the contrast test: a color-mix in sRGB has
   a luminance between its stops, and every stop is a --chip-* that already
   clears 4.5:1 under --chip-ink.

   ON A TINTED ROW, EVERY INK IS --rank-ink - links included. The link inks are
   tuned against --surface and none of them survives being painted on a
   saturated plate. A tinted row is a chip, and a chip's ink is the chip's. */
.pop__list { display: flex; flex-direction: column; gap: 2px; }

.pop__row {
  align-items: baseline;
  background: var(--rank-10);
  border-radius: var(--r-card);
  color: var(--text);
  display: flex;
  gap: var(--space-2);
  padding: var(--space-2);
}
.pop__row:nth-child(1) { background: var(--rank-1); color: var(--rank-ink); }
.pop__row:nth-child(2) { background: var(--rank-2); color: var(--rank-ink); }
.pop__row:nth-child(3) { background: var(--rank-3); color: var(--rank-ink); }
.pop__row:nth-child(4) { background: var(--rank-4); color: var(--rank-ink); }
.pop__row:nth-child(5) { background: var(--rank-5); color: var(--rank-ink); }
.pop__row:nth-child(6) { background: var(--rank-6); color: var(--rank-ink); }
.pop__row:nth-child(7) { background: var(--rank-7); color: var(--rank-ink); }
.pop__row:nth-child(8) { background: var(--rank-8); color: var(--rank-ink); }
.pop__row:nth-child(9) { background: var(--rank-9); color: var(--rank-ink); }

/* The counter. Tabular figures so ten rows of numbers line up as a column
   rather than as ten different widths. */
.pop__num {
  flex: 0 0 auto;
  font-size: var(--fs-meta);
  font-variant-numeric: tabular-nums;
  font-weight: 800;
  min-width: 1.25em;
  opacity: .8;
  text-align: center;
}

/* ONE LINE, like the reference: title, a dash, the chapter. Stacked, each row
   was two lines tall and ten of them ran the sidebar well past the update grid
   beside it. Wrapping is off and the title is the element that gives way, so a
   long name truncates and the chapter number - the part that tells a returning
   reader whether there is anything new - always survives. */
.pop__body {
  align-items: baseline;
  display: flex;
  flex-direction: row;
  gap: .35em;
  min-width: 0;
}

/* The separator is generated, so it never lands in either link's accessible
   name - a screen reader announces two links, not "Title dash Chapter 12". */
.pop__chapter::before {
  content: '-';
  margin-right: .35em;
  opacity: .7;
}
/* Both links take the row's ink, which is --rank-ink on a tinted row and --text
   on the plate. The chapter gets its own link colour back only on the plate,
   below, where it is the ink that colour was measured against. */
.pop__title,
.pop__chapter {
  color: inherit;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pop__title { flex: 0 1 auto; font-size: var(--fs-meta); font-weight: 600; min-width: 0; }
.pop__chapter { flex: 0 0 auto; font-size: var(--fs-meta); opacity: .88; }
.pop__row:nth-child(n + 10) .pop__chapter { color: var(--link-chapter); opacity: 1; }
.pop__title:hover,
.pop__chapter:hover { text-decoration: underline; }


/* ========================================================= RAIL: GENRES === */

/* The sort tabs over the genre table. They ship HIDDEN and js/site.js reveals
   them: they do nothing without it, and a control that does nothing is worse
   than no control. The list under them is already in the order the first tab
   names, so a reader with no JavaScript sees the same alphabetical table a
   crawler does and misses only a convenience. */
.gsort { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-bottom: var(--space-2); }
.gsort__tab {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--text-2);
  cursor: pointer;
  font-size: var(--fs-meta);
  font-weight: 600;
  line-height: 1;
  padding: var(--space-2) var(--space-3);
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.gsort__tab:hover { border-color: var(--border-strong); color: var(--text); }
.gsort__tab.is-on {
  background: var(--chip-genres);
  border-color: var(--chip-genres);
  color: var(--chip-ink);
}
