/* ==========================================================================
   TeamTrek V3 — Styles for the three product mocks v1 does not have
   --------------------------------------------------------------------------
   Loaded only by index-v3.html. Every selector here is new, so index.html
   (v1, which loads the same css/ directory) is unaffected: it never renders
   markup carrying these classes.

   The Insights, Project management and People mocks reuse the existing
   atoms wherever one exists — .mock, .mock--pad, .mock__panel, .rep__head,
   .rep__export, .rep__budget-*, .mock-avatar, .badge-pill — so this file
   only holds what those four tabs did not already need: three small tables,
   a delta figure and two selector strips.
   ========================================================================== */

/* One missing badge tone. Owner and Manager carry weight, so they keep the
   semantic colours; Member and Viewer are the default state and should not
   read as a status at all. */
.badge--neutral { background: var(--neutral-100); color: var(--neutral-700); }
.badge--neutral .dot { background: var(--neutral-400); }

/* --------------------------------------------------------------------------
   Insights mock
   -------------------------------------------------------------------------- */
.ins__periods { display: inline-flex; gap: .25rem; border-radius: var(--r-pill); background: var(--neutral-100); padding: .25rem; }
.ins__period { border-radius: var(--r-pill); padding: .3125rem .625rem; font-size: 11px; font-weight: 600; color: var(--neutral-500); }
.ins__period.is-active { background: #fff; color: var(--neutral-900); box-shadow: var(--shadow-01); }

.ins__stats {
  margin-bottom: 1rem;
  display: grid; gap: .625rem;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
}
.ins__stat { border-radius: var(--r-md); border: 1px solid var(--neutral-200); padding: .75rem; }
.ins__stat-label { font-size: 11px; font-weight: 500; color: var(--neutral-500); }
.ins__stat-value { font-size: 1.25rem; font-weight: 700; letter-spacing: -.02em; color: var(--neutral-900); }
.ins__delta { display: flex; align-items: center; gap: .1875rem; font-size: 10px; font-weight: 600; }
.ins__delta .icon { width: 12px; height: 12px; }
/* Direction, not sentiment: idle time falling is a down arrow and good news,
   so the arrow says which way the number moved and the label says what it
   was. Colour follows the arrow for that reason only. */
.ins__delta--up { color: var(--success-text); }
.ins__delta--down { color: var(--neutral-500); }

.ins__grid { display: grid; gap: 1rem; }
.ins__ai { background: var(--primary-50); border-color: var(--primary-100); }
.ins__ai-head {
  display: flex; align-items: center; gap: .375rem;
  margin-bottom: .75rem;
  font-size: 13px; font-weight: 600; color: var(--primary-800);
}
.ins__ai-head .icon { color: var(--primary-600); }
.ins__ai-list { display: flex; flex-direction: column; gap: .625rem; }
.ins__ai-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  font-size: 11px; font-weight: 500; color: var(--neutral-700);
}

/* --------------------------------------------------------------------------
   Project management mock
   -------------------------------------------------------------------------- */
