/* ==========================================================================
   Akademi Kinetiq — base
   Design tokens transcribed 1:1 from the Figma mock
   (file jkf476XxVyKzrDY9I3D4rS, canvas "Desktop").
   The source file defines no Figma variables, so these custom properties are
   simply a naming of the raw hex values used in the design.
   ========================================================================== */

:root {
  /* ---- Surfaces ---- */
  --c-page:            #131313;
  --c-nav:             rgba(19, 19, 19, 0.8);
  --c-nav-solid:       #131313;
  --c-footer:          #0e0e0e;
  --c-surface:         #1a1a1a;
  --c-surface-alt:     #1c1c1c;
  --c-surface-deep:    #0e0e0e;
  --c-cream-panel:     #f5e6c8;

  /* ---- Lines ---- */
  --c-line:            #4f4632;
  --c-line-soft:       #332e22;
  --c-line-bright:     #ffe6b3;

  /* ---- Text ---- */
  --c-cream:           #ffdf99;
  --c-off-white:       #e5e2e1;
  --c-muted:           #d3c5ab;
  --c-dim:             #a89b80;
  --c-ink:             #131313;

  /* ---- Accent ---- */
  --c-accent:          #ffc400;
  --c-accent-ink:      #251a00;
  --c-accent-ink-2:    #3f2e00;

  /* ---- Focus / overlay ---- */
  --c-focus:           #ffc400;
  --c-scrim:           rgba(19, 19, 19, 0.97);

  /* ---- Type ---- */
  --font-display: 'Anton', 'Arial Narrow', Impact, sans-serif;
  --font-body:    'Hanken Grotesk', 'Helvetica Neue', Arial, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, 'SFMono-Regular', Consolas, monospace;

  /* ---- Fluid type scale ----
     The mock's desktop sizes are the ceilings; the floors are chosen so a
     390px phone still reads comfortably. Body copy is deliberately NOT
     scaled down — only display type and tracking respond. */
  --fs-hero:     clamp(2.75rem, 9vw, 6rem);      /*  44 →  96 */
  --fs-section:  clamp(1.75rem, 4.2vw, 3rem);    /*  28 →  48 */
  --fs-sub:      clamp(1.5rem, 3.2vw, 2rem);     /*  24 →  32 */
  --fs-card:     clamp(1.25rem, 2vw, 1.5rem);    /*  20 →  24 */
  --fs-lede:     clamp(1rem, 1.2vw, 1.125rem);   /*  16 →  18 */
  --fs-label:    clamp(0.8125rem, 1.1vw, 1rem);  /*  13 →  16 */

  /* Wide mono tracking is a desktop luxury; it shreds a narrow column. */
  --track-mono:  clamp(1px, 0.45vw, 1.6px);
  --track-wide:  clamp(2px, 0.7vw, 3.2px);

  /* ---- Layout ---- */
  --container-max: 1280px;
  --gutter: clamp(20px, 5vw, 64px);

  /* ---- Vertical rhythm ----
     The mock gave each page its own hero height (764 / 859 / 971) and each
     section its own padding. These three tokens replace all of that so the
     banner and the spacing between sections read identically on every page. */
  --hero-min:      clamp(560px, 82vh, 820px);
  --hero-pad:      clamp(48px, 7vw, 96px);
  --space-section: clamp(56px, 9vw, 96px);

  /* ---- Chrome height (the fixed header: 68px bar + 1px bottom border) ---- */
  --nav-h: 69px;

  /* Minimum comfortable touch target */
  --tap: 44px;

  /* One easing for every left-to-right wipe on the site: the nav underline,
     the hero headline reveal and the button fills. */
  --ease-wipe: cubic-bezier(0.65, 0, 0.35, 1);
}

/* ==========================================================================
   Reset
   ========================================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* The mock's absolute offsets could push a child past the viewport edge
     during re-flow; clip rather than let the whole page scroll sideways. */
  overflow-x: hidden;
}

body {
  margin: 0;
  background-color: var(--c-page);
  color: var(--c-muted);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote { margin: 0; }

ul, ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
  text-decoration: none;
}

img, svg, video {
  display: block;
  max-width: 100%;
}

/* Aspect ratio is preserved unless a rule deliberately opts out with
   object-fit. Weak enough (one element selector) that any real layout rule
   still wins, but it stops a stray `width: 100%` from squashing an icon. */
