

/* The Walmart-shaped hero: a slim brand line, then a merchandising grid
   with one big photographic feature cell (the run closing next) and the
   promo tiles packed beside it. The band stays indigo; the cells carry
   the excitement. */
.hero-brandline {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 22px 0 16px;
}
.hero-brandcopy h1 {
  color: #fff;
  font-size: var(--text-2xl);
  letter-spacing: -0.01em;
}
.hero-brandcopy p {
  color: var(--indigo-950-muted);
  font-size: var(--text-sm);
  margin-top: 4px;
  max-width: 52ch;
}
.hero-merch {
  display: grid;
  gap: 12px;
  grid-template-columns: minmax(0, 1fr);
}
.hero-feature {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 250px;
  border-radius: var(--r-card);
  overflow: hidden;
  background-color: var(--royal-900);
  background-size: cover;
  background-position: center;
  color: #fff;
}
.hero-feature::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Deep enough that the kicker and title hold their contrast over a bright
     photo (a market stall in daylight), on the tall mobile cell too. */
  background: linear-gradient(
    180deg,
    rgba(23, 16, 64, 0.05) 0%,
    rgba(23, 16, 64, 0.55) 45%,
    rgba(23, 16, 64, 0.92) 100%
  );
}
.hero-feature:hover {
  text-decoration: none;
}
.hero-feature-body {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 18px;
  width: 100%;
}
.hero-feature-kicker {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--indigo-950-soft);
}
.hero-feature-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-2xl);
  line-height: 1.15;
  letter-spacing: -0.01em;
}
.hero-feature-meta {
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.85);
}
.hero-feature-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
}
.hero-statsline {
  padding: 16px 0 24px;
  margin: 0;
  font-size: var(--text-xs);
  color: var(--indigo-950-muted);
}
@media (min-width: 980px) {
  .hero-brandline {
    padding: 26px 0 18px;
  }
  .hero-merch {
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr);
    grid-auto-rows: minmax(150px, auto);
  }
  .hero-carousel {
    grid-row: span 2;
  }
  .hero-feature {
    min-height: 330px;
  }
  .hero-merch .promo-tile {
    min-height: 0;
  }
}
@media (max-width: 979.98px) {
  .hero-merch {
    grid-template-columns: 1fr 1fr;
  }
  .hero-carousel {
    grid-column: span 2;
  }
  .hero-merch .promo-tile {
    min-height: 120px;
    padding: 13px 14px;
  }
  .hero-merch .promo-big {
    font-size: var(--text-lg);
  }
}
@media (max-width: 699.98px) {
  .hero-merch .promo-media {
    display: none;
  }
  .hero-merch .promo-tile.has-media {
    flex-direction: column;
  }
  /* A first-time visitor met the hero's "Join this run" and then four more
     offers before the page had explained one thing. The tiles are ranked
     already (closing, then deposit, then group, then saving), so the phone
     gets the two that can be acted on now and the rest of the page carries
     the others. Every width above this still shows all four. */
  /* The third tile onward, counted among tiles only: nth-of-type would count
     the feature card, which is also an <a>, and hide one tile too many. */
  .hero-merch .promo-tile ~ .promo-tile ~ .promo-tile {
    display: none;
  }
}
.promo-tile {
  display: flex;
  flex-direction: column;
  gap: 5px;
  border-radius: var(--r-card);
  padding: 16px 18px;
  min-height: 138px;
  color: var(--ink);
  box-shadow: 0 1px 2px rgba(23, 16, 64, 0.08);
  transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.promo-tile:hover {
  text-decoration: none;
  transform: translateY(-2px);
  box-shadow: var(--shadow-pop);
}
@media (prefers-reduced-motion: reduce) {
  .promo-tile {
    transition: none;
  }
  .promo-tile:hover {
    transform: none;
  }
}
.tile-accent {
  background: var(--accent-100);
  border: 1px solid var(--accent-line);
}
.tile-royal {
  background: var(--royal-100);
  border: 1px solid var(--royal-200);
}
.tile-sky {
  background: var(--sky-100);
  border: 1px solid var(--sky-100);
}
.tile-plain {
  background: var(--surface);
  border: 1px solid var(--line);
}
/* Image-led tiles: text column plus a rounded media block on the right,
   the way big-shop promo cards pair copy with product photography. */
.promo-tile.has-media {
  flex-direction: row;
  align-items: stretch;
  gap: 14px;
}
.promo-text {
  display: flex;
  flex-direction: column;
  gap: 5px;
  flex: 1;
  min-width: 0;
}
.promo-media {
  flex: none;
  width: 84px;
  align-self: center;
  /* Steps down from the tile's --r-card, concentric inside its 14px gap. */
  border-radius: calc(var(--r-card) - 4px);
  overflow: hidden;
  background: var(--surface);
}
.promo-media img {
  display: block;
  width: 100%;
  height: 84px;
  object-fit: cover;
}
.promo-eyebrow {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.promo-eyebrow svg {
  width: 15px;
  height: 15px;
  flex: none;
}
.tile-accent .promo-eyebrow { color: var(--accent); }
.tile-royal .promo-eyebrow { color: var(--royal-700); }
.tile-sky .promo-eyebrow { color: var(--sky-700); }
.tile-plain .promo-eyebrow { color: var(--ink-60); }
.promo-big {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-xl);
  line-height: 1.15;
  letter-spacing: -0.01em;
}
.promo-big-price {
  color: var(--price);
}
.promo-sub {
  font-size: var(--text-sm);
  color: var(--ink-60);
  line-height: 1.45;
  flex: 1;
}
.promo-cta {
  font-size: var(--text-xs);
  font-weight: 700;
  margin-top: 2px;
}
.promo-cta span {
  display: inline-block;
  transition: transform var(--dur-fast) var(--ease);
}
.promo-tile:hover .promo-cta span {
  transform: translateX(3px);
}
@media (prefers-reduced-motion: reduce) {
  .promo-tile:hover .promo-cta span {
    transform: none;
  }
}
.tile-accent .promo-cta { color: var(--accent); }
.tile-royal .promo-cta { color: var(--royal-700); }
.tile-sky .promo-cta { color: var(--sky-700); }
.tile-plain .promo-cta { color: var(--royal-700); }

/* Seasonal campaign band: one ops-scheduled row, tinted inside the token
   rules (royal for promos, terracotta for a season that is closing). */
.campaign-band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  border-radius: var(--r-card);
  padding: 16px 20px;
  color: var(--ink);
}
.campaign-band:hover {
  text-decoration: none;
  box-shadow: var(--shadow-pop);
}
.campaign-royal {
  background: var(--royal-100);
  border: 1px solid var(--royal-200);
}
.campaign-accent {
  background: var(--accent-100);
  border: 1px solid var(--accent-line);
}
.campaign-copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.campaign-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-lg);
  letter-spacing: -0.01em;
}
.campaign-blurb {
  font-size: var(--text-sm);
  color: var(--ink-60);
}
.campaign-cta {
  font-weight: 700;
  font-size: var(--text-sm);
  flex: none;
}
.campaign-royal .campaign-cta { color: var(--royal-700); }
.campaign-accent .campaign-cta { color: var(--accent); }