.pm__table { padding: 0; overflow: hidden; }
.pm__row {
  display: grid; align-items: center;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) auto minmax(0, .8fr);
  gap: .75rem;
  padding: .625rem .875rem;
  border-top: 1px solid var(--neutral-200);
  font-size: 12px;
}
.pm__row:first-child { border-top: 0; }
.pm__row--head {
  background: var(--neutral-50);
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--neutral-500);
}
.pm__name { display: flex; align-items: center; gap: .375rem; min-width: 0; font-weight: 600; color: var(--neutral-900); }
.pm__name .icon { flex: none; color: var(--primary-600); }
.pm__client { color: var(--neutral-600); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Overlapped avatars: the stack says "a few people" faster than a count,
   and negative margin is cheaper than a wrapper per avatar. */
.pm__stack { display: flex; }
.pm__stack .mock-avatar { width: 24px; height: 24px; font-size: 9px; border: 2px solid #fff; }
.pm__stack .mock-avatar + .mock-avatar { margin-left: -.5rem; }

.pm__tasks { margin-top: 1rem; }
.pm__task { display: flex; align-items: center; gap: .625rem; font-size: 12px; }
.pm__task + .pm__task { margin-top: .5rem; padding-top: .5rem; border-top: 1px solid var(--neutral-150); }
.pm__check {
  display: grid; place-items: center; flex: none;
  width: 18px; height: 18px;
  border-radius: 50%; border: 1.5px solid var(--neutral-300);
  color: var(--neutral-300);
}
.pm__check .icon { width: 11px; height: 11px; }
.pm__task-info { flex: 1; min-width: 0; }
.pm__task-info b { display: block; font-weight: 600; color: var(--neutral-900); }
.pm__task-info span { display: block; font-size: 11px; color: var(--neutral-500); }
.pm__task .mock-avatar { width: 24px; height: 24px; font-size: 9px; }

/* --------------------------------------------------------------------------
   People mock
   -------------------------------------------------------------------------- */
.ppl__views { margin-bottom: 1rem; display: flex; gap: .375rem; }
.ppl__view {
  display: inline-flex; align-items: center; gap: .375rem;
  border-radius: var(--r-pill); border: 1px solid var(--neutral-200);
  padding: .3125rem .6875rem;
  font-size: 11px; font-weight: 600; color: var(--neutral-500);
}
.ppl__view b { font-weight: 700; color: var(--neutral-400); }
.ppl__view.is-active { border-color: var(--primary-200); background: var(--primary-50); color: var(--primary-800); }
.ppl__view.is-active b { color: var(--primary-600); }

.ppl__table { padding: 0; overflow: hidden; }
.ppl__row {
  display: grid; align-items: center;
  grid-template-columns: minmax(0, 1.6fr) auto minmax(0, .9fr) minmax(0, .7fr);
  gap: .75rem;
  padding: .5rem .875rem;
  border-top: 1px solid var(--neutral-200);
  font-size: 12px;
}
.ppl__row:first-child { border-top: 0; }
.ppl__row--head {
  background: var(--neutral-50);
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--neutral-500);
}
.ppl__who { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.ppl__who .mock-avatar { width: 28px; height: 28px; font-size: 10px; }
.ppl__id { min-width: 0; }
.ppl__id b { display: block; font-weight: 600; color: var(--neutral-900); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ppl__id span { display: block; font-size: 11px; color: var(--neutral-500); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ppl__mgr { color: var(--neutral-600); }
.ppl__pr { font-weight: 600; color: var(--neutral-700); }

/* --------------------------------------------------------------------------
   Wider frames get two columns, matching how the Reports mock behaves
   -------------------------------------------------------------------------- */
@media (min-width: 700px) {
  .ins__grid { grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); }
}

/* --------------------------------------------------------------------------
   Narrow: drop the columns that repeat information available elsewhere,
   rather than letting four columns crush to unreadable widths
   -------------------------------------------------------------------------- */
@media (max-width: 560px) {
  .pm__row { grid-template-columns: minmax(0, 1.4fr) auto minmax(0, .8fr); }
  .pm__row > .pm__client,
  .pm__row--head > :nth-child(2) { display: none; }

  .ppl__row { grid-template-columns: minmax(0, 1.6fr) auto minmax(0, .7fr); }
  .ppl__row > .ppl__mgr,
  .ppl__row--head > :nth-child(3) { display: none; }
}

/* ==========================================================================
   Use cases (07) — the right-hand record figure
   --------------------------------------------------------------------------
   Replaces the .statcard the shared js/tabs.js renders. The old card held
   one large audience number ("5 zones", "+9%"); the new tabs are business
   goals, and a goal rests on a record rather than on a headline figure — so
   this shows the record each goal produces: what it is, three lines of it,
   and the one total that gets acted on.

   The shape is identical across all five tabs on purpose. Moving between
   them then compares like with like, instead of asking the reader to learn
   a new layout at every click.

   Loaded only by index-v3.html, so index.html keeps its .statcard.
   ========================================================================== */

.ucfig {
  margin: 0;
  border: 1px solid var(--neutral-200); border-radius: var(--r-lg);
  background: #fff; box-shadow: var(--shadow-02);
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
}
.ucfig__head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: .5rem;
  padding-bottom: .875rem; margin-bottom: 1.125rem;
  border-bottom: 1px solid var(--neutral-150);
}
.ucfig__title { font-size: var(--text-body); font-weight: var(--weight-bold); letter-spacing: -.01em; color: var(--neutral-900); }
.ucfig__meta { font-size: var(--text-xs); color: var(--neutral-500); font-variant-numeric: tabular-nums; }

.ucfig__rows { display: flex; flex-direction: column; gap: 1.125rem; }
.ucfig__row-head { display: flex; align-items: flex-start; justify-content: space-between; gap: .75rem; margin-bottom: .5rem; }
.ucfig__name { min-width: 0; }
.ucfig__name b { display: block; font-size: var(--text-sm); font-weight: var(--weight-semi); color: var(--neutral-900); }
.ucfig__name > span { display: block; font-size: var(--text-xs); color: var(--neutral-500); }
.ucfig__val {
  display: inline-flex; align-items: center; gap: .1875rem; flex: none;
  font-size: var(--text-sm); font-weight: var(--weight-bold);
  color: var(--neutral-900); font-variant-numeric: tabular-nums;
}
.ucfig__val .icon { width: 14px; height: 14px; }
/* Tone marks a line that needs a second look — manual entry, an
   unsubmitted timesheet — not a good/bad judgement on the number. */
.ucfig__val--warning { color: var(--warning-text); }
.ucfig__val--danger { color: var(--danger-text); }
.ucfig__val--neutral { color: var(--neutral-600); }

.ucfig__track { display: block; height: 6px; border-radius: var(--r-pill); background: var(--neutral-100); overflow: hidden; }
.ucfig__bar {
  display: block; height: 100%; width: 0;
  border-radius: var(--r-pill); background: var(--primary-600);
  transition: width .82s var(--ease-brand);
}
.ucfig__bar--warning { background: var(--warning-fill); }
.ucfig__bar--danger { background: var(--danger-fill); }
.ucfig__bar--neutral { background: var(--neutral-400); }

.ucfig__total {
  margin-top: 1.5rem;
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: .5rem;
  border-radius: var(--r-md); background: var(--primary-50);
  padding: .875rem 1rem;
}
.ucfig__total-label { font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--primary-800); }
.ucfig__total-value {
  font-size: 1.375rem; font-weight: var(--weight-bold);
  letter-spacing: -.03em; color: var(--primary-900);
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   Use cases (07) — panel balance
   --------------------------------------------------------------------------
   Measured at 1440 before this block: the left column was 612px wide holding
   a 238px longest bullet, the right figure 416px wide holding a three-row
   table and a total. The wide track held the light content, which is what
   made the panel feel unbalanced — roughly 370px of dead air on every bullet
   row. The two columns also differed by 180px in height, and align-items:
   center split that into ~90px of blank above and below the left column, so
   the copy floated rather than sat.

   Four changes, in order of effect: flip the ratio, stop the float, give the
   left column a fourth element so it has a real height of its own, and rule
   the bullet rows so three short lines read as a set.

   These override css/responsive.css, which index.html also loads — hence
   they live here, in the sheet only index-v3.html pulls in.
   ========================================================================== */

/* 1 + 2. The dense column gets the wider track, and both columns start on
   the same line instead of being centred against each other. */
@media (min-width: 768px) {
  .usecases__panel {
    grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
    align-items: start;
  }
}

/* 3. Kicker: the active goal, restated as a caption. Small enough not to
   compete with the headline, and it gives the column a top edge that lines
   up with the figure's own header. */
.uc__kicker {
  display: flex; align-items: center; gap: .625rem;
  margin-bottom: 1rem;
  font-family: var(--font-mono);
  font-size: var(--text-xs); font-weight: var(--weight-semi);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--primary-700);
}
/* The tile is the section's own component at its small size — the 48px one
   was a block of colour with nothing beside it. */
.uc__kicker .tile { color: var(--primary-700); }

/* The headline no longer needs to clear a 48px tile. */
.usecases__panel .usecases__headline { margin-top: 0; }

/* 4. Ruled rows. Three short ticks floating in a wide column read as
   leftovers; a hairline under each makes them a deliberate list and gives
   the column's width something to do. */
.uc__benefits {
  margin-top: 1.5rem;
  gap: 0;
  border-top: 1px solid var(--neutral-200);
}
.uc__benefits li {
  align-items: center;
  gap: .625rem;
  padding: .8125rem 0;
  border-bottom: 1px solid var(--neutral-200);
  font-size: var(--text-sm);
}
.uc__benefits .icon { margin-top: 0; }

/* The route out. Sits below the rules with room of its own, so it reads as
   an action rather than a fourth bullet. */
.uc__link { margin-top: 1.5rem; font-size: var(--text-sm); }
.uc__link .icon { transition: transform var(--dur-micro) var(--ease-ui); }
.uc__link:hover .icon { transform: translateX(2px); }

/* ==========================================================================
   Footer link groups — collapsible below 1024px
   --------------------------------------------------------------------------
   Three lists of four or five links stacked on a phone is a long scroll for
   a footer. Below 1024px js/footer-v3.js rebuilds each group as a row that
   opens: group name, chevron, links underneath, one open at a time.

   >= 1024px nothing here applies, so laptop and desktop keep the three
   columns exactly as they were.

   The collapse is the same 0fr -> 1fr grid row the FAQ uses, so the panel
   animates to its own content height without anything measuring it.
   ========================================================================== */

@media (max-width: 1023px) {
  /* One group per row. Above 600px the base sheet puts these in three
     columns, which an accordion cannot use. */
  .footer__cols--acc { grid-template-columns: minmax(0, 1fr); gap: 0; }

  .footer__group { border-top: 1px solid rgba(255, 255, 255, .1); }
  .footer__group:last-child { border-bottom: 1px solid rgba(255, 255, 255, .1); }

  .footer__coltoggle {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    width: 100%;
    /* A footer row is a big tap target on purpose: it is the last thing on
       the page and usually reached one-handed. */
    min-height: 56px;
    padding-block: .5rem;
    text-align: left;
  }
  /* The group name steps up from the desktop micro-label: at this size it is
     a row heading you tap, not a caption above a visible list. */
  .footer__cols--acc .footer__coltitle {
    font-size: 1rem; font-weight: var(--weight-bold);
    text-transform: none; letter-spacing: -.01em; color: #fff;
  }

  .footer__colchev {
    display: grid; place-items: center; flex: none;
    width: 32px; height: 32px;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .16);
    color: var(--neutral-300);
    transition: transform var(--dur-ui) var(--ease-brand),
                background-color var(--dur-micro) var(--ease-ui),
                border-color var(--dur-micro) var(--ease-ui),
                color var(--dur-micro) var(--ease-ui);
  }
  .footer__coltoggle[aria-expanded="true"] .footer__colchev {
    transform: rotate(180deg);
    background: var(--primary-600); border-color: var(--primary-600); color: #fff;
  }

  .footer__colpanel {
    display: grid; grid-template-rows: 0fr;
    transition: grid-template-rows var(--dur-ui) var(--ease-brand);
  }
  .footer__colpanel > div { min-height: 0; overflow: hidden; }
  .footer__colpanel.is-open { grid-template-rows: 1fr; }
  /* Padding on the list rather than the panel: a closed panel with padding
     still occupies those pixels, so the rows would never fully collapse. */
  .footer__cols--acc .footer__links {
    margin-top: 0;
    padding-bottom: 1.25rem;
    gap: 0;
  }
  .footer__cols--acc .footer__links a {
    display: block;
    padding-block: .625rem;
    font-size: 1rem;
  }
}

