/**
 * @file        home.css
 * @description Homepage — bands, the two-column hero and the ported card treatments
 *
 * @author      Thomas Dobler - SYNTHETIXMIND LTD
 * @email       tom@synthetixmind.com
 * @company     SYNTHETIXMIND LTD, Paphos, Cyprus
 * @url         https://synthetixmind.com
 *
 * @uid         0ad74f16-8b23-4c95-9e07-63f2a5c18d40
 * @version     0.4.1.10
 * @created     2026-09-11
 * @updated     2026-09-17
 *
 * @copyright   (c) 2026 by SYNTHETIXMIND LTD
 * @license     Proprietary — All rights reserved
 */

.home__intro {
  font-size: var(--text-lg);
}

/* --- Services teaser ------------------------------------------------------ */

.home__services {
  list-style: none;
  margin: var(--space-6) 0;
  padding: 0;
  display: grid;
  gap: var(--space-6);
}

/* Single column below the medium breakpoint. The banded layout's rule is that
   375px is the design target and wider viewports are the enhancement, so the
   multi-column form is the media query rather than the default. */
@media (min-width: 48rem) {
  .home__services {
    grid-template-columns: repeat(3, 1fr);
  }
}

.home__service-title {
  font-size: var(--text-lg);
  margin: 0 0 var(--space-2);
}

.home__service-summary {
  margin: 0;
  color: var(--text-secondary);
  max-width: var(--measure);
}

/* --- Book row ------------------------------------------------------------- */

/* NOT a carousel in the widget sense. The flip preview is deferred for MVP-1
   (Story 5.2, sprint-status.md), and with it went every reason to script this:
   there is no autoplay to pause, no slide index to track and no motion to
   suppress under prefers-reduced-motion. What remains is a list that wraps.
   Horizontal overflow is deliberately NOT used — a scroll container would hide
   books behind a gesture, and FR11 requires every book here to stay reachable. */
.home__carousel {
  list-style: none;
  margin: var(--space-6) 0;
  padding: 0;
  display: grid;
  gap: var(--space-8);
}

@media (min-width: 48rem) {
  .home__carousel {
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  }
}

.home__slide-link {
  display: block;
  text-decoration: none;
  color: var(--text-primary);
}

/* The title is the link text, so it carries the underline that signals a link.
   Underlining the whole block, cover included, would be noise. */
.home__slide-title {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  line-height: var(--leading-tight);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.home__slide-link:hover .home__slide-title,
.home__slide-link:focus-visible .home__slide-title {
  color: var(--text-link);
}

/* Focus lands on the <a>, which wraps the cover, so the ring must be drawn
   around the whole block rather than around the text alone. */
.home__slide-link:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--space-1);
}

/* Intrinsic size is declared on the <img> element too. Both are needed: the
   attributes reserve the space before CSS parses and are what keep CLS at 0,
   while these rules let the cover shrink on a narrow viewport. */
/* The cover now sits on a shelf: a real drop shadow and a hairline edge, so a
 * placeholder-white cover on a white band stops disappearing into it. This also
 * fixes something that was invisible until the bands were tinted - an untinted
 * white rectangle on a white band has no edge at all. */
.home__cover {
  display: block;
  width: 10rem;
  height: auto;
  max-width: 100%;
  background: var(--surface-raised);
  border-radius: var(--radius-sm);
  border: 1px solid rgb(122 147 172 / 0.35);
  box-shadow: 0 0.4rem 1rem rgb(45 49 66 / 0.14);
}

.home__slide-tagline {
  margin: var(--space-2) 0 0;
  max-width: var(--measure);
}