/* ---- Storefront polish (Sep 2026): no seams, and the bells that matter ---- */

/* Feature cell without a cover photo: a solid royal panel with the route
   as its picture, instead of a dark box with a scrim over nothing. */
.hero-feature.no-cover {
  background-color: var(--royal-900);
}
.hero-feature.no-cover::after {
  display: none;
}
.hero-feature-route {
  position: absolute;
  top: 18px;
  left: 18px;
  right: 18px;
}
/* The route strip is absolute, so the copy below it reserves its height;
   without this the kicker slides under the strip on a short cell. */
.hero-feature.no-cover .hero-feature-body {
  padding-top: 82px;
}
.hero-feature-route .route {
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
}
.hero-feature-route .route .code {
  color: #fff;
}
.hero-feature-route .route .city {
  color: var(--indigo-950-muted);
}
.hero-feature-route .route .path .line {
  border-color: rgba(255, 255, 255, 0.45);
}
/* Who is running it and how full it is, inside the feature cell. */
.hero-feature-runner {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
  font-size: var(--text-sm);
}
.hero-feature-runner .avatar {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}
.hero-feature-runner b {
  font-weight: 600;
}
.hero-feature-fill {
  flex: 1;
  min-width: 60px;
  max-width: 160px;
  height: 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.22);
  overflow: hidden;
}
.hero-feature-fill > span {
  display: block;
  height: 100%;
  background: #fff;
  border-radius: 999px;
}

