/* Home page. Figma node 3:3 provides the desktop composition; the hero is a
   band lower than the category tiles, with a pair of covers standing on its
   bottom edge, so it leads the page without outweighing the grid under it. */

.home-page .page-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xl);
  padding-block: var(--space-2xl) var(--space-5xl);
}

/* ---- Hero ---- */

/* Every slide, and the controls, share one grid cell ("stage"). The stage is
   therefore as tall as the tallest slide at the current width, so switching
   banners never changes the section's height, with no script measuring
   anything. Inactive slides are `inert` and invisible but keep their box. */
.home-hero {
  --hero-slide-padding-block: var(--space-lg);
  --hero-slide-padding-bottom: var(--hero-slide-padding-block);

  position: relative;
  display: grid;
  grid-template-areas: "stage";
  overflow: hidden;
}

/* A sideways swipe turns the banner (hero-banners.js); vertical scrolling and
   pinch zoom stay the browser's. */
.home-hero--navigable {
  touch-action: pan-y pinch-zoom;
}

/* Copy on the left, the cover pair on the right. The gap between them is
   the room the pair's ornaments take on their left, so they never reach the
   copy. `isolation` keeps the background picture's negative layer inside
   the slide. */
.home-hero__slide {
  grid-area: stage;
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: calc(var(--hero-cover-width) * var(--hero-ornaments-start));
  min-height: var(--hero-height);
  padding: var(--hero-slide-padding-block) var(--hero-art-inset)
    var(--hero-slide-padding-bottom) var(--hero-copy-inset);
  background-color: var(--color-brand-soft);
  overflow: hidden;
}

.home-hero__slide[inert] {
  visibility: hidden;
}

.home-hero--custom-color {
  background-color: var(--hero-banner-custom-background);
}

.home-hero__background {
  position: absolute;
  z-index: -1;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.home-hero__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-xs);
  max-width: var(--hero-copy-max-width);
}

/* Figma: hero heading is Raleway 700 / 30, leading 1.17 (node 6:82), so it
   outweighs the tile labels under it and the band leads the page. */
.home-hero__title {
  margin: 0;
  font-size: var(--font-size-3xl);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-tight);
}

.home-hero__text {
  margin: 0;
  font-size: var(--font-size-xs);
  line-height: var(--line-height-normal);
}

/* The action is a labelled arrow rather than the Figma pill: the whole band
   is the link (its ::after covers the slide, under the carousel controls),
   so a 50px pill would only add height to a band meant to stay low. The
   amber circle keeps it recognisable as the band's action. */
.home-hero__action {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  margin-top: var(--space-3xs);
  color: var(--color-ink);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: var(--letter-spacing-caps);
  text-transform: uppercase;
  text-decoration: none;
}

.home-hero__action::after {
  position: absolute;
  z-index: var(--hero-layer-content);
  inset: 0;
  content: "";
}

.home-hero__action-mark {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  width: var(--hero-action-mark-size);
  height: var(--hero-action-mark-size);
  border-radius: var(--radius-pill);
  background-color: var(--color-action);
}

.home-hero__action-arrow {
  width: var(--hero-action-arrow-size);
  height: var(--hero-action-arrow-size);
}

/* The pair's box spans the band's full height, through the slide's padding,
   and is as wide as the overlapping pair; the covers stand on its bottom
   edge, cut by the band, so they never decide its height: the copy does. */
/* `isolation` keeps the covers' stacking inside the pair, under the banner's
   stretched link, so a click on a cover follows it too. */
.home-hero__art {
  position: relative;
  isolation: isolate;
  align-self: stretch;
  width: calc(var(--hero-cover-width) * (2 - var(--hero-cover-overlap)));
  margin-top: calc(-1 * var(--hero-slide-padding-block));
  margin-bottom: calc(-1 * var(--hero-slide-padding-bottom));
}

.home-hero__book {
  --hero-cover-cut: calc(var(--hero-cover-width) / (var(--hero-cover-aspect)) / 4);

  position: absolute;
  bottom: calc(-1 * var(--hero-cover-cut));
  width: var(--hero-cover-width);
  aspect-ratio: var(--hero-cover-aspect);
  object-fit: cover;
  box-shadow: var(--shadow-card);
}

/* The first cover in front, the second behind it and higher. */
.home-hero__book--one {
  z-index: 1;
  left: 0;
}

.home-hero__book--two {
  right: 0;
  bottom: calc(var(--hero-cover-width) * var(--hero-cover-step) - var(--hero-cover-cut));
}