.home__slide-author {
  margin: var(--space-1) 0 0;
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

/* --- Hero ------------------------------------------------------------------
 * The front door's first impression, and until now it was a page-coloured band
 * with body-sized type - correct, and completely silent. It is now an INVERSE
 * band, which puts it on the same Deep Space as the masthead directly above it
 * and reads as one continuous masthead-and-hero rather than as a dark strip
 * balanced on a light page.
 *
 * ON CONTRAST. Everything here is Cloud White (11.28:1) or Golden Accent
 * (4.88:1) on Deep Space - DESIGN.md section 2 rule 6, which forbids Digital
 * Blue (3.61:1) and Steel Mist (4.05:1) on this surface at any size. The
 * primary button keeps its Deep Space label on gold via the new
 * `.band--inverse .button--primary` rule in base.css; without that it would
 * have inherited Cloud White from `.band--inverse a` and rendered at 1.90:1.
 *
 * The focus ring flips to Cloud White automatically - base.css already carries
 * `.band--inverse :focus-visible { outline-color: var(--focus-ring-inverse) }`,
 * which exists because the shared Deep Space ring measures 1.00:1 here. */

.home__hero {
  position: relative;
  overflow: hidden;               /* clips the aura to the band */
  padding-block: var(--space-10);
}

@media (min-width: 48rem) {
  .home__hero { padding-block: var(--space-12); }
}

/* THE AURA. Two soft radial washes in the brand blue and gold, sitting behind
 * the text and well below the contrast floor that would make them a surface the
 * text has to survive - Deep Space is still the background everywhere the words
 * actually are, so the 11.28:1 measurement holds.
 *
 * DECORATIVE, and therefore aria-hidden by construction: it is a pseudo-element
 * with no content, which no assistive technology announces. It is also the
 * reason the band needs `overflow: hidden` rather than a fixed size. */
.home__hero::before {
  content: "";
  position: absolute;
  inset: -40% -10% auto -10%;
  height: 160%;
  /* Three washes rather than two, and stronger. Digital Blue twice at
   * different sizes gives the light somewhere to come FROM, and the gold sits
   * opposite it so the band has a warm corner and a cool one instead of an
   * even tint. None of it is a surface any text sits on - the words are all
   * inside .container, over Deep Space - so the 11.28:1 measurement is
   * untouched by how strong these get. */
  background:
    radial-gradient(38% 46% at 12% 12%, rgb(5 142 217 / 0.42), transparent 72%),
    radial-gradient(52% 62% at 30% 42%, rgb(5 142 217 / 0.20), transparent 70%),
    radial-gradient(44% 54% at 88% 78%, rgb(191 155 48 / 0.30), transparent 72%);
  pointer-events: none;
  z-index: 0;
}

/* A second, sharper layer: one diagonal sheen across the band. Separate from
 * ::before so the two can arrive at different speeds - the wash swells, the
 * sheen slides - which is what stops the hero reading as a single fading
 * rectangle. Also decorative, also no content, also announced by nothing. */
.home__hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(104deg, transparent 38%, rgb(238 240 242 / 0.07) 50%, transparent 62%);
  pointer-events: none;
  z-index: 0;
}

/* Everything real sits above the wash. */
.home__hero > .container {
  position: relative;
  z-index: 1;
}

/* The one place on the site that earns display type. `text-wrap: balance` is
 * already global in base.css, so a three-word last line cannot happen. */
.home__hero h1 {
  font-size: var(--text-4xl);
  margin-bottom: var(--space-5);
}

/* A short gold rule under the heading rather than a full-width border: a rule
 * that spans the measure reads as a separator between two things, and this one
 * is an underline belonging to the heading above it. Decorative, so it is a
 * pseudo-element and never text. */
/* The rule is now a gradient that fades out to the right rather than a flat
 * bar that stops. Same 4rem, same gold, but it reads as an underline trailing
 * off instead of a dash. */
.home__hero h1::after {
  content: "";
  display: block;
  width: 4rem;
  height: 3px;
  margin-top: var(--space-4);
  background: linear-gradient(90deg, var(--color-golden-accent), rgb(191 155 48 / 0.15));
  border-radius: var(--radius-full);
}

/* The primary CTA carries a gold halo on a dark band. It is a box-shadow, so it
 * costs no layout and cannot push anything; the colour is the button's own
 * background at low alpha, which keeps it a glow rather than a second border. */
.home__hero-actions .button--primary {
  box-shadow: 0 0 0 0 rgb(191 155 48 / 0.55), 0 0.5rem 1.5rem rgb(191 155 48 / 0.28);
}

.home__hero .home__intro {
  color: var(--text-on-dark);
  max-width: var(--measure);
}

/* The two routes out of the hero. The secondary one is a plain link rather than
 * a second button: two buttons of equal weight ask the visitor to choose before
 * they know enough to, which is how a front door becomes a fork. */

.home__hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-6);
}

.home__hero-secondary {
  min-height: var(--touch-min);
  display: inline-flex;
  align-items: center;
}