/* Under 1024px the wordmark follows the accordion instead of a block of
   links, so it needs the space the list's own margin used to provide. */
@media (max-width: 1023px) {
  .footer__cols--acc + .footer__wordmark,
  .footer__top:has(.footer__cols--acc) + .footer__wordmark { margin-top: 2.5rem; }
}

/* ==========================================================================
   Hero — rotating headline
   --------------------------------------------------------------------------
   Three sentences take the h1 in turn, driven by js/hero-v3.js.

   The transition is a dissolve, not a wipe: a line softens out of focus and
   drifts up as it leaves, and the next drifts in from below and sharpens.
   Nothing travels far and nothing moves at a speed the eye has to track,
   which is what keeps a headline this large from feeling busy. A wipe at
   this size reads as mechanical — the text marches — where a dissolve reads
   as the sentence resolving.

   Only opacity, transform and filter are animated, so every frame is
   composited and the headline never triggers layout.

   Loaded only by index-v3.html; index.html keeps its static headline.
   ========================================================================== */

/* All three phrases share one grid cell, so the heading is always as tall as
   the tallest sentence and nothing below it moves on a swap. No measuring,
   no reserved magic number. */
.hero__rot { display: grid; }
.hero__phrase {
  grid-area: 1 / 1;
  /* Hidden phrases keep their space (that is what holds the height) but stay
     out of selection, find-in-page and the accessibility tree. */
  visibility: hidden;
}
.hero__phrase.is-on,
.hero__phrase.is-out { visibility: visible; }

