/* ==========================================================================
   LEECOI — Homepage
   ==========================================================================

   Page-scoped composition only. Loaded after utilities.css.

   Every class here is prefixed by its section and exists on exactly one page.
   Anything in this file that starts being wanted on a second page is a
   component in waiting and belongs in components.css instead.

   Sections, in document order:
     1  hero, including the signature progress curve
     2  toolbelt
     3  tracks        (uses the shared .card and .grid)
     4  program shape (.beats)
     5  capstones     (.project, on the shared slider)
     6  career support (.support)
     7  what is on offer (.figures, .ledger)
     8  questions     (.qa)
     9  closing call to action (.closing)
   ========================================================================== */

/* ==========================================================================
   HERO
   ========================================================================== */

.hero {
  position: relative;
  /* Clips the blurred glows, which extend well past their boxes. */
  overflow: hidden;
  isolation: isolate;

  /* The header is fixed and transparent over this section, so the hero owns
     the space beneath it rather than main pushing itself down. */
  padding-block-start: calc(var(--header-h) + clamp(3rem, 8vh, 6rem));
  padding-block-end: clamp(3.5rem, 9vh, 6.5rem);
}

.hero__field {
  position: absolute;
  inset: 0;
  z-index: var(--z-behind);
  pointer-events: none;
}

/* Two slow-drifting pools of light. Gold is the warm one and blue the cool
   one, both far below the threshold where they would read as a gradient
   background: they exist to keep the flat field from looking like a slab. */
.hero__glow-a {
  inset-block-start: -18%;
  inset-inline-start: -6%;
  inline-size: 38rem;
  block-size: 38rem;
  background: radial-gradient(circle, rgb(212 175 55 / 0.1), transparent 68%);
}

.hero__glow-b {
  inset-block-start: 4%;
  inset-inline-end: -12%;
  inline-size: 44rem;
  block-size: 44rem;
  background: radial-gradient(circle, rgb(76 141 255 / 0.11), transparent 68%);
  /* Offset so the two never pulse in unison, which would read as a heartbeat. */
  animation-delay: -12s;
}

/* Two columns above the nav breakpoint, one below, with the facts row spanning
   the full width underneath both. Named areas rather than nth-child ordering, so
   the intent survives someone adding a fourth child. */
.hero__inner {
  position: relative;
  display: grid;
  grid-template-areas:
    'copy'
    'visual'
    'facts';
  gap: var(--space-2xl);
  align-items: center;
}

@media (min-width: 62rem) {
  .hero__inner {
    grid-template-areas:
      'copy visual'
      'facts facts';
    /* The copy column is narrower than the chart: the headline wants a short
       measure, and the chart needs width for eighteen legible points. */
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr);
    gap: var(--space-3xl) var(--space-2xl);
  }
}

.hero__copy {
  grid-area: copy;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-lg);
  /* min-width: 0 stops a long word in the headline forcing the grid column
     wider than its track. */
  min-inline-size: 0;
}

.hero__visual {
  grid-area: visual;
  min-inline-size: 0;
}

.hero__facts {
  grid-area: facts;
}

.hero__eyebrow {
  margin-block-end: var(--space-2xs);
}

.hero__title {
  font-size: var(--step-d1);
  line-height: var(--step-d1-lh);
  letter-spacing: var(--step-d1-ls);
  font-weight: var(--weight-bold);

  /* ~24 characters per line at the largest step, which keeps the headline to
     two or three lines at every width instead of one very long one. */
  max-width: 20ch;
}

.hero__lead {
  font-size: var(--step-lead);
  line-height: var(--step-lead-lh);
  letter-spacing: var(--step-lead-ls);
  color: var(--muted);
  max-width: 46ch;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-block-start: var(--space-xs);
}

/* On the narrowest phones two side-by-side buttons each end up too small to
   read comfortably, so they stack and fill the width. */
@media (max-width: 26rem) {
  .hero__actions {
    inline-size: 100%;
  }

  .hero__actions .btn {
    flex: 1 1 100%;
  }
}