/* --- Shared band furniture ------------------------------------------------
 * VIVIDNESS ON THE LIGHT BANDS, AND THE RULE THAT CONSTRAINS IT. These bands
 * were flat white and flat Cloud White alternating, which is calm and
 * completely inert. Each now carries a low-alpha wash anchored to one corner so
 * the eye has somewhere to go.
 *
 * WHICH WASH DEPENDS ON THE BAND, and this is measured rather than judged by
 * eye. Two text tokens sit DIRECTLY on these bands - --text-secondary and
 * --text-link - and on Cloud White they measure 4.54:1 and 4.51:1. That is
 * 0.04 and 0.01 of margin over the AA floor, so on a `band--page` there is
 * room for NO darkening tint at all: gold at 7% takes them to 4.30 and 4.27,
 * and every smaller alpha of every brand colour fails too, because the margin
 * is smaller than the smallest useful wash.
 *
 *   band--raised (#FFFFFF)  -> 5.18 and 5.15 untinted, so a 6-7% brand wash
 *                              lands at 4.83 / 4.80 and clears. Coloured.
 *   band--page (Cloud White) -> no headroom. The wash LIGHTENS instead, which
 *                              moves both tokens UP (4.60 at 10% white).
 *
 * An earlier draft of this block put gold on all four bands and took the
 * Insights band to 4.37:1. It was caught by computing the ratios rather than by
 * looking at the page, which is the only way this class of defect is ever
 * caught - a 4.37 and a 4.55 are indistinguishable to the eye. */

/* Coloured washes: white bands, which have the headroom. */
.home__story {
  background-image: radial-gradient(60% 80% at 100% 0%, rgb(5 142 217 / 0.06), transparent 70%);
}

.home__events {
  background-image: radial-gradient(55% 75% at 100% 100%, rgb(5 142 217 / 0.06), transparent 70%);
}

/* Lightening washes: Cloud White bands, which have none. A white glow reads as
 * depth in the same way and cannot cost a single point of contrast. */
.home__authors {
  background-image: radial-gradient(55% 75% at 0% 100%, rgb(255 255 255 / 0.85), transparent 70%);
}

.home__latest {
  background-image: radial-gradient(60% 80% at 0% 0%, rgb(255 255 255 / 0.85), transparent 70%);
}

/* Every band heading gets the hero's trailing gold rule, so the page reads as
 * one system rather than as a decorated hero on top of a plain document.
 * Decorative and generated - never text, never announced, and never a surface
 * anything has to be legible against. */
.home__story h2::after,
.home__services-band h2::after,
.home__authors h2::after,
.home__events h2::after,
.home__latest h2::after {
  content: "";
  display: block;
  width: 2.5rem;
  height: 3px;
  margin-top: var(--space-3);
  background: linear-gradient(90deg, var(--color-golden-accent), rgb(191 155 48 / 0.15));
  border-radius: var(--radius-full);
}

.home__band-intro {
  margin: calc(var(--space-2) * -1) 0 var(--space-6);
  max-width: var(--measure);
  color: var(--text-secondary);
}

/* --- Featured authors -----------------------------------------------------
 * The cards ARE the roster's cards - same markup, same classes, author.css does
 * the work. Only the count differs, and that is the controller's decision. Three
 * across at the wide breakpoint so the band reads as a row rather than as a
 * truncated list. */

@media (min-width: 48rem) {
  .home__authors .authors__grid { grid-template-columns: repeat(3, 1fr); }
}

/* --- Coming up -------------------------------------------------------------
 * A calendar, so it is laid out as one: the date leads, because the date is the
 * question a reader has. The title follows as the link. */

.home__events-list {
  list-style: none;
  margin: var(--space-6) 0;
  padding: 0;
  display: grid;
  gap: var(--space-5);
}

@media (min-width: 48rem) {
  .home__events-list { grid-template-columns: repeat(2, 1fr); }
}

/* A card on its own surface, with the gold bar as its leading edge.
 *
 * THE SURFACE IS LOAD-BEARING, NOT DECORATION. `.home__event-when` is
 * --text-secondary, which measures 4.54:1 on Cloud White - a margin of 0.04
 * over the AA floor. An earlier draft of this band put a 7% gold wash directly
 * under that text and took it to 4.30:1. Measured, not guessed: every
 * darkening tint fails at every alpha, because there is no room. Lifting the
 * text onto --surface-raised buys it 5.18:1 and lets the band keep a colour
 * wash in the gutters, where nothing reads. */
.home__event {
  padding: var(--space-4);
  /* A SOLID BORDER, not a gradient background layer. The previous spelling was
   * `background: linear-gradient(...) left / 3px 100% no-repeat, <color>`, which
   * is valid CSS and renders as a 3px edge in a browser that implements the
   * shorthand's `position / size` form. Obscura does not: it drops the size and
   * paints the gradient across the whole element, so the card came out SOLID
   * GOLD with body text on it. That shipped to production in 0.27.0 on the
   * homepage event cards.
   *
   * Whether real browsers were also affected was not determinable from here,
   * and a live site is the wrong place to leave that question open - so the
   * pattern is now one that renders identically everywhere and still respects
   * border-radius, which is what ruled out `border-image` earlier. */
  background: var(--surface-raised);
  border-left: 3px solid var(--color-golden-accent);
  border-radius: var(--radius-md);
  box-shadow: 0 0.25rem 0.75rem rgb(45 49 66 / 0.07);
}