.home-hero__slide--one-art .home-hero__book {
  left: calc(var(--hero-cover-width) * (1 - var(--hero-cover-overlap)) / 2);
}

/* Scaled with the covers and standing on the band's bottom edge, as they
   do, so the frame keeps its place around them at every width; a taller
   band leaves the room above it plain. */
.home-hero__ornaments {
  position: absolute;
  bottom: 0;
  left: calc(-1 * var(--hero-cover-width) * var(--hero-ornaments-start));
  width: calc(var(--hero-cover-width) * var(--hero-ornaments-span));
  max-width: none;
  pointer-events: none;
}

.home-hero__ornaments img {
  display: block;
  width: 100%;
  height: auto;
}

/* A phone's scattered ornaments, placed beside its action (below). */
.home-hero__scatter {
  display: none;
}

/* Outside the slides and above their stretched link. On a desktop the dots
   sit on the band's bottom edge, centred, in the room between the copy and
   the pair's ornaments; the arrows stand on the band's sides. */
.home-hero__controls {
  grid-area: stage;
  align-self: end;
  justify-self: center;
  z-index: calc(var(--hero-layer-content) + 1);
  display: flex;
  align-items: center;
}

.home-hero__dots {
  display: flex;
  gap: var(--space-2xs);
}

.home-hero__dot {
  position: relative;
  display: block;
  min-width: var(--space-md);
  min-height: var(--tap-target);
  border: 0;
  background: transparent;
  cursor: pointer;
}

.home-hero__dot::after {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--hero-dot-size);
  height: var(--hero-dot-size);
  border: var(--stroke-hairline) solid var(--color-ink);
  border-radius: var(--radius-pill);
  background-color: var(--color-canvas);
  content: "";
  transform: translate(-50%, -50%);
}

.home-hero__dot[aria-current="true"]::after {
  background-color: var(--color-ink);
}

.home-hero__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
}

.home-hero__nav--previous {
  left: var(--hero-nav-offset);
}

.home-hero__nav--next {
  right: var(--hero-nav-offset);
}

/* The band answers a pointer as a category tile does: its covers lift and
   its action moves to the accent (base.css), here with the arrow nudged on. */
@media (hover: hover) and (pointer: fine) {
  .home-hero__book,
  .home-hero__action-arrow {
    transition: transform var(--motion-tile) var(--ease-out);
  }

  .home-hero__slide:has(.home-hero__action):hover .home-hero__book {
    transform: translateY(calc(-1 * var(--cover-hover-lift)));
  }

  .home-hero__slide:hover .home-hero__action-arrow {
    transform: translateX(var(--hero-action-nudge));
  }
}

@media (prefers-reduced-motion: reduce) {
  .home-hero__slide:has(.home-hero__action):hover .home-hero__book,
  .home-hero__slide:hover .home-hero__action-arrow {
    transform: none;
  }
}

.home-hero__slide.is-leaving-next {
  animation: hero-slide-out-next var(--motion-switch) var(--ease-out) both;
}

.home-hero__slide.is-entering-next {
  animation: hero-slide-in-next var(--motion-switch) var(--ease-out) both;
}

.home-hero__slide.is-leaving-previous {
  animation: hero-slide-out-previous var(--motion-switch) var(--ease-out) both;
}

.home-hero__slide.is-entering-previous {
  animation: hero-slide-in-previous var(--motion-switch) var(--ease-out) both;
}

/* Carousel motion reveals content from the selected direction. */
@keyframes hero-slide-out-next {
  to { transform: translateX(-100%); }
}

@keyframes hero-slide-in-next {
  from { transform: translateX(100%); }
}

@keyframes hero-slide-out-previous {
  to { transform: translateX(100%); }
}

@keyframes hero-slide-in-previous {
  from { transform: translateX(-100%); }
}

/* ---- Category tiles ---- */

.home-categories__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--home-categories-gap);
}

/* One gap between tiles, between the grid and the listing links, and between
   the links, so the links read as another row of the grid. Declared on the
   grid too for the all-categories page, which uses the grid on its own. */
.home-categories,
.home-categories__grid {
  --home-categories-gap: var(--space-md);
}

.home-categories {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--home-categories-gap);
}

.home-categories__grid {
  width: 100%;
}

.home-categories__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--home-categories-gap);
}

.all-categories__grid {
  margin-top: var(--space-xl);
}

.home-curation__more {
  display: inline-flex;
  align-items: center;
  min-height: var(--admin-bar-height);
  color: var(--color-ink);
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-tight);
  text-decoration: underline;
  text-underline-offset: var(--space-3xs);
}