/* Two campaign bands sit side by side on desktop instead of stacking. */
.campaign-grid {
  display: grid;
  gap: 12px;
  margin-top: 16px;
}
@media (min-width: 980px) {
  .campaign-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.campaign-grid .campaign-band {
  align-items: flex-start;
  flex-direction: column;
  height: 100%;
}

/* Shelves scroll sideways at every width: one tidy row with snap points,
   never a ragged second row. Arrows are a mouse convenience (home.js). */
.grid-listings.shelf-x,
.grid-cards.shelf-x {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  gap: 14px;
  padding-bottom: 6px;
  scrollbar-width: none;
  scroll-padding-left: 0;
}
.grid-listings.shelf-x::-webkit-scrollbar,
.grid-cards.shelf-x::-webkit-scrollbar {
  display: none;
}
.grid-listings.shelf-x > * {
  flex: 0 0 auto;
  width: 42vw;
  max-width: 185px;
  scroll-snap-align: start;
}
.grid-cards.shelf-x > * {
  flex: 0 0 auto;
  width: 82vw;
  max-width: 320px;
  scroll-snap-align: start;
}
@media (min-width: 700px) {
  .grid-listings.shelf-x > * {
    width: 208px;
    max-width: none;
  }
  .grid-cards.shelf-x > * {
    width: 300px;
    max-width: none;
  }
}
.shelf-nav {
  display: none;
  gap: 6px;
  flex: none;
}
@media (hover: hover) and (min-width: 980px) {
  .shelf-nav {
    display: inline-flex;
  }
}
.shelf-nav button {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
  display: grid;
  place-items: center;
  cursor: pointer;
}
.shelf-nav button svg {
  width: 16px;
  height: 16px;
}
.shelf-nav button:hover:not([disabled]) {
  border-color: var(--royal-500);
  color: var(--royal-700);
}
.shelf-nav button[disabled] {
  opacity: 0.35;
  cursor: default;
}
.section-head .section-tools {
  display: flex;
  align-items: center;
  gap: 14px;
  flex: none;
}

/* Listing without a photo: the department glyph, so a card never shows an
   empty white square.

   Quietened 14 Sep 2026. At 36% of a square tile in --royal-500 the glyph
   was the loudest thing in the grid, so a shelf of unphotographed items read
   as a wall of purple icons rather than as products, and a GHS 90,000 car
   announced itself with a cartoon. It is a fallback and should look like
   one: smaller, in the muted ink the rest of the system uses for absent
   information, on the soft line tint the photo box already carries. The
   glyph still says which department the item is in, which is the whole of
   its job. Fixing the CONTENT gap is a separate matter from making the gap
   ugly; this only stops it shouting. */
.listing-card .photo-ph,
.listing-hero .photo-ph {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--line-soft);
  color: var(--ink-40);
}
.listing-card .photo-ph svg,
.listing-hero .photo-ph svg {
  width: 22%;
  height: 22%;
  opacity: 0.55;
}
/* 22% of a hero is a 130px glyph, which is the shouting the rule above was
   written to stop. The hero is one box on one page, not a wall of them, so
   it takes a fixed size rather than a share of itself. */
.listing-hero .photo-ph svg {
  width: 64px;
  height: 64px;
}

/* Meet the runners: the people behind the runs, badges attached.

   A shelf on a phone, a row of four on a desk (23 Sep 2026). As a two-column
   grid at 390px three runners made two cards and an orphan, and every card
   was 170px wide, so "Test Runner One" broke into three lines beside its
   avatar. Every other strip on the page already scrolls sideways in one
   tidy row; this one now does the same, and a card wide enough for a name
   is what makes the name the trust element it is meant to be. */