.home__event-when {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  font-weight: 600;
}

/* Colour is never the sole means of conveying anything (NFR21): the location is
 * distinguished by position and by its own wording, not by this tint. */
.home__event-where { font-weight: 400; }

.home__event-title {
  font-size: var(--text-lg);
  margin: 0;
}

.home__event-title a {
  display: inline-block;
  min-height: var(--touch-min);
}

/* --- What this is, in three moves ----------------------------------------- */

.home__steps {
  list-style: none;
  margin: var(--space-6) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-6);
}

@media (min-width: 48rem) {
  .home__steps { grid-template-columns: repeat(3, 1fr); }
}

/* The number is generated, not typed. It is decorative - the <ol> already
 * carries the ordering for assistive technology, and a typed "1." in the
 * heading would be announced twice. */
/* A gold disc with the numeral in it, rather than a small grey digit. This is
 * the single most visible change on the light half of the page: three numbered
 * discs give the band a rhythm that a paragraph of body text cannot.
 *
 * Deep Space on Golden Accent is 4.88:1 - the same pairing the primary button
 * uses, and above the 4.5:1 floor - so the numeral stays legible rather than
 * becoming decoration that happens to contain a character.
 *
 * It styles a REAL <span aria-hidden>, not a `content: counter()` pseudo: see
 * the note in home/index.html.twig. The short version is that Obscura does not
 * implement CSS counters, so nothing here could have told a broken counter from
 * a working one. */
.home__step-num {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--radius-full);
  background: linear-gradient(135deg, var(--color-golden-accent), #D8B44A);
  color: var(--text-on-gold);
  font-family: var(--font-heading, inherit);
  font-size: var(--text-lg);
  font-weight: 700;
  line-height: 1;
  margin-bottom: var(--space-3);
  box-shadow: 0 0.35rem 0.9rem rgb(191 155 48 / 0.35);
}

.home__step-title {
  font-size: var(--text-lg);
  margin: 0 0 var(--space-2);
}

.home__step p {
  margin: 0;
  color: var(--text-secondary);
  max-width: var(--measure);
}

/* --- Latest articles ------------------------------------------------------ */

.home__articles {
  list-style: none;
  margin: var(--space-6) 0;
  padding: 0;
  display: grid;
  gap: var(--space-5);
}

@media (min-width: 48rem) {
  .home__articles { grid-template-columns: repeat(2, 1fr); }
}

/* A gold tick to the left of each headline, so the archive reads as a list of
 * entries rather than as a wall of links. Generated and decorative. */
/* Same card, same reason as .home__event above: `.home__article-excerpt` and
 * `.home__article-meta` are both --text-secondary, and this band is Cloud
 * White, where that token has 0.04 of margin. On --surface-raised it has
 * 5.18:1 and the band's own wash can never reach it. */
.home__article {
  padding: var(--space-4);
  background: var(--surface-raised);
  border-radius: var(--radius-md);
  border-left: 3px solid rgb(191 155 48 / 0.45);
  box-shadow: 0 0.25rem 0.75rem rgb(45 49 66 / 0.07);
}

.home__article-title {
  font-size: var(--text-lg);
  margin: 0 0 var(--space-1);
}

/* The whole title is the link, so the target is as large as the words are -
 * no separate "read more" and nothing that needs a minimum height bolted on. */
.home__article-title a {
  display: inline-block;
  min-height: var(--touch-min);
}