.hero__line { display: block; }
.hero__line > span {
  display: block;
  opacity: 0;
  will-change: opacity, transform, filter;
}

/* The closing line of every sentence lands in the brand teal: three
   different sentences, one consistent resolution. */
.hero__line--accent { color: var(--primary-600); }

/* Per-line stagger, set from --i in the markup, so the sentence resolves top
   to bottom rather than arriving as one block. The outgoing sentence uses a
   tighter stagger and a shorter duration than the incoming one — it should
   be gone before the new line lands on top of it, not crossfading through
   it for the whole beat. */
.hero__phrase.is-on > .hero__line > span {
  animation: heroSoftIn 1.05s var(--ease-brand) forwards;
  animation-delay: calc(var(--i, 0) * .09s);
}
.hero__phrase.is-out > .hero__line > span {
  opacity: 1;
  animation: heroSoftOut .55s var(--ease-brand) forwards;
  animation-delay: calc(var(--i, 0) * .06s);
}

@keyframes heroSoftIn {
  from { opacity: 0; filter: blur(12px); transform: translateY(18px) scale(.985); }
  /* Focus arrives before the movement finishes, so the line is readable for
     most of its travel rather than sharpening at the last moment. */
  55%  { opacity: 1; filter: blur(0); }
  to   { opacity: 1; filter: blur(0); transform: translateY(0) scale(1); }
}
@keyframes heroSoftOut {
  from { opacity: 1; filter: blur(0); transform: translateY(0) scale(1); }
  to   { opacity: 0; filter: blur(9px); transform: translateY(-14px) scale(.99); }
}

/* js/hero-v3.js returns early under reduced motion, so only phrase 1 is ever
   in the DOM — but its lines still start at opacity 0, so they need to be
   resolved to their end state rather than left invisible. */
@media (prefers-reduced-motion: reduce) {
  .hero__line > span {
    opacity: 1 !important;
    filter: none !important;
    transform: none !important;
    animation: none !important;
  }
  .hero__phrase { visibility: hidden; }
  .hero__phrase.is-on { visibility: visible; }
}

