/* The public site's own stylesheet.
 *
 * It reuses tokens.css so the marketing page and the product cannot drift apart
 * in colour, but it is DELIBERATELY not app.css: that file styles a learner
 * inside an LMS iframe, and none of those constraints apply here.
 *
 * Everything below works with no JavaScript at all. A public page that needs a
 * bundle to say what a product does has failed at the one thing it is for.
 *
 * COLOUR POLICY (BRAND.md section 3). Every colour on this page encodes
 * something: the five mastery states in their fixed order, and the five
 * provenance sources. There are no decorative colour blocks. Where colour
 * distinguishes anything, a label or a shape distinguishes it too, so nothing
 * depends on seeing hue (WCAG 1.4.1).
 *
 * Contrast for every pair here is recomputed by scripts/check-contrast.py to four
 * decimals with a 0.15 margin. It is not estimated. This site once shipped black
 * text on a near-black canvas because a token name was invented, and the only
 * thing that caught it was Ian looking at it. */

*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  /* Without this the UA paints scrollbars and any form control in light
   * colours on a dark page. */
  color-scheme: light dark;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--wzp-bg-canvas, #faf9f7);
  color: var(--wzp-text-primary, #1a1a18);
  font: 16px/1.65 var(--wzp-font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
  /* Long-form prose, so let the browser hyphenate rather than leaving rivers. */
  text-wrap: pretty;
}

/* A visible focus ring, on everything, always. The design audit found the
 * product's own focus ring was invisible on primary buttons -- border/focus and
 * accent/default were the same hex, contrast 1.00 -- so this is the one style
 * rule in the file that is not a preference. */
:focus-visible {
  outline: 3px solid var(--wzp-border-focus, #2f5d50);
  outline-offset: 2px;
  border-radius: 2px;
}

.skip {
  position: absolute; left: -9999px;
  background: var(--wzp-bg-surface-raised, #ffffff); color: var(--wzp-text-primary, #1a1a18);
  padding: .75rem 1rem; z-index: 10; border-radius: 6px;
}
.skip:focus { left: 1rem; top: 1rem; }

.wrap { max-width: 66rem; margin: 0 auto; padding: 0 1.5rem; }
.wrap--narrow { max-width: 46rem; }

/* ---- header --------------------------------------------------------------- */

header.top {
  border-bottom: 1px solid var(--wzp-border-subtle, #e5e3de);
  padding: 1.1rem 0;
}
header.top .wrap { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }

/* The lockup: mark, wordmark, glyph. The mark is inline SVG rather than an <img>
 * so it inherits colour -- it is a one-colour mark by constraint, and colour is
 * an enhancement it must never depend on. */
.lockup { display: flex; align-items: center; gap: .6rem; margin: 0; text-decoration: none; color: inherit; }
.lockup svg { width: 30px; height: 30px; display: block; color: var(--wzp-accent-text, #245046); flex: none; }
.lockup .word { font-size: 1.3rem; font-weight: 650; letter-spacing: -0.02em; color: var(--wzp-text-primary, #1a1a18); }
.lockup .kanji { color: var(--wzp-text-muted, #6b6862); font-size: .9rem; align-self: flex-end; padding-bottom: .2rem; }

.topnav { display: flex; gap: 1.4rem; align-items: center; font-size: .92rem; margin: 0; padding: 0; list-style: none; }

/* ---- hero ----------------------------------------------------------------- */

.hero { padding: 4rem 0 3rem; }
.hero h1 {
  font-size: clamp(2rem, 5vw, 3.1rem);
  line-height: 1.12; letter-spacing: -0.025em;
  margin: 0 0 1.1rem; max-width: 34ch; text-wrap: balance;
}
.hero .lede { font-size: clamp(1.05rem, 2.2vw, 1.3rem); color: var(--wzp-text-secondary, #55534e); max-width: 58ch; margin: 0 0 2rem; }

.tag {
  display: inline-block; margin-bottom: 1.5rem;
  background: var(--wzp-bg-accent-subtle, #eaf0ee); color: var(--wzp-text-primary, #1a1a18);
  border: 1px solid var(--wzp-border-strong, #87847e);
  padding: .3rem .7rem; border-radius: 999px;
  font-size: .8rem; letter-spacing: .02em;
}

.actions { display: flex; flex-wrap: wrap; gap: .8rem; margin: 0; }
.btn {
  display: inline-block; padding: .6rem 1.1rem; border-radius: 8px;
  font-weight: 600; font-size: .95rem; text-decoration: none;
  border: 1px solid var(--wzp-accent-default, #2f5d50);
}
.btn--primary { background: var(--wzp-accent-default, #2f5d50); color: var(--wzp-text-on-accent, #ffffff); }
.btn--primary:hover { background: var(--wzp-accent-hover, #24483e); border-color: var(--wzp-accent-hover, #24483e); }
.btn--secondary { background: transparent; color: var(--wzp-accent-text, #245046); border-color: var(--wzp-border-strong, #87847e); }
.btn--secondary:hover { border-color: var(--wzp-accent-default, #2f5d50); }

/* ---- sections ------------------------------------------------------------- */

section { padding: 3.5rem 0; border-top: 1px solid var(--wzp-border-subtle, #e5e3de); }
section h2 { font-size: clamp(1.35rem, 3vw, 1.8rem); letter-spacing: -0.02em; margin: 0 0 .6rem; text-wrap: balance; }
section > .wrap > p { color: var(--wzp-text-secondary, #55534e); max-width: 64ch; }
.eyebrow {
  font-size: var(--wzp-label-sm, 11px); line-height: var(--wzp-label-sm-lh, 14px);
  text-transform: uppercase; letter-spacing: .09em; font-weight: 600;
  color: var(--wzp-text-muted, #6b6862); margin: 0 0 .6rem;
}

.grid { display: grid; gap: 1.25rem; margin-top: 2rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.card {
  background: var(--wzp-bg-surface, #ffffff);
  /* border-strong, not border-default: a card's background differs from the
   * canvas by 1.05:1, so this border is the ONLY thing that says "these are
   * grouped panels". At border-default's 1.46:1 the grouping was invisible.
   * 3.54:1 light, 3.95:1 dark -- recomputed, not assumed. */
  border: 1px solid var(--wzp-border-strong, #87847e);
  border-radius: 10px; padding: 1.4rem;
}
.card h3 { margin: 0 0 .5rem; font-size: 1.05rem; letter-spacing: -0.01em; }
.card p { margin: 0; color: var(--wzp-text-secondary, #55534e); font-size: .95rem; }

/* ---- the provenance spine ------------------------------------------------- */
/* Each card takes the colour of the SOURCE it describes, from the product's own
 * provenance palette -- the same hue a learner sees on a claim that came from
 * that source. The colour is reinforced by the label above it, so the meaning
 * survives greyscale. */

/* Doubled up (`.card.card--source`) so these beat `.card`'s `border` shorthand on
 * specificity rather than on source order. They did render correctly at equal
 * specificity because they come later in the file -- which is exactly the kind of
 * thing that breaks silently the first time somebody reorders the stylesheet. */
.card.card--source { border-left-width: 4px; }
.card.card--deck        { border-left-color: var(--wzp-provenance-deck, #2a6099); }
.card.card--recording   { border-left-color: var(--wzp-provenance-recording, #1f6b70); }
.card.card--instructor  { border-left-color: var(--wzp-provenance-instructor, #2f7a4f); }
.card.card--gap         { border-left-color: var(--wzp-provenance-gap, #b4531f); border-left-style: dashed; }

.card--source .src {
  display: block; margin: 0 0 .45rem;
  font-size: var(--wzp-label-sm, 11px); line-height: var(--wzp-label-sm-lh, 14px);
  text-transform: uppercase; letter-spacing: .09em; font-weight: 700;
}
.card--deck       .src { color: var(--wzp-provenance-deck, #2a6099); }
.card--recording  .src { color: var(--wzp-provenance-recording, #1f6b70); }
.card--instructor .src { color: var(--wzp-provenance-instructor, #2f7a4f); }
.card--gap        .src { color: var(--wzp-provenance-gap-text, #b0511e); }

/* ---- the mastery progression ---------------------------------------------- */
/* Five states, left to right, in their fixed order. Never reordered for visual
 * balance -- the order IS the content (BRAND.md section 3). */

.prog { list-style: none; margin: 2rem 0 0; padding: 0; display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); }
.prog li { border-top: 4px solid; padding-top: .7rem; }
.prog .name { display: block; font-weight: 650; font-size: .95rem; margin-bottom: .15rem; }
.prog .what { display: block; color: var(--wzp-text-secondary, #55534e); font-size: .88rem; }
.prog .ord {
  display: inline-block; min-width: 1.35rem;
  font-size: var(--wzp-label-sm, 11px); letter-spacing: .08em;
  color: var(--wzp-text-muted, #6b6862);
}
/* QUALIFIED WITH `li`, for the same reason as the evidence verdicts in app.css --
 * and I made the identical mistake here after fixing it there, which is why
 * scripts/check-css-specificity.py now exists.
 *
 * `.prog li` above sets the `border-top` SHORTHAND, which resets the colour to
 * currentColor. Bare `.prog--1` is (0,1,0) and loses to `.prog li` at (0,1,1), so
 * all five states rendered as the same near-black bar: the brand's primary colour
 * story, entirely absent, on the section whose whole subject is that the five
 * states are distinguishable. */
.prog li.prog--1 { border-top-color: var(--wzp-mastery-not-started, #8a8781); }
.prog li.prog--2 { border-top-color: var(--wzp-mastery-attempted, #b4531f); }
.prog li.prog--3 { border-top-color: var(--wzp-mastery-practiced, #7a5ca8); }
.prog li.prog--4 { border-top-color: var(--wzp-mastery-ready, #2f7a4f); }
.prog li.prog--5 { border-top-color: var(--wzp-mastery-mastered, #1f4b44); }

/* ---- the workflow chain --------------------------------------------------- */

.chain { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin: 1.75rem 0 0; padding: 0; list-style: none; }
.chain li {
  background: var(--wzp-bg-surface-sunken, #f2f1ed);
  /* Same reasoning as the cards: sunken is 1.07:1 against canvas, so the chip
   * outline is doing all the work of showing these are discrete steps. */
  border: 1px solid var(--wzp-border-strong, #87847e);
  border-radius: 6px; padding: .45rem .8rem; font-size: .9rem;
}
/* The SEAM, not an arrow. The gap between two applications is the thing this
 * product models, so it is drawn as a labelled boundary rather than decoration. */
.chain .seam {
  border: 0; background: none; padding: 0 .15rem;
  display: inline-flex; flex-direction: column; align-items: center; gap: .15rem;
  color: var(--wzp-accent-text, #245046);
}
.chain .seam .bar { display: block; width: 2.4rem; border-top: 2px dashed currentColor; }
.chain .seam .lbl { font-size: var(--wzp-label-sm, 11px); letter-spacing: .07em; text-transform: uppercase; }

/* ---- the hand-off loop ---------------------------------------------------- */
/*
 * MOTION POLICY (BRAND.md section 6): motion may show a sequence, never
 * decorate, and prefers-reduced-motion must stop it dead rather than slow it.
 *
 * So the ANIMATION IS THE OPT-IN, not the default. The markup renders the
 * complete still frame with no animation at all: every panel, both seams, and the
 * artefact at rest in the final application. The keyframes only exist inside
 * `prefers-reduced-motion: no-preference`. That means the still frame is what you
 * get from reduced motion, from an old browser, from a failed stylesheet, and
 * from a print -- rather than being a fallback nobody ever looks at.
 *
 * The figcaption states the sequence in words unconditionally, so a reader who
 * cannot see the motion loses nothing.
 */
.loop { margin: 0; }
.loop-frame {
  background: var(--wzp-bg-surface, #ffffff);
  border: 1px solid var(--wzp-border-strong, #87847e);
  border-radius: 12px; padding: 1.1rem;
}
.loop svg { display: block; width: 100%; height: auto; }
.loop figcaption {
  margin-top: .85rem; color: var(--wzp-text-secondary, #55534e);
  font-size: .9rem; max-width: 54ch;
}

/* Panels and the artefact, in their resting state. */
.flow-panel { fill: var(--wzp-bg-surface-sunken, #f2f1ed); stroke: var(--wzp-border-strong, #87847e); stroke-width: 2; }
.flow-label { fill: var(--wzp-text-primary, #1a1a18); font-size: 15px; font-weight: 600; }
.flow-sub { fill: var(--wzp-text-muted, #6b6862); font-size: 12px; letter-spacing: .07em; text-transform: uppercase; }
.flow-seam { stroke: var(--wzp-accent-text, #245046); stroke-width: 2; stroke-dasharray: 5 5; }
.flow-seam-label { fill: var(--wzp-accent-text, #245046); font-size: 11px; letter-spacing: .07em; text-transform: uppercase; }
.flow-artefact { fill: var(--wzp-mastery-mastered, #1f4b44); }
.flow-check { stroke: var(--wzp-status-success, #2f7a4f); stroke-width: 3; fill: none; }
/* The still frame is a COMPLETED workflow: the artefact rests in the last
 * application, both hand-offs are ticked, so the two applications it has already
 * passed through must read as done rather than untouched. Without this the static
 * picture contradicted itself. The keyframes below start these back at
 * border-strong, because a loop restarting is the workflow starting over. */
.flow-panel--2, .flow-panel--3 { stroke: var(--wzp-mastery-mastered, #1f4b44); }

@media (prefers-reduced-motion: no-preference) {
  /* ONE 12s TIMELINE, NO animation-delay ANYWHERE. Every stage is expressed as a
   * percentage of the same shared period, so all five elements restart together.
   *
   * Staging with animation-delay is the obvious way to write this and it is wrong
   * for an infinite loop: a 12s animation delayed by 3.2s completes its cycle at
   * 15.2s, so it never re-aligns with an undelayed 12s animation. The two drift
   * permanently out of phase. The live page showed the consequence -- the second
   * application marked done while the artefact had just restarted back in the
   * first -- and no amount of tuning the delays fixes it, because the periods
   * simply do not divide.
   *
   * Nothing here needs animation-fill-mode either. `backwards` was only papering
   * over the delays: with the still frame as the default resting style, a delayed
   * element shows the FINISHED state until its animation starts. Remove the delays
   * and that problem disappears at the source rather than being suppressed.
   *
   * Stage boundaries, so the numbers below are readable rather than magic:
   *   0-8%    the artefact sits in app one
   *   25-33%  it has crossed seam one and sits in app two
   *   58-66%  it has crossed seam two and sits in app three
   *   92-100% it rests there, then everything resets together */
  .flow-artefact { animation: flow-travel  12s cubic-bezier(.5,0,.5,1) infinite; }
  .flow-check--1 { animation: flow-tick-1  12s linear infinite; }
  .flow-check--2 { animation: flow-tick-2  12s linear infinite; }
  .flow-panel--2 { animation: flow-touch-2 12s linear infinite; }
  .flow-panel--3 { animation: flow-touch-3 12s linear infinite; }

  @keyframes flow-travel {
    0%,   8%  { transform: translateX(0); }
    25%,  33% { transform: translateX(238px); }
    58%,  66% { transform: translateX(476px); }
    92%, 100% { transform: translateX(476px); }
  }

  /* A tick appears once its seam has been crossed and clears just before the reset,
   * so a restart never reads as a second hand-off. */
  @keyframes flow-tick-1 {
    0%,  22%  { opacity: 0; }
    27%, 94%  { opacity: 1; }
    98%, 100% { opacity: 0; }
  }
  @keyframes flow-tick-2 {
    0%,  55%  { opacity: 0; }
    60%, 94%  { opacity: 1; }
    98%, 100% { opacity: 0; }
  }

  /* An application reads as done from the moment the artefact lands in it. */
  @keyframes flow-touch-2 {
    0%,  30%  { stroke: var(--wzp-border-strong, #87847e); }
    34%, 94%  { stroke: var(--wzp-mastery-mastered, #1f4b44); }
    98%, 100% { stroke: var(--wzp-border-strong, #87847e); }
  }
  @keyframes flow-touch-3 {
    0%,  63%  { stroke: var(--wzp-border-strong, #87847e); }
    67%, 94%  { stroke: var(--wzp-mastery-mastered, #1f4b44); }
    98%, 100% { stroke: var(--wzp-border-strong, #87847e); }
  }
}

/* ---- the walkthrough ------------------------------------------------------ */

.steps { list-style: none; margin: 2.5rem 0 0; padding: 0; display: grid; gap: 3.5rem; }
.step { display: grid; gap: 1.5rem; grid-template-columns: minmax(15rem, 22rem) 1fr; align-items: start; }
.step-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.7rem; height: 1.7rem; border-radius: 999px;
  background: var(--wzp-accent-default, #2f5d50); color: var(--wzp-text-on-accent, #ffffff);
  font-size: .85rem; font-weight: 700; margin-bottom: .7rem;
}
.step h3 { margin: 0 0 .5rem; font-size: 1.15rem; letter-spacing: -0.01em; }
.step p { margin: 0 0 .6rem; color: var(--wzp-text-secondary, #55534e); font-size: .95rem; }
.step .note { color: var(--wzp-text-muted, #6b6862); font-size: .88rem; }

/* A screen, framed. No fake window chrome, no tilt, no drop shadow: the frame is
 * a 1px border because the screenshot is evidence, not an illustration. */
.shot { margin: 0; }
.shot img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--wzp-border-strong, #87847e);
  border-radius: 10px;
  background: var(--wzp-bg-surface, #ffffff);
}
.shot figcaption { margin-top: .6rem; font-size: .85rem; color: var(--wzp-text-muted, #6b6862); }

/* ---- status --------------------------------------------------------------- */

dl.status { margin: 1.75rem 0 0; }
dl.status div { display: grid; grid-template-columns: 11rem 1fr; gap: 1rem; padding: .85rem 0; border-top: 1px solid var(--wzp-border-subtle, #e5e3de); }
dl.status div:first-child { border-top: 0; }
dl.status dt { font-weight: 600; font-size: .95rem; }
dl.status dd { margin: 0; color: var(--wzp-text-secondary, #55534e); font-size: .95rem; }
/* Status is a state, so it takes the colour of the state -- working, in progress,
 * and not built are the same three-way distinction the product makes everywhere.
 * The dt text says which, so the colour is reinforcement. */
dl.status div.is-working  dt { border-left: 4px solid var(--wzp-mastery-ready, #2f7a4f); padding-left: .6rem; }
dl.status div.is-progress dt { border-left: 4px solid var(--wzp-mastery-attempted, #b4531f); padding-left: .6rem; }
dl.status div.is-absent   dt { border-left: 4px dashed var(--wzp-mastery-not-started, #8a8781); padding-left: .6rem; }

a { color: var(--wzp-text-link, var(--wzp-accent-default, #2f5d50)); text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }

footer {
  border-top: 1px solid var(--wzp-border-subtle, #e5e3de);
  padding: 2.5rem 0 3.5rem; color: var(--wzp-text-muted, #6b6862); font-size: .9rem;
}
footer ul { list-style: none; padding: 0; margin: 0 0 1.2rem; display: flex; flex-wrap: wrap; gap: 1.2rem; }

@media (max-width: 52rem) {
  .step { grid-template-columns: 1fr; gap: 1rem; }
}
@media (max-width: 34rem) {
  dl.status div { grid-template-columns: 1fr; gap: .2rem; }
  .topnav { display: none; }
}

/* The blanket stop. Anything animated that this file forgot still halts here --
 * and the hand-off loop above never starts in the first place, because its
 * keyframes are scoped to no-preference rather than being switched off after the
 * fact. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}