.home__article-meta {
  margin: 0 0 var(--space-2);
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

.home__article-excerpt {
  margin: 0;
  max-width: var(--measure);
  color: var(--text-secondary);
}

/* --- Motion ----------------------------------------------------------------
 * EVERY RULE BELOW IS AN ENHANCEMENT AND NOTHING DEPENDS ON IT. The page is
 * complete and readable with this whole block deleted, which is the test that
 * matters: NFR28 says the site works without JavaScript, and there is no
 * JavaScript here either - these are CSS animations and CSS scroll timelines.
 *
 * Three rules govern the whole block:
 *
 *   1. Nothing animates FROM invisible except under a scroll timeline that is
 *      known to be supported. An element hidden by an animation that never runs
 *      is content the visitor never sees - the failure mode of every
 *      reveal-on-scroll library, and it costs the whole page in a browser that
 *      does not support it. So the @supports guard wraps the opacity, not just
 *      the animation.
 *   2. prefers-reduced-motion is honoured by switching the animations OFF, not
 *      by shortening them. A vestibular trigger shortened is still a trigger.
 *   3. Motion is entrance-only and never loops. Nothing on this page moves
 *      while it is being read (SC 2.2.2). That rule is why there is no drifting
 *      gradient behind the hero: an aura that breathes forever is the obvious
 *      way to make a front door feel alive, and it is exactly the thing 2.2.2
 *      forbids. It fades in once and then holds still. */

@keyframes home-rise {
  from { opacity: 0; transform: translateY(1.75rem); }
  to   { opacity: 1; transform: none; }
}

@keyframes home-wash {
  from { opacity: 0; transform: scale(1.12); }
  to   { opacity: 1; transform: none; }
}

/* The gold rule under the h1 draws itself in. `width` rather than a transform
 * so it cannot overhang the heading mid-animation on a narrow viewport. */
@keyframes home-rule {
  from { width: 0; }
  to   { width: 4rem; }
}

/* The diagonal sheen slides across once and stops. ONCE is the whole point: a
 * sheen that repeats is a loop, and nothing on this page may move while it is
 * being read (SC 2.2.2). */
@keyframes home-sheen {
  from { opacity: 0; transform: translateX(-35%); }
  60%  { opacity: 1; }
  to   { opacity: 1; transform: none; }
}

/* The CTA's gold halo expands out of the button and settles. Animating the
 * box-shadow spread rather than a transform so the button's box never moves and
 * nothing beside it can be nudged. */
@keyframes home-halo {
  from { box-shadow: 0 0 0 0 rgb(191 155 48 / 0.55), 0 0.5rem 1.5rem rgb(191 155 48 / 0); }
  to   { box-shadow: 0 0 0 0.75rem rgb(191 155 48 / 0), 0 0.5rem 1.5rem rgb(191 155 48 / 0.28); }
}

/* The numbered discs arrive with a slight overshoot. Small - 1.08 - because a
 * bounce on three headings reads as a toy, and the point is emphasis rather
 * than personality. */
@keyframes home-pop {
  from { opacity: 0; transform: scale(0.72); }
  70%  { transform: scale(1.08); }
  to   { opacity: 1; transform: none; }
}

/* The hero arrives on load, in reading order. NOT scroll-driven: it is above
 * the fold by definition, so a scroll timeline would either have already
 * finished or never start.
 *
 * STAGGERED BY DURATION, NOT BY DELAY, and that is the whole point of this
 * comment. The obvious spelling is one duration and three animation-delays,
 * which needs `animation-fill-mode: backwards` to hold the element at the
 * keyframe's opacity: 0 while it waits its turn - and that is rule 1 above
 * being broken three lines after it was written. An element parked at opacity 0
 * by a fill mode is invisible for as long as the animation has not started, and
 * anything that stops the animation starting at all leaves it invisible for
 * good, with nothing to report.
 *
 * Three durations from a common start need no fill mode: at t=0 every element
 * is already animating, so the from-state is a frame rather than a parked
 * state, and if animations do not run the elements simply are where they
 * belong. The effect reads the same - they settle one after another.
 *
 * The travel is 28px rather than the 16px it was. That is the single change
 * that turns this from something only the person who wrote it notices into
 * something a visitor sees, and it costs nothing: the same rule about parked
 * opacity governs any amplitude. */
@media (prefers-reduced-motion: no-preference) {
  .home__hero::before            { animation: home-wash 1.1s cubic-bezier(0.16, 1, 0.3, 1); }
  .home__hero::after             { animation: home-sheen 1.4s cubic-bezier(0.16, 1, 0.3, 1); }
  .home__hero h1                 { animation: home-rise 0.55s cubic-bezier(0.16, 1, 0.3, 1); }
  .home__hero h1::after          { animation: home-rule 0.9s cubic-bezier(0.16, 1, 0.3, 1); }
  .home__hero .home__intro       { animation: home-rise 0.75s cubic-bezier(0.16, 1, 0.3, 1); }
  .home__hero .home__hero-actions { animation: home-rise 0.95s cubic-bezier(0.16, 1, 0.3, 1); }

  /* Runs last of the hero set, so the halo lands after the button has settled
   * rather than travelling with it. The end state is what the static rule
   * already declares, so a browser that skips the animation shows the halo
   * anyway - rule 1 again, in its smallest form. */
  .home__hero-actions .button--primary {
    animation: home-halo 1.2s cubic-bezier(0.16, 1, 0.3, 1);
  }
}

/* Bands below the fold rise as they are scrolled into view. `animation-timeline`
 * is what makes this possible without a single line of script; where it is not
 * supported the @supports block never applies and the content is simply there,
 * which is the correct fallback rather than a degraded one.
 *
 * The section HEADINGS are included as well as the cards. A band whose items
 * animate while its title sits still reads as a list loading underneath a
 * finished header; moving the heading first makes the whole band arrive as one
 * thing. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .home__step,
    .home__slide,
    .home__article,
    .home__event,
    .home__service,
    .home__authors .authors__item,
    .home__story h2,
    .home__services-band h2,
    .home__authors h2,
    .home__events h2,
    .home__latest h2 {
      animation: home-rise 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
      animation-timeline: view();
      /* Runs across the first third of the element's pass through the viewport,
       * and is finished well before it reaches the middle of the screen - an
       * animation still running under the reader's eye is a distraction. */
      animation-range: entry 0% cover 30%;
    }

    /* The disc arrives on its own timeline, slightly later than the card it
     * sits in, so the number lands after the text rather than with it. */
    .home__step-num {
      animation: home-pop 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
      animation-timeline: view();
      animation-range: entry 10% cover 40%;
    }

    /* The band headings' gold rules draw themselves in, the same gesture the
     * hero makes on load. `width` rather than scaleX so a partially drawn rule
     * can never overhang the heading it belongs to. */
    .home__story h2::after,
    .home__services-band h2::after,
    .home__authors h2::after,
    .home__events h2::after,
    .home__latest h2::after {
      animation: home-rule 0.7s cubic-bezier(0.16, 1, 0.3, 1) both;
      animation-timeline: view();
      animation-range: entry 0% cover 35%;
    }
  }
}