/* ==========================================================================
   Hero — "What TeamTrek captures" signal strip
   --------------------------------------------------------------------------
   Ported from index-v2.html's .signals band and rebuilt in v3's vocabulary:
   .card for the surface, .tile for the icon plate, .spark for the chart,
   .eyebrow for the heading, and the --primary / --neutral ramps throughout.
   v2's own classes were not carried over — they are written against a
   different token set and would have needed that set imported with them.

   Four across on desktop, two on tablet, one on a phone. The strip closes
   the hero: the headline makes a claim, the window shows the product, and
   this says what is actually being recorded.

   Loaded only by index-v3.html.
   ========================================================================== */

.hero__signals {
  position: relative;
  /* .hero__inner is a two-column grid at >= 1024px, so the strip arrives as
     a third grid item and would sit in column 1 at 122px wide. It spans the
     whole row instead — it belongs under both columns, not beside one. */
  grid-column: 1 / -1;
  margin-top: 3.5rem;
  padding-top: 2.5rem;
  /* A hairline rather than a full band: the strip belongs to the hero, and a
     background change here would read as a new section. */
  border-top: 1px solid var(--neutral-200);
}
.hero__signals-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 1.25rem;
  margin-bottom: 1.5rem;
}
.hero__signals-lead { font-size: var(--text-sm); color: var(--neutral-600); }

.hero__signals-grid {
  display: grid; gap: 1rem;
  grid-template-columns: minmax(0, 1fr);
}
.hero__signals-grid > * { min-width: 0; }

.sig {
  display: flex; flex-direction: column; gap: .875rem;
  padding: 1.125rem;
  /* Each card rises on its own beat, ordered by --rv from the markup. A
     little longer and a little shorter a distance than the page's standard
     reveal: four cards arriving in sequence needs to feel unhurried, where
     one block arriving can afford to be brisk. */
  opacity: 0;
  transform: translateY(18px) scale(.99);
  transition: opacity .72s var(--ease-brand), transform .72s var(--ease-brand);
  transition-delay: calc(var(--rv, 0) * .09s);
}
.hero__signals-grid.is-in .sig { opacity: 1; transform: none; }
.sig__head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
.sig__kicker {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: var(--text-xs); font-weight: var(--weight-semi); color: var(--neutral-700);
}
/* Its own chip rather than .badge-pill: the label here is a state, not a
   status, so it stays neutral instead of borrowing a semantic colour. */
.sig__chip {
  border-radius: var(--r-pill);
  border: 1px solid var(--neutral-200);
  padding: .1875rem .5rem;
  font-size: 11px; font-weight: var(--weight-semi); color: var(--neutral-600);
  white-space: nowrap;
}

.sig__top { display: flex; align-items: flex-start; gap: .75rem; }
.sig__id { min-width: 0; }
.sig__title {
  display: block;
  font-size: .9375rem; font-weight: var(--weight-bold);
  letter-spacing: -.015em; color: var(--neutral-900);
}
.sig__note {
  display: block; margin-top: .25rem;
  font-size: var(--text-xs); line-height: 1.5; color: var(--neutral-600);
}

.sig__value {
  font-size: 1.5rem; font-weight: var(--weight-bold);
  letter-spacing: -.035em; color: var(--neutral-900);
  font-variant-numeric: tabular-nums; line-height: 1;
}

/* The chart fills when the strip arrives, not on load, so the four cards
   read as measurements being taken rather than four finished pictures
   already sitting there. Height comes from --h, set per bar at build time.

   The delay is composed rather than baked in: the card's own --rv, then a
   beat for the card to finish rising, then the bar's index. A bar therefore
   never starts before the card holding it has landed, whatever order the
   cards are in or how many there are. */
.sig__spark { height: 34px; gap: 2px; }
.sig__spark span {
  height: 0;
  transition: height .62s var(--ease-brand);
  transition-delay: calc(var(--rv, 0) * .09s + .34s + var(--b, 0) * .026s);
}
.hero__signals-grid.is-in .sig__spark span { height: var(--h); }
.sig__spark span.is-peak { background: var(--primary-600); }

.sig__range {
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: 11px;
  color: var(--neutral-600); font-variant-numeric: tabular-nums;
}