/* ---- Facts row ---------------------------------------------------------
   A <dl>: each figure genuinely is a term and its value.

   `column-reverse` puts the number above its label visually while leaving <dt>
   before <dd> in the DOM, which is the order the spec requires and the order a
   screen reader needs to pair them correctly. Presentation should bend to
   semantics, not the reverse. */
.hero__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
  gap: var(--space-lg);

  inline-size: 100%;
  max-width: 44rem;
  padding-block-start: var(--space-lg);
  border-block-start: 1px solid var(--line);
}

.hero__fact {
  display: flex;
  flex-direction: column-reverse;
  gap: var(--space-2xs);
}

/* Below ~480px the three-up grid breaks 2+1, which reads as a mistake rather
   than a layout. Narrow screens get a stacked readout instead: value, then
   label, one per row, separated by hairlines. Same DOM, and it suits the
   instrument-panel direction better than a lopsided grid.

   `row-reverse` with `justify-content: flex-end` packs the pair to the left
   while leaving <dt> before <dd> in the DOM, so the value reads first
   visually and the term still precedes its description for a screen reader. */
@media (max-width: 30rem) {
  .hero__facts {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .hero__fact {
    flex-direction: row-reverse;
    justify-content: flex-end;
    align-items: baseline;
    gap: var(--space-md);

    padding-block: var(--space-sm);
    border-block-end: 1px solid var(--line);
  }

  .hero__fact:last-child {
    border-block-end: 0;
  }

  .hero__fact-value {
    font-size: 1.5rem;
    /* Reserves room for two digits so the labels start on a common left edge
       instead of stepping in and out with the number's width. */
    min-inline-size: 2.25ch;
  }
}

.hero__fact-value {
  font-family: var(--font-mono);
  font-size: clamp(1.75rem, 3.4vw, 2.25rem);
  font-weight: var(--weight-medium);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.hero__fact-label {
  font-family: var(--font-mono);
  font-size: var(--step-micro);
  line-height: var(--step-micro-lh);
  letter-spacing: var(--step-micro-ls);
  text-transform: uppercase;
  color: var(--muted);
}

/* ==========================================================================
   TOOLBELT
   --------------------------------------------------------------------------
   Set as monospaced wordmarks rather than vendor logos. Three reasons, in
   order of weight: naming these as partners would be untrue, redrawing other
   companies' trademarks by hand gets them subtly wrong, and a row of tool
   names reads as a dependency list, which is exactly what it is.
   ========================================================================== */

.toolbelt__item {
  font-size: var(--step-small);
  letter-spacing: 0.02em;
  /* Cursor stays default: these are labels, not links. */
  cursor: default;
}

/* ==========================================================================
   THE PROGRESS CURVE
   --------------------------------------------------------------------------
   The signature element. Markup is generated by tools/build-curve.mjs.

   Colour note: the plotted line is the data blue, not the gold. Gold is
   rationed to four roles, and the fourth is the CONVERGED ENDPOINT only, which
   is why exactly one of the eighteen points is gold. That single dot is also
   the dot at the centre of the logo.
   ========================================================================== */

.curve-panel {
  border-radius: var(--radius-xl);
  border: 1px solid var(--line);
  background-color: var(--panel);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

.curve-panel__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);

  padding: var(--space-sm) var(--space-md);
  border-block-end: 1px solid var(--line);
  background-color: var(--bg-sunk);

  font-family: var(--font-mono);
  font-size: 0.75rem;
}

.curve-panel__title {
  color: var(--ink);
  letter-spacing: 0.04em;
}

.curve-panel__meta {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.curve-panel__plot {
  padding: var(--space-md) var(--space-md) 0;
}

.curve {
  display: block;
  inline-size: 100%;
  /* The viewBox is 600x300, so height follows from the aspect ratio. Declaring
     it reserves the box before paint and stops the hero reflowing. */
  aspect-ratio: 600 / 300;
  /* Focus rings on the points are drawn outside their radius and would be
     clipped by the SVG's own bounds. */
  overflow: visible;
}

.curve__grid line {
  stroke: var(--line);
  stroke-width: 1;
}

.curve__area {
  fill: url(#curve-fill);
}

.curve__line {
  fill: none;
  stroke: var(--link);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---- Points -------------------------------------------------------------
   Interaction states SCALE the circle rather than animating its `r`. The
   geometry properties (r, cx, cy) are only usable from CSS in recent engine
   versions, and a point that silently stops responding in one browser is worse
   than one that never moved. `transform-box: fill-box` makes the element's own
   bounding box the reference, so `transform-origin: center` means the centre of
   the dot rather than the origin of the whole SVG. */
.curve__point {
  fill: var(--panel);
  stroke: var(--link);
  stroke-width: 2;
  cursor: pointer;

  transform-box: fill-box;
  transform-origin: center;
  transition:
    transform var(--dur-fast) var(--ease-ui),
    fill var(--dur-fast) var(--ease-ui),
    stroke var(--dur-fast) var(--ease-ui);
}

.curve__point:hover,
.curve__point.is-active {
  fill: var(--link);
  transform: scale(1.35);
}

/* The converged endpoint: the one gold thing in the chart, and the fourth and
   final rationed use of the accent. Its larger radius is set in the markup. */
.curve__point--converged {
  fill: var(--accent);
  stroke: var(--accent);
}

.curve__point--converged:hover,
.curve__point--converged.is-active {
  fill: var(--accent);
  transform: scale(1.25);
}

/* SVG elements honour the `outline` shorthand inconsistently, so the focus ring
   is drawn as a contrasting stroke instead. */
.curve__point:focus-visible {
  outline: none;
  stroke: var(--ring);
  stroke-width: 3.5;
  transform: scale(1.45);
}

/* Font size inside an SVG is expressed in viewBox units, so it shrinks with the
   chart. At 390px the panel renders the 600-unit viewBox at roughly half scale,
   which put these labels at about 6 physical pixels. Bumped on narrow screens so
   the axis stays readable at the size it is actually displayed. */
.curve__axis {
  fill: var(--muted);
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

@media (max-width: 34rem) {
  .curve__axis {
    font-size: 18px;
    letter-spacing: 0.06em;
  }
}

.curve__axis--end {
  text-anchor: end;
}

/*
  A NOTE ON TOUCH TARGETS

  Eighteen points across a 320px chart sit about 17px apart, below the 24px
  minimum in WCAG 2.2 SC 2.5.8. That criterion exempts targets whose
  presentation is essential, and it genuinely is here: the points are at the
  coordinates the data puts them, and spacing them further apart would stop the
  chart being the chart.

  The exemption is only defensible because the points are an enhancement rather
  than the only route to the information. The shape is described in the SVG's
  <desc>, the converged week is stated in the caption, and the whole series is
  reachable by keyboard through one Tab stop. Nothing here is available only by
  hitting a 9px dot.
*/

/* ---- Readout ------------------------------------------------------------ */
.curve-panel__readout {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-xs);

  padding: var(--space-sm) var(--space-md) var(--space-md);
  font-family: var(--font-mono);
  font-size: 0.75rem;
}

.curve-panel__readout-live {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.curve-panel__hint {
  color: var(--muted);
}

/* Pointing devices do not need to be told about arrow keys, and a mouse user
   reading a keyboard hint is noise. It appears once focus is inside the chart. */
@media (hover: hover) and (pointer: fine) {
  .curve-panel__hint {
    opacity: 0;
    transition: opacity var(--dur-base) var(--ease-ui);
  }

  .curve-panel:focus-within .curve-panel__hint {
    opacity: 1;
  }
}

/* ==========================================================================
   4. PROGRAM SHAPE
   --------------------------------------------------------------------------
   An <ol>, and numbered, because this genuinely is a sequence: the beats happen
   in this order and the order carries meaning. Everywhere on this site that
   content is NOT a sequence, the index is left off.
   ========================================================================== */

.beats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
  gap: var(--space-xl);
}

.beat {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding-block-start: var(--space-lg);
  /* A top rule per beat rather than a card: three cards here would compete
     with the six track cards directly above. */
  border-block-start: 2px solid var(--line-strong);
}

/* The middle beat is the one the section is about, so its rule is the accent.
   This is a 2px rule, not text, which is why it can carry the gold on a light
   field where gold text would fail contrast. */
.beat:nth-child(2) {
  border-block-start-color: var(--accent);
}

.beat__index {
  font-size: var(--step-micro);
  letter-spacing: var(--step-micro-ls);
  color: var(--muted);
}

.beat__title {
  font-size: var(--step-d3);
  line-height: var(--step-d3-lh);
  letter-spacing: var(--step-d3-ls);
  font-weight: var(--weight-semi);
}

.beat__text {
  color: var(--muted);
  font-size: var(--step-small);
  line-height: 1.65;
}

/* ==========================================================================
   5. CAPSTONE PROJECTS
   ========================================================================== */

/* Wider than the default slide, because each card carries a deliverables list
   and a four-item list in a 320px card wraps every line. */
.projects__slide {
  inline-size: min(23rem, 84vw);
}

.projects__track {
  align-items: stretch;
}

.project {
  block-size: 100%;
  gap: var(--space-sm);
}

/* The track slug, set as a slug. It is the identifier the syllabus and the URL
   both use, so it is shown as data rather than prettified into a title. */
.project__track {
  font-size: 0.75rem;
  color: var(--link);
  letter-spacing: 0.02em;
}

.project__deliverables {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);

  margin-block-start: auto;
  padding-block-start: var(--space-md);
  padding-inline-start: var(--space-lg);
  border-block-start: 1px solid var(--line);

  list-style: disc outside;
  font-size: var(--step-small);
  line-height: 1.55;
  color: var(--muted);
}

.projects__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  margin-block-start: var(--space-lg);
}

/* ==========================================================================
   6. CAREER SUPPORT
   ========================================================================== */

.support__items {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);

  margin-block-start: var(--space-md);
  padding-block-start: var(--space-md);
  padding-inline-start: var(--space-lg);
  border-block-start: 1px solid var(--line);

  list-style: disc outside;
  font-size: var(--step-small);
  line-height: 1.6;
  color: var(--muted);
}

/* ==========================================================================
   7. WHAT IS ON OFFER
   ========================================================================== */

.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: var(--space-lg);
  padding-block: var(--space-xl);
  border-block: 1px solid var(--line);
}