/* Hover and focus. A transform rather than a shadow change so the card does not
 * reflow its neighbours, and on :focus-visible as well as :hover so the keyboard
 * gets the same feedback the mouse does. */
@media (prefers-reduced-motion: no-preference) {
  .home__cover,
  .home__article-title a {
    transition: transform var(--duration-base) var(--ease-out);
  }

  .home__cover {
    transition-property: transform, box-shadow;
  }

  .home__slide-link:hover .home__cover,
  .home__slide-link:focus-visible .home__cover {
    transform: translateY(-0.6rem) scale(1.05) rotate(-1.2deg);
    box-shadow: 0 1.25rem 2.25rem rgb(45 49 66 / 0.28);
    border-color: var(--color-golden-accent);
  }

  .home__article-title a:hover,
  .home__article-title a:focus-visible {
    transform: translateX(0.25rem);
  }

  /* The whole entry shifts its accent bar to full gold on hover, so the feedback
   * is the article rather than only the words. Colour is not the sole signal -
   * the title moves too (NFR21). */
  .home__article {
    transition: border-color var(--duration-base) var(--ease-out);
  }

  .home__article:hover,
  .home__article:focus-within {
    border-left-color: var(--color-golden-accent);
  }

  /* The event entry lifts the way an author card does, so the two bands behave
   * alike rather than each having its own idea. */
  .home__event {
    transition: transform var(--duration-base) var(--ease-out);
  }

  .home__event:hover,
  .home__event:focus-within {
    transform: translateX(0.25rem);
  }

  /* The CTA lifts rather than only changing colour, so the affordance survives
   * for a visitor who cannot distinguish the hover tint (NFR21). */
  .home__hero-actions .button {
    transition:
      transform var(--duration-base) var(--ease-out),
      box-shadow var(--duration-base) var(--ease-out);
  }

  .home__hero-actions .button:hover,
  .home__hero-actions .button:focus-visible {
    transform: translateY(-0.125rem);
    box-shadow: 0 0.5rem 1.25rem rgb(0 0 0 / 0.35);
  }
}

/* ===========================================================================
 * PORTED FROM THE SHOWCASE DESIGN (Stitch screen "Catalyst Elements - Books,
 * Advisory & Executive Practice", also served verbatim at /dash/preview/landing).
 *
 * What is here is that design's TYPOGRAPHY and CARD TREATMENTS applied to the
 * columns this database actually has. What is not here is every block of the
 * comp that had no column behind it; each omission is named in the template
 * beside the band it belongs to, rather than in a list nobody reads.
 * ======================================================================== */