.spotlight {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  gap: 12px;
  padding-bottom: 6px;
  scrollbar-width: none;
}
.spotlight::-webkit-scrollbar {
  display: none;
}
.spotlight > * {
  flex: 0 0 auto;
  width: 72vw;
  max-width: 264px;
  scroll-snap-align: start;
}
@media (min-width: 980px) {
  .spotlight {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    overflow: visible;
    padding-bottom: 0;
  }
  .spotlight > * {
    width: auto;
    max-width: none;
  }
}
.spot-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}
.spot-head {
  display: flex;
  /* Top, not centre: the line under the name is one line for a runner with
     nothing open and two for one with a run, and centring then lifts the
     name of the shorter card out of line with its neighbour. */
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
  /* And the slack a varying head leaves goes UNDER the head, not under the
     badges. A name wraps to two lines on one card and not on its neighbour
     (which is the rule: a runner's name is the trust element and must never
     become "Kojo Mens..."), so the badge rows sat 41px apart on a phone,
     across the one line a reader scans a row of people by. Absorbing the
     slack here anchors the stars, the badges and the link to the bottom of
     every card in the row without clamping a single name. */
  flex: 1;
}
/* A runner's name is the trust element on this card, so it wraps to a second
   line rather than becoming "Kojo Mens...". */
.spot-name {
  font-weight: 700;
  overflow-wrap: anywhere;
}
.spot-sub {
  font-size: var(--text-xs);
  color: var(--ink-60);
}
.spot-stars {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-xs);
  font-weight: 600;
}
.spot-stars svg {
  width: 14px;
  height: 14px;
  fill: var(--gold);
}
.spot-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.spot-foot {
  /* No margin-top:auto any more: .spot-head absorbs the free space above,
     so there is none left here and the rule only read as if it were the
     thing doing the aligning. */
  /* The link reads "See their runs -> " with a space in the markup, but the
     shared 44px touch-target rule in app.css makes it inline-flex, and a
     flex container drops the whitespace between its items: it rendered as
     "See their runs->" while every other arrow link on the page kept its
     space. The gap puts it back rather than the markup faking one. */
  gap: 4px;
  padding-top: 6px;
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--royal-700);
}