.category-tile {
  position: relative;
  display: block;
  height: var(--tile-height);
  padding: var(--space-2xl) 0 0 var(--space-2xl);
  background-color: var(--color-brand-soft);
  color: var(--color-ink);
  text-decoration: none;
  overflow: hidden;
}

/* Figma: tile labels are Raleway 400 / 24 over two lines (node 65:1480). */
.category-tile__name {
  position: relative;
  z-index: 1;
  display: block;
  max-width: 8.0625rem; /* 129 */
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-regular);
  line-height: var(--line-height-tight);
}

/* The tile surface and label at button size, for links that sit under the
   grid rather than in it. The doubled class outranks the fixed tile height
   and padding in the breakpoint blocks below. */
.category-tile.category-tile--plain {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: auto;
  min-width: var(--home-link-tile-min-width);
  min-height: var(--action-height);
  padding: var(--space-2xs) var(--space-lg);
}

.category-tile--plain .category-tile__name {
  max-width: none;
  font-size: var(--font-size-md);
}

/* Covers fan out of the bottom-right corner and are clipped by the tile. */
.category-tile__art {
  position: absolute;
  inset: 0;
}

/* A cover is as tall as the Figma one at every width, while its width scales
   down, so a narrower cover shows the middle of the photo. Covers 1 and 3 hang
   off the tile's bottom edge, and their files stop just below the lowest point
   the tile ever shows, hover lift and shadow included
   (build_category_tiles.py); their boxes are cut to match, so the photo is
   never stretched. */
.category-tile__book {
  position: absolute;
  width: var(--home-tile-cover-width); /* 64 -> 95 */
  height: var(--home-tile-cover-height);
  object-fit: cover;
  box-shadow: var(--shadow-card);
}

.category-tile__book--1 {
  left: 0.5625rem;
  top: 11.4375rem;
  height: 6.5rem; /* 104 */
}

.category-tile__book--2 {
  left: 14.3125rem;
  top: 6.875rem;
}

.category-tile__book--3 {
  left: 7.4375rem;
  top: 9.9375rem;
  height: 8rem; /* 128 */
}

@media (hover: hover) and (pointer: fine) {
  .category-tile:hover .category-tile__name {
    color: var(--color-action);
  }

  /* Figma pairs tile hover with a DISSOLVE at EASE_OUT over 300ms; the covers
     lift out of the tile as the surface brightens. */
  .category-tile__book {
    transition: transform var(--motion-tile) var(--ease-out);
  }

  .category-tile:hover .category-tile__book {
    transform: translateY(calc(-1 * var(--cover-hover-lift)));
  }
}

@media (prefers-reduced-motion: reduce) {
  .category-tile:hover .category-tile__book {
    transform: none;
  }
}

/* ---- Curated products ---- */

.home-curation {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-md);
  padding-top: var(--space-2xl);
}

.home-curation__title {
  margin: 0;
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  text-align: center;
}

/* Segmented pill: an outlined track whose selected segment is filled amber. */
.home-curation__tabs {
  position: relative;
  display: flex;
  align-items: center;
  width: var(--home-curation-tabs-width);
  height: var(--action-height);
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  background-color: var(--color-canvas);
}

.home-curation__tabs::before {
  position: absolute;
  inset: 0 auto 0 0;
  width: 50%;
  border-radius: var(--radius-pill);
  background-color: var(--color-action);
  content: "";
  transform: translateX(100%);
}

.home-curation__tabs:has([data-curation-tab="discount"].is-active)::before {
  transform: translateX(0);
}

/* A link (each list has its own URL), centred like the button it reads as. */
.home-curation__tab {
  position: relative;
  z-index: 1;
  display: flex;
  flex: 1;
  align-items: center;
  align-self: stretch;
  justify-content: center;
  border-radius: var(--radius-pill);
  color: var(--color-ink);
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  text-decoration: none;
  text-transform: uppercase;
}

.home-curation__tab.is-active {
  font-weight: var(--font-weight-bold);
}

@media (prefers-reduced-motion: no-preference) {
  .home-curation__tabs::before {
    transition: transform var(--motion-switch) var(--ease-out),
                background-color var(--motion-card) var(--ease-out);
  }
}

.home-curation__panel {
  width: 100%;
  margin-top: var(--space-md);
}

/* Four to a row on a desktop, not Figma's three 387px home cards: a laptop
   showed one row of three per screen. Four columns are the 1200 grid's own
   285 module, so the cards line up under the category tiles above and are
   the size of the catalog's, and they take the compact card (book-card.css)
   that size was drawn for (CLAUDE.md, "Figma source-of-truth deviations").
   They keep the home card's signature, though: the cover overhangs the
   tinted surface from a white band, with the badge on its middle line, and
   the short description stays under the title, so the offers still read as a
   shop window rather than a listing. */