/* --- The eyebrow ----------------------------------------------------------
 * A small uppercase label above every section heading, from the comp. It is
 * TEXT, not decoration, so it obeys the contrast rules the rest of the page
 * does - and this is the one token choice on the page with no margin in it.
 *
 * On the light bands --text-secondary measures 4.54:1 on Cloud White, a margin
 * of 0.04 over the floor, which is exactly the finding 0.27.0 recorded when a
 * 5% gold wash took the same token to 4.37. So the eyebrow uses it at FULL
 * strength and never at an alpha: an `opacity` on this element, or a tint
 * under it, would spend a margin that does not exist. Letter-spacing and
 * weight do the work a lighter colour would have done. */
.home__eyebrow {
  margin: 0 0 var(--space-2);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* On the inverse hero the same label sits on Deep Space, where Steel Mist is
 * 4.05:1 and forbidden by DESIGN.md section 2 rule 6 at any size. Cloud White
 * is 11.28:1, so the label is simply the band's own text colour and is set
 * apart by size and spacing rather than by tint. */
.home__hero .home__eyebrow {
  color: var(--text-on-dark);
}

/* WITHHELD BELOW 48rem, and for the reason 0.30.0's classification chip was:
 * it costs 29px directly above the <h1> on a screen that has already spent
 * 208px (English) or 216px (German) on a three-row masthead, and it says
 * nothing the heading and the standfirst beneath it do not. Measured: the
 * homepage's <h1> sits at 298px with this rule and 327px without it.
 *
 * The BAND eyebrows further down the page keep theirs at every width - they
 * are below the fold wherever the reader starts, so they cost nothing that is
 * being competed for, and they are the labels that make a long page scannable
 * on a phone in the first place. */
@media (max-width: 47.999rem) {
  .home__hero .home__eyebrow {
    display: none;
  }
}

/* --- The hero's two columns -----------------------------------------------
 * ONE COLUMN UNTIL 64rem, and that is the 375px budget rather than a taste.
 * 0.28.0's book hero came out 70px over its budget in German the first time it
 * was made two columns, so this one is measured: below the breakpoint the
 * panel is not laid out at all and the band renders what it rendered before
 * the panel existed.
 *
 * 7/5 rather than 1/1: the thesis is the page's argument and the panel is an
 * illustration of it, so they are not of equal weight. That is the comp's own
 * ratio (lg:col-span-7 / lg:col-span-5). */
.home__hero-grid {
  display: grid;
  gap: var(--space-8);
  align-items: center;
}

@media (min-width: 64rem) {
  .home__hero-grid {
    grid-template-columns: 7fr 5fr;
  }
}

/* The lede keeps the reading measure the hero had when it sat in
 * `.container--reading`. Widening the container must not widen the prose: a
 * 1200px line of body text is unreadable whatever band it is on. */
.home__hero-lede {
  max-width: var(--measure);
}

/* --- The featured book panel ----------------------------------------------
 * WITHHELD BELOW 64rem - `display: none`, not `visibility` and not a clip, so
 * the browser lays out nothing and the cover is never fetched on a phone. The
 * panel is aria-hidden in the markup for a reason given there (every fact on
 * it repeats in the carousel below), so hiding it here removes nothing a
 * narrow visitor could otherwise have reached. */
.home__featured {
  display: none;
}

@media (min-width: 64rem) {
  .home__featured {
    display: block;
    justify-self: end;
    /* 20rem, MEASURED DOWN FROM 24. The panel is the tallest thing in the band
     * and therefore sets the band's height: at 24rem the cover came out 504px
     * and the hero 975px at 1280 - a front door where the second band is below
     * the fold on a laptop. At 20rem the same panel is ~630px and the hero
     * clears 750, which still gives the book a full column without the hero
     * becoming the page. */
    max-width: 20rem;
  }
}

/* A raised card on the dark band, which is the comp's treatment: the book is
 * an object sitting on the field rather than more text printed on it. The
 * panel is the only Surface Raised element inside an inverse band, so its text
 * tokens are the LIGHT-band ones - Deep Space on white at 11.28:1 - and the
 * colour is stated rather than inherited for the same reason
 * `.masthead__cta` states its own: `.band--inverse a` would paint this Cloud
 * White, which on white is 1.08:1. */
.home__featured-link {
  display: block;
  padding: var(--space-5);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  box-shadow: 0 1rem 2.5rem rgb(0 0 0 / 0.3);
  color: var(--text-primary);
  text-decoration: none;
}

/* 3/4 IS THE COMP'S RATIO (`aspect-[3/4]` with `object-cover`) and it is also
 * the only way this panel has a predictable height. Covers are operator
 * uploads with no enforced ratio, and the placeholder that stands in for a
 * missing one is 2:3 - so without a declared box the panel's height, and with
 * it the whole band's, changes with whichever book the shuffle put first. */
.home__featured-cover {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  margin-bottom: var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid rgb(122 147 172 / 0.35);
  background: var(--surface-page);
}

.home__featured-body {
  display: block;
}

/* The panel's own eyebrow is on white, not on the band, so it takes the light
 * surface's token rather than the hero override above. */
.home__featured .home__eyebrow {
  display: block;
  color: var(--text-secondary);
}

.home__featured-title {
  display: block;
  font-family: var(--font-heading);
  font-size: var(--text-xl);
  font-weight: 600;
  line-height: var(--leading-tight);
}

/* NO RULE FOR A SUBTITLE. The panel carried one for one draft and it was
 * removed, because the panel is aria-hidden on the grounds that everything on
 * it repeats in the carousel below - and the subtitle was the one field that
 * did NOT repeat anywhere on this page. A real column rendered only inside a
 * subtree no screen reader announces is worse than a column not rendered here
 * at all; the book's own page prints it, out loud. */

/* The comp sets the book's line in italic with a gold rule beside it. Kept,
 * because a tagline is the publisher's voice rather than the book's data, and
 * the rule is what separates the two without introducing another colour. */
.home__featured-tagline {
  display: block;
  margin-top: var(--space-3);
  padding-left: var(--space-3);
  border-left: 2px solid var(--color-golden-accent);
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-style: italic;
}

.home__featured-author {
  display: block;
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid rgb(122 147 172 / 0.35);
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

/* --- The physical facts on a catalogue card -------------------------------
 * The comp's "312 pages - Clothbound - 48.00" line. A flex row with a dot
 * between items rather than a table: three facts of three different kinds,
 * where a <dl> would promise a structure the reader does not need here. The
 * book's own page has the <dl>, with labels.
 *
 * The separator is a PSEUDO-ELEMENT on each item after the first, so it is
 * never selected, never read aloud, and disappears correctly when the line
 * wraps or when only one of the three facts is recorded. */
.home__slide-facts {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: var(--space-2) 0 0;
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.home__slide-facts > span + span::before {
  content: "\00b7";
  margin-right: var(--space-2);
  color: var(--border-subtle);
}

/* The price is the one fact a reader scans for, so it is the one that is not
 * grey. --text-primary rather than --color-gold-text, which is 4.54:1 on Cloud
 * White - the same hairline margin the eyebrow avoids, and a price is not
 * worth spending it on. */
.home__slide-price {
  color: var(--text-primary);
  font-weight: 600;
}

/* --- The event card's summary and facts -----------------------------------
 * The same treatment as the book card's, deliberately: the two bands describe
 * different things and a reader should not have to learn two layouts to read
 * a line of facts. */
.home__event-summary {
  margin: var(--space-2) 0 0;
  max-width: var(--measure);
  color: var(--text-secondary);
  font-size: var(--text-sm);
}

.home__event-facts {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2);
  margin: var(--space-2) 0 0;
  color: var(--text-secondary);
  font-size: var(--text-xs);
}

.home__event-facts > span + span::before {
  content: "\00b7";
  margin-right: var(--space-2);
  color: var(--border-subtle);
}

.home__event-price {
  color: var(--text-primary);
  font-weight: 600;
}

/* --- The article byline ---------------------------------------------------
 * The name leads and the date follows, which is the comp's order and the
 * useful one: who wrote it decides whether the date matters. */
.home__article-author {
  font-weight: 600;
  color: var(--text-primary);
}

.home__article-author + time::before {
  content: "\00b7";
  margin: 0 var(--space-2);
  color: var(--border-subtle);
}

/* --- The action anchor ----------------------------------------------------
 * The comp's closing block on the advisory band. A raised card with a gold
 * left edge - BORDER-LEFT, never a gradient in the `background` shorthand's
 * position/size form, which is the spelling 0.28.0 found renders as a solid
 * gold block in this project's own test browser. That defect shipped live
 * once; only the safe spelling is used here. */
.home__anchor {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-6);
  padding: var(--space-5);
  border-left: 3px solid var(--color-golden-accent);
  border-radius: var(--radius-sm);
  background: var(--surface-page);
}

.home__anchor-note {
  margin: 0;
  flex: 1 1 18rem;
  max-width: var(--measure);
  color: var(--text-secondary);
  font-size: var(--text-sm);
}