img { height: auto; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

/* ==========================================================================
   Shared typography primitives
   ========================================================================== */

/* Anton display headings */
.display {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--c-cream);
  text-transform: uppercase;
}

/* JetBrains Mono eyebrow / label */
.eyebrow {
  font-family: var(--font-mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 1.6px;
  color: var(--c-muted);
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  font-family: var(--font-display);
  font-size: 14px;
  line-height: 19.6px;
  letter-spacing: 0.7px;
  text-transform: uppercase;
  white-space: nowrap;
}

.btn--solid {
  background-color: var(--c-accent);
  color: var(--c-accent-ink);
}

.btn--outline {
  background-color: transparent;
  border: 1px solid var(--c-line-bright);
  color: var(--c-cream);
}

/* --------------------------------------------------------------------------
   Button hover — one mechanic for every button on the site

   A fill wipes in from the left and, on the way out, keeps travelling and
   leaves to the right: the same gesture as the nav underline and the hero
   headline reveal. Variants only declare --btn-fill and, where the label
   would otherwise be unreadable once covered, its hover colour.

   The fill is a pseudo-element at z-index -1, which paints above the button's
   own background but below its label — so no label needs wrapping in a span.
   `isolation` keeps that negative index from escaping behind the button.

   The list below names buttons defined in the page stylesheets. They are
   grouped here rather than repeated in four files because the mechanic is one
   decision; only the colours are local.
   -------------------------------------------------------------------------- */

.btn,
.site-nav__cta,
.site-menu__cta,
.pw-decision__btn,
.site-wa,
.ab-ctas__btn {
  position: relative;
  isolation: isolate;
  /* The label flips in a single step just before the fill's midpoint. Fading
     it across the full duration leaves it pale over ground the fill has
     already covered — readable at neither end. */
  transition: color 0ms linear 130ms, border-color 340ms var(--ease-wipe);
}

.btn::before,
.site-nav__cta::before,
.site-menu__cta::before,
.pw-decision__btn::before,
.site-wa::before,
.ab-ctas__btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-color: var(--btn-fill, var(--c-accent));
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 340ms var(--ease-wipe);
}

.btn:hover::before,
.site-nav__cta:hover::before,
.site-menu__cta:hover::before,
.pw-decision__btn:hover::before,
.site-wa:hover::before,
.ab-ctas__btn:hover::before {
  transform: scaleX(1);
  transform-origin: left;
}

/* Solid buttons wipe to white: a primary action should get hotter under the
   pointer, and every darker yellow in the palette reads as disabled. Their
   labels are already dark, so they stay legible without a hover colour. */
.btn--solid { --btn-fill: #ffffff; }

.btn--outline {
  --btn-fill: var(--c-accent);
}

.btn--outline:hover {
  color: var(--c-accent-ink);
  border-color: var(--c-accent);
}

/* --------------------------------------------------------------------------
   Card hover — the corner cut

   Grouped here for the same reason the button wipe above is: the mechanic is
   one decision shared by three stylesheets' worth of cards. Only the card
   names are local.

   An accent wedge slides out of the top-right corner and the title turns with
   it, on the same 340ms/--ease-wipe the buttons use. Deliberately NOT the full
   accent flood — that treatment belongs to things you can press, and these
   cards are static <article>s. If one ever becomes a link, add :focus-visible
   beside every :hover here or the state is invisible to keyboard users.
   -------------------------------------------------------------------------- */
.pathway-card,
.decision-card,
.ab-who__card--heritage,
.ab-who__card--focus,
.ab-who__card--intensity,
.coach-card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  transition: border-color 340ms var(--ease-wipe);
}

.pathway-card::after,
.decision-card::after,
.ab-who__card--heritage::after,
.ab-who__card--focus::after,
.ab-who__card--intensity::after,
.coach-card::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 64px;
  height: 64px;
  background-color: var(--c-accent);
  clip-path: polygon(100% 0, 100% 100%, 0 0);
  /* Parked diagonally outside its own corner, so it arrives along the
     diagonal the wedge points down rather than sliding in flat. */
  transform: translate(100%, -100%);
  transition: transform 340ms var(--ease-wipe);
  pointer-events: none;
}