@media (min-width: 600px) {
  .hero__signals-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .hero__signals-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* The whole arrival is decoration: every figure the bars draw is already
   written out as text in the same card. */
@media (prefers-reduced-motion: reduce) {
  .sig {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .sig__spark span { height: var(--h) !important; transition: none !important; }
}

/* ---- Space the removed rotating card used to occupy --------------------
   css/responsive.css reserves 15.75rem under the hero figure at >= 1024px
   for the clock chip and the feature card. index-v3.html no longer carries
   that card — the signal strip below says the same four things — so all
   that is left to clear is the frame's own shadow. index.html loads the
   same responsive sheet and still has its card, which is why this override
   lives here.
   ------------------------------------------------------------------------ */
@media (min-width: 1024px) {
  .hero__figure { padding-bottom: 2rem; }
}

/* ==========================================================================
   Proof strip — single-line logo marquee
   --------------------------------------------------------------------------
   Grayscale and dimmed at rest; the logo under the cursor comes up to full
   colour. Hovering the strip pauses the scroll — colouring a target that is
   still sliding out from under the pointer is a worse interaction than a
   row that stops.

   Built on the page's existing @keyframes marquee (a max-content track of
   two identical halves, translated -50%), so this is the same engine as the
   workspace cards and the feature pills further down rather than a third
   implementation of the same idea.

   A note on "original colour": these five marks are drawn in currentColor
   and have no brand palette to restore, so hover reveals the product's own
   accent — the mark in --primary-600, the wordmark in --neutral-900. If the
   logos are ever replaced with real ones carrying real colours, delete the
   two colour declarations in .proof__logo:hover and the marks will come up
   in their own.

   index.html loads the same css/ and keeps its static row, which is why all
   of this keys off a v3-only attribute.
   ========================================================================== */

.proof__label { margin-bottom: 1.5rem; }

.proof__marquee {
  position: relative;
  /* Widened to the container's padding box rather than its text column, so
     the fades sit outside the label's own left edge and the row reads as
     passing through the section instead of starting inside it.

     Not true full-bleed: .container is max-width 1280px, so this recovers
     the 40px gutter and stops there. A 100vw bleed would reach the viewport
     edge but counts the scrollbar and brings horizontal overflow with it —
     and the page's other two marquees are container-width for the same
     reason, so this matches them. */
  margin-inline: calc(var(--container-pad) * -1);
  /* The clip belongs here, not on the track. The track is width: max-content
     — it is sized to its own content, so overflow on it has nothing to cut.
     Without this the row spilled past the section to the viewport edge and
     the fades had nothing to fade against. */
  overflow: hidden;
}
/* Hidden until the track is filled, so the un-cloned five logos never flash
   as a short row before the clones land. */
.proof__marquee:not(.is-ready) .proof__track { visibility: hidden; }

/* The fades are the section's own ground, so the row dissolves rather than
   being cut off by a box edge. */
.proof__marquee::before,
.proof__marquee::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 72px;
  z-index: 2; pointer-events: none;
}
.proof__marquee::before { left: 0;  background: linear-gradient(90deg, var(--neutral-50), transparent); }
.proof__marquee::after  { right: 0; background: linear-gradient(270deg, var(--neutral-50), transparent); }

.proof__track {
  display: flex; width: max-content;
  animation: marquee 44s linear infinite;
  will-change: transform;
}
/* Pause on hover and on keyboard focus reaching any logo. */
.proof__marquee:hover .proof__track,
.proof__marquee:focus-within .proof__track { animation-play-state: paused; }

.proof__set { display: flex; align-items: center; gap: 3.5rem; padding-inline: 1.75rem; }

/* ---- The logos ----------------------------------------------------------
   Grayscale at rest. The filter and the colours are separate properties on
   purpose: grayscale alone does nothing to a mark already drawn in grey, so
   the rest state is also dimmed, and hover restores opacity, ink and accent
   together.
   ------------------------------------------------------------------------ */
.proof__marquee .proof__logo {
  flex: none;
  display: inline-flex; align-items: center; gap: .5625rem; height: 28px;
  font-size: 1.0625rem; font-weight: var(--weight-bold);
  letter-spacing: -.025em;
  color: var(--neutral-500);
  filter: grayscale(1);
  opacity: .62;
  transition: opacity .34s var(--ease-ui),
              filter .34s var(--ease-ui),
              color .34s var(--ease-ui);
}
.proof__marquee .proof__logo svg {
  width: 24px; height: 24px; flex: none;
  transition: color .34s var(--ease-ui), transform .34s var(--ease-brand);
}

.proof__marquee .proof__logo:hover,
.proof__marquee .proof__logo:focus-visible {
  filter: grayscale(0);
  opacity: 1;
  color: var(--neutral-900);
}
.proof__marquee .proof__logo:hover svg,
.proof__marquee .proof__logo:focus-visible svg {
  color: var(--primary-600);
  transform: scale(1.06);
}

/* ---- Arrival -----------------------------------------------------------
   The strip fades in as one object. The logos are already moving, so a
   per-logo stagger on top of the scroll read as two motions competing.
   ------------------------------------------------------------------------ */
[data-proof-marquee] {
  opacity: 0;
  transition: opacity .6s var(--ease-brand);
}
[data-proof-marquee].is-in { opacity: 1; }

/* ---- Reduced motion ---------------------------------------------------
   No scroll at all: the track stops and the first set is shown as a plain
   wrapped row. Hover still colours a logo, since that is a response to the
   reader rather than ambient movement.
   ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  [data-proof-marquee] { opacity: 1 !important; transition: none !important; }
  .proof__track { animation: none !important; width: auto; flex-wrap: wrap; }
  .proof__set--clone { display: none; }
  .proof__set { flex-wrap: wrap; gap: 1.25rem 2.25rem; padding-inline: 0; }
  .proof__marquee { margin-inline: 0; }
  .proof__marquee::before,
  .proof__marquee::after { display: none; }
}

/* ==========================================================================
   How it works — horizontal stepper
   --------------------------------------------------------------------------
   Four steps across the page instead of stacked down a column, with the
   stage below rather than beside them. Driven by js/how-v3.js.

   The section pins for the length of its scroll track: a horizontal rail
   reads as a timeline, and a timeline you are travelling along should hold
   still while it advances. Same pattern as the workspace assembly above, so
   the page has one idea about scroll-driven stages instead of two.

   index.html loads the same css/ and keeps its vertical stepper, which is
   why every selector here is either new or scoped under .how__pin.
   ========================================================================== */

/* The section's own padding is replaced by the pin's centring, so .section
   is dropped from the class list and the vertical rhythm comes from here. */
.how__scroll { position: relative; height: 280vh; }
.how__pin {
  position: sticky; top: 0;
  min-height: 100vh;
  display: flex; flex-direction: column; justify-content: center;
  padding-block: 4rem;
}
.how__pin > .section__head { margin-bottom: 3rem; }

/* ---- The rail ----------------------------------------------------------
   One line behind four dots, not three segments between them: a single
   object is what fills, and a segmented one would need four separate fills
   kept in step with each other.
   ------------------------------------------------------------------------ */
.howh { position: relative; --howh-gap: 2rem; }
.howh__rail {
  /* Spans dot centre to dot centre. The dots sit at the left edge of their
     own column, not centred in it, so a symmetric inset left the first dot
     stranded off the start of the line — this is measured from the grid
     instead: 6px in on the left (the dot's radius), and on the right
     whatever is left of the fourth column after its dot.

     For a 4-column 1fr grid with gap g the column width is (W - 3g)/4, so
     the last dot centre sits at 3(W - 3g)/4 + 3g + 6 = 0.75W + 0.75g + 6,
     which leaves 25% - 0.75g - 6px on the right. --howh-gap keeps the gap
     in one place so the two cannot drift. */
  position: absolute; top: 5px; height: 2px;
  left: 6px;
  right: calc(25% - (var(--howh-gap) * 0.75) - 6px);
}
.howh__rail-line,
.howh__rail-fill {
  position: absolute; inset-block: 0; left: 0;
  border-radius: var(--r-pill);
}
.howh__rail-line { right: 0; background: var(--ink-line); }
.howh__rail-fill {
  width: 0;
  background: linear-gradient(90deg, var(--primary-700), var(--primary-400));
  /* Short, because the driver already updates every frame — this only
     smooths the gap between frames rather than animating the travel. */
  transition: width .12s linear;
}

.howh__steps {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--howh-gap, 2rem);
}