/* What buyers say: verified ratings with words, on paper. */
.reviews {
  display: grid;
  gap: 12px;
}
@media (min-width: 720px) {
  .reviews {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* Live feed rows carry the run's cover as a thumb when there is one. */
.activity-thumb {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: 9px;
  object-fit: cover;
  background: var(--paper);
}

/* Closing band: the one thing to do next.

   Moved off indigo-950 onto --night (owner decision 14 Sep 2026). It used to
   carry .hero-dark like the header and the escrow hero, which meant the
   signed-out home showed three heavy indigo slabs and the buyer had no way
   to tell the band that is about their money from the band that is about
   signing up. FRONTEND.md reserves indigo-950 for the header and the
   escrow/wallet hero; this is that rule being kept. merch.css loads after
   app.css, so naming .cta-band here beats .hero-dark at equal specificity
   without editing it. White on --night is 17.46:1. */
.cta-band {
  padding: 36px 0;
  background: var(--night);
}
.cta-band-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  flex-wrap: wrap;
}
.cta-band h2 {
  color: #fff;
  font-size: var(--text-xl);
}
.cta-band p {
  color: var(--night-muted);
  margin-top: 6px;
  max-width: 56ch;
}
.cta-band-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
@media (min-width: 980px) {
  .cta-band {
    padding: 44px 0;
  }
  .cta-band h2 {
    font-size: var(--text-2xl);
  }
}

/* ---- the escrow line, inside the hero (14 Sep 2026) ----------------------

   The buyer's own money, carried as the first thing inside the hero band
   rather than as a second dark section stacked above it. It used to be a
   .portal-hero section of its own; two siblings both carrying .hero-dark
   merged into one 1,060px indigo mass on a phone, and the effect was that
   the buyer's GHS 680 outstanding read as a sub-header of the marketing
   hero. It has no ground of its own on purpose: the hero supplies it, and a
   second tint inside the same band would rebuild the seam this removes.

   The rule below it is the only separation it needs, and it is the reason
   this is not just a div: it says "your money" ends here and "the shop"
   begins, inside one surface. */
.escrow-line {
  padding: 18px 0 16px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
  margin-bottom: 4px;
}
.escrow-line .money-lg {
  /* One step down from the portal's hero numeral. This is a status line on a
     shopping page, not the whole subject of the page as it is on /my/. */
  font-size: var(--text-2xl);
  line-height: 1.15;
}
.escrow-line .btn {
  /* The pay CTA must not sit so tight to the numeral that the two read as one
     object; baseline alignment on the row handles the rest. */
  margin-left: 2px;
}
@media (min-width: 980px) {
  .escrow-line {
    padding: 22px 0 18px;
  }
}
/* ---- end escrow line ---- */

/* ---------------------------------------------------------------------------
   The departments band (23 Sep 2026, owner: "shop by category must be
   enhanced and made better with some accent background").

   What was wrong was not the discs, it was the ground. Eleven circles of
   --line-soft on --paper, between two white shelves, gave the row nothing to
   sit on: the flattest 120px on the page, and the page's only navigation.
   --sand is the one warm ground the palette has and the only token added for
   exactly this job; the trust band uses it and nothing else does, so the
   storefront now alternates cool, warm, cool as it scrolls instead of
   reading as one grey column of eleven sections.

   No new colour, no gradient, no shadow: the band is a flat fill between two
   hairlines, the discs go white so they read as objects on that ground
   rather than holes cut out of it, and the only thing added to each circle
   is the number of products actually behind it.
   --------------------------------------------------------------------------- */
.dept-band {
  background: var(--sand);
  border-top: 1px solid var(--sand-line);
  border-bottom: 1px solid var(--sand-line);
  color: var(--ink);
  padding: 20px 0 22px;
  margin-top: 18px;
}
.dept-band-head {
  margin-bottom: 10px;
}
/* White discs with a warm hairline. On --paper the disc is a grey fill on a
   grey page; on --sand that same fill disappears into the band. */
.dept-band .cat-circle .disc {
  background: var(--surface);
  border-color: var(--sand-line);
}
.dept-band .cat-circle.active .disc {
  background: var(--royal-100);
  border-color: var(--royal-700);
}
/* The count under the name. --text-xs is the floor, so it is not smaller
   than the name, only quieter; the negative margin cancels most of the
   column gap so the pair reads as one label in two lines. */
.cat-count {
  margin-top: -4px;
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1.2;
  color: var(--ink-40);
  text-align: center;
}

/* The products under the circles: the same buy tiles as the strips at the
   top of the page, on a hairline of their own inside the band, so the band
   reads as one thought (here are the departments, here is what is in them)
   rather than as two sections that happen to share a colour. */
.dept-mix {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--sand-line);
}
.dept-mix-head {
  margin-bottom: 12px;
}
@media (min-width: 980px) {
  .dept-band {
    padding: 28px 0 30px;
  }
  .dept-mix {
    margin-top: 24px;
    padding-top: 22px;
  }
}

/* ---- The hero carousel (22 Sep 2026) ------------------------------------ */

/* The runs closing next, one feature card each, in a native snap scroller.
   Swiped, never rotated. The track owns the grid cell the single feature
   card used to own; every slide is as wide as the track minus a sliver, so
   the next card peeks in from the right on a phone and says "there is more"
   without a hint. */