.pathway-card:hover::after,
.decision-card:hover::after,
.ab-who__card--heritage:hover::after,
.ab-who__card--focus:hover::after,
.ab-who__card--intensity:hover::after,
.coach-card:hover::after {
  transform: translate(0, 0);
}

.pathway-card:hover,
.decision-card:hover,
.ab-who__card--heritage:hover,
.ab-who__card--focus:hover,
.ab-who__card--intensity:hover,
.coach-card:hover {
  border-color: var(--c-accent);
}

.pathway-card__title,
.decision-card__title,
.ab-who__card-title,
.ab-who__card-heading,
.coach-card__title {
  transition: color 340ms var(--ease-wipe);
}

.pathway-card:hover .pathway-card__title,
.decision-card:hover .decision-card__title,
.ab-who__card--heritage:hover .ab-who__card-title,
.ab-who__card--focus:hover .ab-who__card-heading,
.ab-who__card--intensity:hover .ab-who__card-heading,
.coach-card:hover .coach-card__title {
  color: var(--c-accent);
}

/* The open card's photo gets a push-in with it. Held at 1.04 — the media box
   is a fixed height, so more crops the paddles out of frame. That box already
   clips in home.css, so nothing needs adding for the scale.

   520ms, not the 620ms this used to run at, so it lands with the card rather
   than a tenth of a second after it. Everything in the open gesture is on this
   one clock now: the column widths, the media box, the copy and this. Four
   near-but-not-equal durations is what makes a transition feel unsettled —
   the thing appears to finish, then something keeps moving. */
.pathway-card__media img {
  transition: transform 520ms var(--ease-wipe);
}

.pathway-card:hover .pathway-card__media img {
  transform: scale(1.04);
}

/* --------------------------------------------------------------------------
   Card body copy — collapsed until the card opens

   Here rather than in home.css and about.css for the same reason as the wedge
   above: the Pathway cards and the Who We Are cards do one thing, and written
   twice the two copies drift the first time either page is edited.

   A closed card carries its heading and icon only; the paragraph is what you
   get for pointing at one, and it arrives with the photo.

   The collapse is a grid whose only row animates 0fr -> 1fr. That is the one
   reliable way to take a text block from nothing to its natural height:
   `height: auto` cannot be interpolated, and a fixed pixel height would have
   to be guessed per card and would break the moment anyone edits the copy.
   The inner element needs overflow: hidden and min-height: 0, or it refuses to
   be squeezed below its own content — which is why each paragraph wraps its
   text in a <span>.

   Collapsing rather than merely fading matters: an invisible paragraph still
   holding its space leaves every heading floating above a void, which reads as
   a broken card rather than a quiet one.

   Opacity and grid rows only, never display or visibility — the text stays in
   the document throughout, selectable, findable with ctrl-F and read out by
   screen readers. Hidden from the eye, not removed from the page.

   Confined to the range that has a pointer and three side-by-side columns.
   Below 1024px both grids reflow and every paragraph simply shows.
   -------------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .pathway-card__text,
  .ab-who__card-text,
  .ab-who__card-body {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    /* 520ms to match the card's own opening — see the note on the photo scale
       above. The opacity is shorter on purpose and sits inside that envelope,
       so the words are legible before the box stops growing. */
    transition: grid-template-rows 520ms var(--ease-wipe), opacity 260ms linear;
  }

  .pathway-card__text > *,
  .ab-who__card-text > *,
  .ab-who__card-body > * {
    overflow: hidden;
    min-height: 0;
  }

  .pathway-card:hover .pathway-card__text,
  .pathway-card.is-open .pathway-card__text,
  .ab-who__cards > article:hover .ab-who__card-text,
  .ab-who__cards > article:hover .ab-who__card-body,
  .ab-who__cards > article.is-open .ab-who__card-text,
  .ab-who__cards > article.is-open .ab-who__card-body {
    grid-template-rows: 1fr;
    opacity: 1;
    /* Opening, the words wait for the box to exist before fading in. Closing,
       they go first — text lingering over a shrinking box is the part that
       looks broken. */
    transition-delay: 0ms, 200ms;
  }

  /* Rest state: the first card of each section is already open before anything
     is touched, so the section reads as a designed layout rather than a row of
     empty boxes waiting to be discovered. See the matching blocks in home.css
     and about.css, which open that card's photo and columns.

     Guarded on the absence of BOTH signals rather than written as a default,
     because it has to yield the instant any card is hovered or remembered.
     Once the visitor has touched any card this never applies again — the
     .is-open class stays behind on whichever card they left. */
  .home-pathway__grid:not(:has(.pathway-card:hover)):not(:has(.pathway-card.is-open)) .pathway-card--l1 .pathway-card__text,
  .ab-who__cards:not(:has(> article:hover)):not(:has(> article.is-open)) .ab-who__card--heritage .ab-who__card-text {
    grid-template-rows: 1fr;
    opacity: 1;
  }
}