/* ---- A step ------------------------------------------------------------ */
.how__pin .how__step {
  position: relative;
  padding-top: 2.25rem;              /* room for the dot above the number */
  cursor: pointer;
  opacity: .38;
  /* No transform on the active step here. The vertical version nudged it
     8px right to read as progress; in a row that shift would push a column
     out of alignment with its own dot on the rail. Progress is carried by
     the fill and the dot instead. */
  transition: opacity var(--dur-ui) var(--ease-ui);
}
.how__pin .how__step.is-done { opacity: .62; }
.how__pin .how__step.is-active {
  opacity: 1;
  /* Cancels the shared rule's 8px nudge. In the vertical layout that shift
     read as progress; here it slides the whole column — and its dot — out
     of alignment with the rail, which is what the fill is measured against.
     Progress is carried by the fill and the dot state instead. */
  transform: none;
}
.how__pin .how__step:not(.is-active):hover { opacity: .8; }
.how__pin .how__step:focus-visible {
  outline: 2px solid var(--primary-400); outline-offset: 6px;
  border-radius: var(--r-sm);
}

.howh__dot {
  position: absolute; top: 0; left: 0;
  width: 12px; height: 12px; border-radius: 50%;
  border: 2px solid var(--ink-line); background: var(--ink-900);
  box-sizing: border-box;
  transition: background-color var(--dur-ui) var(--ease-ui),
              border-color var(--dur-ui) var(--ease-ui),
              transform var(--dur-ui) var(--ease-brand),
              box-shadow var(--dur-ui) var(--ease-ui);
}
.how__step.is-done .howh__dot { background: var(--primary-700); border-color: var(--primary-700); }
.how__step.is-active .howh__dot {
  background: var(--primary-500); border-color: var(--primary-500);
  transform: scale(1.35);
  box-shadow: 0 0 0 6px rgba(0, 169, 143, .16);
}