.hero-carousel {
  position: relative;
  min-width: 0;
}
.hero-track {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
  border-radius: var(--r-card);
}
.hero-track::-webkit-scrollbar {
  display: none;
}
.hero-track:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}
.hero-track > .hero-feature {
  flex: 0 0 calc(100% - 28px);
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
/* One slide fills the track, no peek: nothing to swipe to. */
.hero-track > .hero-feature:only-child {
  flex-basis: 100%;
}
/* The cover is an image now, not a background, so the slides after the
   first can carry loading="lazy" and cost nothing until they are swiped
   to. It sits under the same gradient the background sat under. */
.hero-feature-cover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.hero-feature::after {
  z-index: 0;
}
.hero-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 8px;
  /* Above the slide body (z-index 1), or the dots cannot be tapped. */
  z-index: 2;
  display: flex;
  justify-content: center;
  gap: 6px;
  pointer-events: none;
}
.hero-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.45);
  pointer-events: auto;
  cursor: pointer;
  /* 44px of tap on an 8px dot. */
  position: relative;
}
.hero-dot::after {
  content: "";
  position: absolute;
  inset: -18px -10px;
}
.hero-dot.is-on {
  background: #fff;
  width: 20px;
  border-radius: 4px;
}
.hero-dot:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}
.hero-arrow {
  /* Bottom-right corner, as a pair, where the slide has nothing of its
     own: mid-height on the left they sat over the title's first letter. */
  position: absolute;
  bottom: 14px;
  right: 62px;
  z-index: 2;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: 50%;
  background: rgba(23, 16, 64, 0.55);
  color: #fff;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.hero-arrow.is-next {
  right: 14px;
}
.hero-arrow svg {
  width: 20px;
  height: 20px;
}
.hero-arrow:disabled {
  opacity: 0.35;
  cursor: default;
}
.hero-arrow:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}
@media (min-width: 980px) {
  /* A desk has no thumb: full-width slides and arrows instead of the peek. */
  .hero-track > .hero-feature {
    flex-basis: 100%;
  }
  .hero-arrow[hidden] {
    display: grid;
  }
}
@media (hover: none) {
  .hero-arrow {
    display: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-track {
    scroll-behavior: auto;
  }
}

/* ---- The hero carousel, poster edition (22 Sep 2026) --------------------- */

/* Owner: "way better, this is clunky". The first cut was the old panel
   inside a scroller: an indigo box on an indigo band with a boxed route
   strip inside it. This is a poster: the cover fills the card under a scrim,
   the two facts that decide a tap ride the top corners, and everything a
   buyer needs stacks at the foot in one column. Full bleed on a phone, so
   the cards run to the screen edge like an app and the next one shows a
   real edge rather than a sliver of gutter. */
.hero-carousel {
  margin-inline: -16px;
}
.hero-track {
  gap: 12px;
  padding-inline: 16px;
  scroll-padding-inline: 16px;
  border-radius: 0;
}
.hero-track > .hero-feature {
  flex: 0 0 calc(100% - 44px);
  transition:
    transform var(--dur-med) var(--ease),
    opacity var(--dur-med) var(--ease);
}
.hero-track > .hero-feature:only-child {
  flex-basis: 100%;
}
.hero-track > .hero-feature.is-off {
  transform: scale(0.965);
  opacity: 0.78;
}
.hero-feature {
  min-height: 0;
  aspect-ratio: 4 / 5;
  max-height: 560px;
  border-radius: 20px;
  background-color: var(--royal-900);
  /* A hairline of light on the edge so the card reads as an object on the
     indigo band rather than a darker patch of it. */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}
.hero-feature::after {
  z-index: 0;
  background: linear-gradient(
    180deg,
    rgba(23, 16, 64, 0.4) 0%,
    rgba(23, 16, 64, 0) 26%,
    rgba(23, 16, 64, 0.1) 48%,
    rgba(23, 16, 64, 0.86) 76%,
    rgba(23, 16, 64, 0.96) 100%
  );
}
.hero-feature.no-cover::after {
  display: block;
  background: linear-gradient(180deg, rgba(23, 16, 64, 0) 40%, rgba(23, 16, 64, 0.55) 100%);
}
.hero-feature-sky {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 70% at 12% 0%, var(--royal-500) 0%, var(--royal-700) 32%, var(--royal-900) 66%, #1b1445 100%);
}
.hero-feature-sky::before,
.hero-feature-sky::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
}
.hero-feature-sky::before {
  width: 78%;
  aspect-ratio: 1;
  right: -24%;
  top: -22%;
}
.hero-feature-sky::after {
  width: 46%;
  aspect-ratio: 1;
  left: -14%;
  bottom: 8%;
}
/* The two facts that decide a tap, in the top corners. */
.hero-feature-top {
  position: absolute;
  z-index: 2;
  top: 14px;
  left: 14px;
  right: 14px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
}
.hero-feature-kicker {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 0 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: #fff;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.hero-feature-closes {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  padding: 0 12px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.92);
  color: var(--indigo-950);
  font-size: var(--text-xs);
  font-weight: 800;
  white-space: nowrap;
}
.hero-feature-closes.is-urgent {
  background: var(--accent-600);
  color: #fff;
}
/* The foot: one column, in the order a buyer reads it. */
.hero-feature-body {
  gap: 8px;
  padding: 16px;
}
.hero-feature.no-cover .hero-feature-body {
  padding-top: 16px;
}
.hero-feature-route {
  position: static;
  align-self: flex-start;
  margin-bottom: 2px;
}
.hero-feature-route .route {
  display: inline-flex;
  gap: 10px;
  padding: 6px 10px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  font-size: var(--text-xs);
}
.hero-feature-route .route .code {
  font-size: var(--text-md);
}
.hero-feature-route .route .city {
  display: none;
}
.hero-feature-route .route .path {
  min-width: 44px;
}
.hero-feature-title {
  font-size: clamp(26px, 7.4vw, 32px);
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -0.02em;
  text-shadow: 0 2px 12px rgba(23, 16, 64, 0.5);
  text-wrap: balance;
}
.hero-feature-meta {
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.86);
}
.hero-feature-runner {
  margin-top: 4px;
  gap: 8px;
}
.hero-feature-runner .avatar {
  width: 28px;
  height: 28px;
  font-size: 12px;
  background: rgba(255, 255, 255, 0.22);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.85);
}
.hero-feature-count {
  margin-left: auto;
  color: rgba(255, 255, 255, 0.86);
  font-variant-numeric: tabular-nums;
}
.hero-feature-fill {
  flex: none;
  width: 100%;
  max-width: none;
  height: 5px;
  background: rgba(255, 255, 255, 0.25);
}
.hero-feature-row {
  margin-top: 6px;
}
.hero-feature-cta {
  flex: 1;
  justify-content: center;
  min-height: 48px;
  font-weight: 800;
  border-radius: 999px;
}
/* Dots and arrows INSIDE the card (owner, 22 Sep 2026: "the controls should
   be contained within, not outside the images"). The nav is a layer over
   the track that lets every touch through except on the controls
   themselves: dots centred just under the top chips, arrows in the
   bottom-right corner beside the door on a desk. */