.home-curation__grid {
  --grid-gap: var(--space-md);
  --book-card-overhang: initial;
  --book-card-cover-inset: initial;
  --book-card-badge-top: var(--card-badge-offset-catalog);
  --book-card-description-display: initial;

  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--grid-row-gap, var(--grid-gap)) var(--grid-gap);
}

/* The offers are a shop window, not a listing: a title takes at most two
   lines and a longer one ends in an ellipsis, so every card shows its cover,
   price and action at the same height (the product owner's choice,
   2026-10-03). The full title stays in the markup for assistive technology
   and opens in full on the book's page. The link's ::after, which stretches
   it over the card, is positioned against the card, so the clip leaves it
   whole. */
.home-curation__grid .book-card__title a {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

/* "Load more" is the list's last item. On the grid it takes a row of its own,
   centred under the books; the phone carousel turns it into a closing card. */
.home-curation__item--more {
  grid-column: 1 / -1;
  display: flex;
  justify-content: center;
  margin-top: var(--space-md);
}

.home-curation__more {
  gap: var(--space-xs);
  padding: var(--space-3xs) 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

/* The spin itself is the shared `.loading-icon` primitive (components.css);
   only the size this surface wants lives here. */
.home-curation__more-icon {
  width: 3rem;
  height: 3rem;
}

.home-curation__more-label {
  color: inherit;
}

/* ---- Responsive ---- */

@media (max-width: 61.9375rem) {
  /* A touch layout: a swipe takes the arrows' place (hero-banners.js) and
     the dots stay on the band's bottom edge. Without script the dots are
     still links to each banner. */
  .home-hero {
    --hero-height: var(--hero-height-medium);
    --hero-cover-width: var(--hero-cover-width-medium);
    --hero-copy-inset: var(--space-lg);
    --hero-art-inset: var(--space-xl);
  }

  /* Scoped to the controls to outrank the step-arrow primitive's display
     (controls.css loads after this sheet). */
  .home-hero__controls .home-hero__nav {
    display: none;
  }

  .home-categories__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Once the tiles pair up, each link takes a tile's column. */
  .home-categories__actions {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    width: 100%;
  }

  .category-tile.category-tile--plain {
    min-width: 0;
  }

  /* Two- and one-column tiles are wider than a desktop quarter, so the
     viewport-scaled cover would shrink exactly where there is more room.
     Keep the desktop cover and its fan so the tile reads the same. */
  .category-tile {
    --home-tile-cover-width: var(--home-tile-cover-width-max);
  }

  /* Two columns and the phone carousel are wide enough for the home card,
     so the compact card's settings (book-card.css) are dropped here. */
  .home-curation__grid {
    --book-card-overhang: initial;
    --book-card-cover-width: initial;
    --book-card-cover-aspect: initial;
    --book-card-cover-inset: initial;
    --book-card-body-padding: initial;
    --book-card-badge-top: initial;
    --book-card-title-size: initial;
    --book-card-price-size: initial;

    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 36.25rem) {
  /* Stacked, as a phone banner reads: the title and description across the
     band's full width, then a row with the action on the left and the cover
     pair on the right, standing on the band's bottom edge. Beside the pair the
     copy had a third of the width and could only be cut short; across the band
     it reads whole. The pair has a row of its own, so a longer text moves it
     down with the band instead of leaving it sunk under an empty strip. The
     ornaments keep to that row too, no higher than the pair, so they never
     reach the copy above: a strip on the pair's right, and the rest scattered
     over whatever room the action leaves. With several banners the dots sit at the foot of the
     action's side, under the action. */
  .home-hero {
    --hero-height: auto;
    --hero-cover-width: var(--hero-cover-width-compact);
    --hero-ornaments-start: var(--hero-ornaments-start-compact);
    --hero-ornaments-span: var(--hero-ornaments-span-compact);
    /* The front cover's visible three quarters and the back one's step. */
    --hero-pair-height: calc(var(--hero-cover-width) / (var(--hero-cover-aspect)) * 3 / 4
      + var(--hero-cover-width) * var(--hero-cover-step));
    --hero-copy-inset: var(--space-sm);
    --hero-art-inset: var(--space-sm);
    --hero-slide-padding-block: var(--space-sm);
  }

  .home-hero__slide {
    grid-template-areas:
      "title title title"
      "text text text"
      "action scatter art";
    grid-template-columns: auto minmax(0, 1fr) auto;
    /* A banner shorter than the tallest is stretched to its height; the cover
       row takes the extra, so its copy stays together at the top. */
    grid-template-rows: auto auto minmax(calc(var(--hero-pair-height) - var(--hero-slide-padding-bottom)), 1fr);
    align-items: start;
    gap: var(--space-3xs) var(--space-xs);
  }

  .home-hero__controls {
    justify-self: start;
    margin-inline-start: calc(var(--hero-copy-inset) - (var(--space-md) - var(--hero-dot-size)) / 2);
  }

  /* Its children are placed on the slide's grid, the action apart from the
     text above it. */
  .home-hero__copy {
    display: contents;
  }

  .home-hero__title {
    grid-area: title;
    font-size: var(--font-size-xl);
  }

  .home-hero__text {
    display: -webkit-box;
    grid-area: text;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
  }

  .home-hero__action {
    grid-area: action;
    margin-top: var(--space-xs);
    text-decoration: underline;
    text-decoration-color: var(--color-action);
    text-decoration-thickness: var(--stroke-control);
    text-underline-offset: var(--space-3xs);
  }

  .home-hero__action-mark {
    display: none;
  }

  .home-hero__scatter {
    display: block;
    grid-area: scatter;
    align-self: end;
    width: 100%;
    height: var(--hero-pair-height);
    margin-bottom: calc(-1 * var(--hero-slide-padding-bottom));
    pointer-events: none;
  }

  .home-hero__art {
    grid-area: art;
    align-self: stretch;
    margin-top: 0;
  }

  /* Two tiles per row, like the catalog grid. The half-width tile is shorter
     and its cover fan is placed in percentages of the tile - the Figma
     offsets divided by the 285x258 tile - so the same composition scales
     down instead of the fixed desktop offsets pushing covers out of view. */
  .home-categories,
  .home-categories__grid {
    --home-categories-gap: var(--space-xs);
  }

  .category-tile {
    --home-tile-cover-width: var(--home-tile-cover-width-min);
    height: var(--tile-height-compact);
    padding: var(--space-sm) var(--space-sm) 0;
  }

  .category-tile__name {
    font-size: var(--font-size-md);
    overflow-wrap: break-word;
  }

  .category-tile.category-tile--plain {
    padding-inline: var(--space-sm);
  }

  .category-tile__book--1 {
    left: 3.2%;   /* 9 / 285 */
    top: 70.9%;   /* 183 / 258 */
  }

  .category-tile__book--2 {
    left: 80.4%;  /* 229 / 285 */
    top: 42.6%;   /* 110 / 258 */
  }

  .category-tile__book--3 {
    left: 41.8%;  /* 119 / 285 */
    top: 61.6%;   /* 159 / 258 */
  }

  /* Discounts / New books become a horizontal scroll-snap row that bleeds to
     the screen edge: one card in view and the start of the next, so the row
     visibly continues sideways. "Load more" still appends to its end. */
  .home-curation__grid {
    display: flex;
    gap: var(--space-sm);
    margin-inline: calc(-1 * var(--gutter));
    padding: 0 var(--gutter) var(--space-2xs);
    overflow-x: auto;
    overflow-y: hidden;
    scroll-padding-inline: var(--gutter);
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }

  /* curation.js suspends snapping while it inserts a loaded page, so the row
     does not slide out from under the reader; it lifts again on the next swipe. */
  .home-curation__grid--snap-suspended {
    scroll-snap-type: none;
  }

  .home-curation__grid::-webkit-scrollbar {
    display: none;
  }

  .home-curation__item {
    flex: 0 0 var(--home-curation-card-width);
    min-width: 0;
    scroll-snap-align: start;
  }

  /* The closing card: a book card's width, height (the row stretches it to
     the tallest card) and surface, holding only the action. */
  .home-curation__item--more {
    margin-top: 0;
  }

  .home-curation__more {
    flex: 1 1 auto;
    flex-direction: column;
    justify-content: center;
    padding: var(--space-xl);
    background-color: var(--color-surface);
    text-align: center;
  }

  /* "All categories" reads as the entry point into the tile grid below it,
     not a footer link, so it leads on mobile. */
  .home-categories__actions {
    order: -1;
  }

  .home-curation__title {
    font-size: var(--font-size-md);
  }

  /* The switch keeps its tap-target height but not the desktop width and
     type, which made it the heaviest thing on a phone screen. */
  .home-curation__tabs {
    width: var(--home-curation-tabs-width-compact);
  }

  .home-curation__tab {
    font-size: var(--font-size-xs);
  }
}