/* The colour changes carry the state on their own; only the movement goes.
   Note the state itself is kept in every case below: a card still opens, its
   photo still appears, its copy still arrives. Only the travel is dropped —
   removing the feature outright would hide content from the person who asked
   for less motion, not less information. */
@media (prefers-reduced-motion: reduce) {
  .pathway-card::after,
  .decision-card::after,
  .ab-who__card--heritage::after,
  .ab-who__card--focus::after,
  .ab-who__card--intensity::after,
  .coach-card::after { transition: none; }

  .pathway-card__media img { transition: none; }
  .pathway-card:hover .pathway-card__media img { transform: none; }

  .home-pathway__grid,
  .ab-who__cards,
  .pathway-card__media,
  .pathway-card__icon,
  .ab-who__icon,
  .ab-who__photo,
  .pathway-card__text,
  .ab-who__card-text,
  .ab-who__card-body {
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
  }
}

/* ==========================================================================
   Responsive helpers
   ========================================================================== */

/* The mock hard-codes line breaks tuned to a 1280px column. Keeping the
   markup but hiding the breaks below the desktop grid preserves the designed
   desktop line endings exactly while letting narrow columns wrap naturally. */
@media (max-width: 1023px) {
  .br-lg { display: none; }
}

/* ==========================================================================
   Section reveal

   Deliberately the quiet effect on the page. The left-to-right wipe is
   reserved for the hero headline, the nav underline and the buttons; if every
   section heading did the same thing, the hero would stop being a moment.
   This only stops the page arriving all at once.

   The hidden state is scoped to .js-rv, which an inline script in the document
   head sets. Without scripting the class never appears and every section
   renders normally — there is no path where this leaves content invisible.
   ========================================================================== */

.js-rv .rv {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 620ms ease-out,
    transform 620ms cubic-bezier(0.16, 1, 0.3, 1);
}

.js-rv .rv.is-in {
  opacity: 1;
  transform: none;
}

/* Supporting copy trails its heading rather than moving in lockstep. */
.rv--d1 { transition-delay: 90ms; }
.rv--d2 { transition-delay: 180ms; }

/* --------------------------------------------------------------------------
   Sequenced reveal — .seq

   A card grid arriving as one block reads as a single object; arriving in
   order reads as a list. Add `seq` beside `rv` on the grid and its children
   reveal one after another instead of together.

   This rides on the reveal above rather than replacing it: same 620ms, same
   curve, same .is-in flag, same observer. nav.js needs no change — the grid
   still carries .rv and is still what gets observed. The only addition is a
   per-child transition-delay, which is CSS alone.

   The container must NOT fade itself, or the children fade inside an already
   fading box and the whole thing double-dips into mush. So it opts out and
   hands the reveal down.

   The rv--d1 / rv--d2 on a seq grid stop meaning "delay this element" — the
   element no longer animates — and start meaning "delay where the run of
   cards begins". Same intent, same beats, so the markup keeps reading right.
   -------------------------------------------------------------------------- */
.js-rv .rv.seq {
  opacity: 1;
  transform: none;
  transition: none;
  --seq-base: 0ms;
  --seq-step: 90ms;   /* the gap already between rv--d1 and rv--d2, so the
                         cards land on the same beat as every heading */
}

.js-rv .rv.seq.rv--d1 { --seq-base: 90ms; }
.js-rv .rv.seq.rv--d2 { --seq-base: 180ms; }