.hero-nav {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}
.hero-dots {
  position: absolute;
  left: 16px;
  right: 16px;
  top: 54px;
  bottom: auto;
  display: flex;
  justify-content: center;
  gap: 6px;
  pointer-events: none;
}
.hero-dot {
  width: 6px;
  height: 6px;
  background: rgba(255, 255, 255, 0.55);
  /* A hairline of indigo so a white dot still reads over a white sky. */
  box-shadow: 0 0 0 1px rgba(23, 16, 64, 0.35);
  pointer-events: auto;
  transition: width var(--dur-fast) var(--ease), background var(--dur-fast);
}
.hero-dot.is-on {
  width: 22px;
  border-radius: 3px;
  background: #fff;
}
.hero-arrows {
  position: absolute;
  right: 22px;
  bottom: 28px;
  display: flex;
  gap: 8px;
  pointer-events: auto;
}
.hero-arrow,
.hero-arrow.is-next {
  position: static;
  width: 36px;
  height: 36px;
  margin: 0;
  background: rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: #fff;
}
.hero-arrow:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.26);
}
.hero-arrow svg {
  width: 18px;
  height: 18px;
}
@media (min-width: 980px) {
  .hero-carousel {
    margin-inline: 0;
  }
  .hero-track {
    padding-inline: 0;
    scroll-padding-inline: 0;
    border-radius: 20px;
  }
  .hero-track > .hero-feature {
    flex-basis: 100%;
  }
  .hero-track > .hero-feature.is-off {
    transform: none;
    opacity: 1;
  }
  .hero-feature {
    aspect-ratio: auto;
    min-height: 420px;
    max-height: none;
  }
  .hero-feature-title {
    font-size: 34px;
  }
  .hero-feature-body {
    padding: 22px;
    max-width: 520px;
  }
  .hero-feature-top {
    top: 18px;
    left: 18px;
    right: 18px;
  }
  .hero-feature-cta {
    flex: 0 0 auto;
    min-width: 200px;
  }
  .hero-dots {
    left: 22px;
    right: 22px;
    top: 60px;
  }
}
@media (prefers-reduced-motion: reduce) {
  .hero-track > .hero-feature {
    transition: none;
  }
  .hero-dot {
    transition: none;
  }
}