.how__pin .how__step-title { margin-top: .375rem; font-size: 1.125rem; }
.how__pin .how__step-desc { margin-top: .5rem; max-width: none; font-size: var(--text-sm); }

/* ---- The stage --------------------------------------------------------
   Full width of the step row: flush with step 01's left edge and step 04's
   right edge, so the card and the row share one set of margins instead of
   the card floating 340px inside them.

   Height comes from the content, not a number. The panels are stacked in a
   single grid cell rather than absolutely positioned, which makes the stage
   exactly as tall as its tallest panel — the previous fixed 300px was
   cropping panel 01, whose four people rows come to 425px. Stacking in grid
   also means no panel needs to know about any other.
   ------------------------------------------------------------------------ */
.how__pin .how__figure { position: static; margin-top: 3rem; }
.how__pin .how__stage {
  height: auto;
  display: grid;
}
/* Overrides the shared rule's position:absolute; inset:0. Every panel takes
   the same cell, so they overlap without leaving the flow. */
.how__pin .how__panel {
  position: relative;
  inset: auto;
  grid-area: 1 / 1;
  /* A column so .howp can take the height left over below the head, which
     is what lets the shorter panels centre their content instead of
     clumping at the top of a card sized by the tallest one. */
  display: flex; flex-direction: column;
}

/* ---- Inside a panel ---------------------------------------------------
   Two columns: the record on the left, and on the right the figure or state
   that record produces. At 1200px a single column of three to five rows was
   mostly empty card, and the right-hand content was previously squeezed
   into a one-line foot strip.
   ------------------------------------------------------------------------ */
.howp {
  margin-top: 1.125rem;
  flex: 1;
  display: grid; gap: 2.5rem;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  align-items: stretch;
}
/* Centred, not spread. The card is as tall as its tallest panel, so the
   three shorter ones have room to spare: centring sits their content in the
   middle of that room, where space-between would push a three-row list to
   100px gaps and top alignment leaves the card looking half-used. */
.howp__main {
  display: grid; gap: .625rem; min-width: 0;
  align-content: center;
}

/* A hairline, not a filled sub-panel: the right column is the same record
   read a different way, not a separate object. */
/* The side column starts with a label, so it reads from the top even when
   the record beside it is centred. */
.howp__side {
  min-width: 0;
  padding-left: 2.5rem;
  border-left: 1px solid var(--ink-line);
}
.howp__side-label {
  font-size: .6875rem; font-weight: var(--weight-bold);
  text-transform: uppercase; letter-spacing: .1em;
  color: var(--neutral-500);
}
.howp__chips { margin-top: .875rem; display: flex; flex-wrap: wrap; gap: .375rem; }

.howp__stats { margin-top: .875rem; display: grid; gap: .75rem; }
.howp__stats > div { display: flex; align-items: baseline; gap: .625rem; }
.howp__stats b {
  font-size: 1.375rem; font-weight: var(--weight-bold); letter-spacing: -.03em;
  color: #fff; font-variant-numeric: tabular-nums;
}
.howp__stats span { font-size: var(--text-xs); color: var(--neutral-400); }

.howp__note {
  margin-top: 1.125rem;
  font-size: var(--text-xs); line-height: 1.55; color: var(--neutral-400);
}
.howp__note .accent { color: var(--primary-300); font-weight: var(--weight-semi); }

/* The clock and its pulse are the whole left column on step 02, so they get
   the room the people list gets on step 01. */
.how__pin .how__clock { margin-top: .25rem; }

/* ---- Narrow ------------------------------------------------------------
   Below 1024px the four columns are too thin for a title and two lines of
   description, so the pin is released and the steps stack. This is the same
   call the section's original vertical layout made at the same width.
   ------------------------------------------------------------------------ */
@media (max-width: 1023px) {
  .how__scroll { height: auto; }
  .how__pin {
    position: static; min-height: 0;
    padding-block: var(--section-y, 4rem);
  }
  .howh__rail { display: none; }
  .howh__steps { grid-template-columns: minmax(0, 1fr); gap: 1.75rem; }
  .how__pin .how__step { opacity: 1; padding-top: 0; padding-left: 2rem; }
  .howh__dot { top: 6px; }
  /* One column inside the panels too: the side column's hairline and 2.5rem
     indent do not survive being squeezed, and the stats read fine stacked
     under the record they summarise. */
  .howp { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
  .howp__side { padding-left: 0; border-left: 0; padding-top: 1.25rem; border-top: 1px solid var(--ink-line); }
}

@media (prefers-reduced-motion: reduce) {
  .how__scroll { height: auto; }
  .how__pin { position: static; min-height: 0; }
  .how__pin .how__step { opacity: 1; }
  .howh__rail-fill { transition: none; }
}