/* Driven by @keyframes, not by transition, and that is load-bearing.

   These same elements are hover cards: they transition their border colour,
   and on the two grids that expand they transition their own layout too. A
   `transition` shorthand here would replace theirs — one declaration on
   .js-rv .seq > * outranks a bare .pathway-card — and the staggered
   transition-delay below would then be inherited by the hover as well, so a
   card in the fourth position would take 450ms to begin reacting to a pointer.

   Animation and transition are separate properties that never overwrite each
   other, so the reveal and the hover can both exist on one element. Two
   keyframes rather than one because the original used a different curve for
   the fade than for the movement, and that is worth keeping. */
.js-rv .seq > * {
  opacity: 0;
  /* Slightly less travel than the 18px .rv rise, because the scale is doing
     part of the work; both together at full strength overshoots. */
  transform: translateY(14px) scale(0.96);
}

.js-rv .seq.is-in > * {
  animation:
    seq-fade 620ms ease-out both,
    seq-rise 620ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes seq-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes seq-rise {
  from { transform: translateY(14px) scale(0.96); }
  to   { transform: none; }
}

/* Written out to 8. Three of the four grids hold three cards; the ceiling is
   headroom for a fourth being added without this silently stopping. */
.js-rv .seq.is-in > *:nth-child(1) { animation-delay: calc(var(--seq-base) + 0 * var(--seq-step)); }
.js-rv .seq.is-in > *:nth-child(2) { animation-delay: calc(var(--seq-base) + 1 * var(--seq-step)); }
.js-rv .seq.is-in > *:nth-child(3) { animation-delay: calc(var(--seq-base) + 2 * var(--seq-step)); }
.js-rv .seq.is-in > *:nth-child(4) { animation-delay: calc(var(--seq-base) + 3 * var(--seq-step)); }
.js-rv .seq.is-in > *:nth-child(5) { animation-delay: calc(var(--seq-base) + 4 * var(--seq-step)); }
.js-rv .seq.is-in > *:nth-child(6) { animation-delay: calc(var(--seq-base) + 5 * var(--seq-step)); }
.js-rv .seq.is-in > *:nth-child(7) { animation-delay: calc(var(--seq-base) + 6 * var(--seq-step)); }
.js-rv .seq.is-in > *:nth-child(8) { animation-delay: calc(var(--seq-base) + 7 * var(--seq-step)); }

/* The badge blocks that overlap a photo wipe in from the edge they are pinned
   to instead of rising — a solid block laid onto an image reads better as a
   reveal than as a movement, and the direction comes from the anchor rather
   than being the same everywhere.

   These are NOT observed themselves; they key off their parent figure's
   .is-in. An element clipped to zero area reports an intersection ratio of 0,
   so a clip-path element can never trigger the observer that would un-clip it
   — it would stay hidden forever. The figure is unclipped, fires normally, and
   the badge follows it. That also gives the sequence we want: photo settles,
   badge lands on top of it. */
.js-rv .badge-wipe { transition: clip-path 560ms var(--ease-wipe) 320ms; }

.js-rv .badge-wipe--l { clip-path: inset(0 100% 0 0); }   /* pinned left  → wipes right */
.js-rv .badge-wipe--r { clip-path: inset(0 0 0 100%); }   /* pinned right → wipes left  */

.js-rv .is-in .badge-wipe { clip-path: inset(0 0 0 0); }

/* Declared after the rules above so it wins: content is simply present. */
@media (prefers-reduced-motion: reduce) {
  .js-rv .rv,
  .js-rv .rv.is-in,
  /* The staggered children need naming explicitly: they are not .rv elements,
     so the rules above never reach them and they would stay at opacity 0. */
  .js-rv .seq > *,
  .js-rv .seq.is-in > *,
  .js-rv .badge-wipe,
  /* Needs to match the specificity of `.js-rv .is-in .badge-wipe` above to
     beat it; source order then decides. */
  .js-rv .is-in .badge-wipe {
    opacity: 1;
    transform: none;
    clip-path: none;
    transition: none;
    /* The sequenced children reveal via @keyframes, which `transition: none`
       does not touch — without this they would still rise and fade. */
    animation: none;
  }
}

/* Carries a value for assistive tech that the visible element cannot, e.g. the
   final number behind an animating counter. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   Accessibility floor
   ========================================================================== */

:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 3px;
}

/* The design is dark-on-dark; a default UA ring would be invisible. */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    /* Without this a delayed entrance animation still holds its hidden start
       frame for the full delay, then snaps in — the exact effect the setting
       asks us to avoid. */
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
}