/* Same column-reverse reasoning as the hero facts: the value reads first, and
   <dt> still precedes <dd> in the DOM so the pairing is announced correctly. */
.figure {
  display: flex;
  flex-direction: column-reverse;
  gap: var(--space-2xs);
}

.figure__value {
  font-family: var(--font-mono);
  font-size: clamp(2rem, 4.5vw, 3rem);
  font-weight: var(--weight-medium);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--ink);
}

.figure__label {
  font-family: var(--font-mono);
  font-size: var(--step-micro);
  line-height: var(--step-micro-lh);
  letter-spacing: var(--step-micro-ls);
  text-transform: uppercase;
  color: var(--muted);
}

/* ---- The two-column ledger --------------------------------------------- */
.ledger {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
  gap: var(--space-xl);
}

.ledger__col {
  padding: var(--space-lg);
  border-radius: var(--radius-lg);
  border: 1px solid var(--line);
  background-color: var(--panel);
  /* A 2px inline edge, reading as a margin annotation rather than a coloured
     card. The two columns are distinguished by edge, not by fill. */
  border-inline-start: 2px solid var(--positive);
}

.ledger__col--not {
  border-inline-start-color: var(--accent);
}

.ledger__title {
  font-size: var(--step-d3);
  line-height: var(--step-d3-lh);
  letter-spacing: var(--step-d3-ls);
  font-weight: var(--weight-semi);
  margin-block-end: var(--space-md);
}

.ledger__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  padding-inline-start: var(--space-lg);
  list-style: disc outside;
  color: var(--muted);
  font-size: var(--step-small);
  line-height: 1.6;
}

/* ==========================================================================
   8. QUESTIONS
   --------------------------------------------------------------------------
   Moved to components.css section 17 when /faq/ started using the same
   disclosure list. Nothing page-specific remains.
   ========================================================================== */

/* ==========================================================================
   9. CLOSING CALL TO ACTION
   --------------------------------------------------------------------------
   Moved to components.css section 16 when the catalogue and the six track
   pages started using the same band. Nothing page-specific remains.
   ========================================================================== */
