/* main.css
   Tokens, type scale, layout, both color schemes.
   Palette is BUILD.md option B: pale cool ground, spruce ink,
   one violet signal hue reserved for links and the focus ring. */

/* ---------- Color tokens ----------
   Every ratio below is measured (WCAG 2.x relative luminance),
   not estimated. Light scheme is canonical. */

:root {
  --ground: #e9ebe7; /* page background */
  --ink:    #1f3230; /* body text        11.24:1 on --ground */
  --muted:  #4a5a57; /* metadata         6.05:1  on --ground */
  --signal: #4a2e8c; /* links + focus ring 8.49:1 on --ground (text and non-text) */
  --surface:#dfe3dc; /* hover tint, non-text, no contrast claim */
  --ok:     #17693c; /* correct state in the memory test 5.61:1 on --ground */
  --err:    #a62639; /* wrong state in the memory test   5.92:1 on --ground */
}

@media (prefers-color-scheme: dark) {
  :root {
    --ground: #131a17; /* page background */
    --ink:    #e4eae6; /* body text        14.49:1 on --ground */
    --muted:  #9fafa8; /* metadata         7.72:1  on --ground */
    --signal: #b7a5ef; /* links + focus ring 8.12:1 on --ground (text and non-text) */
    --surface:#1c2420; /* hover tint, non-text, no contrast claim */
    --ok:     #7ed09a; /* correct state in the memory test 9.58:1 on --ground */
    --err:    #f2a3a8; /* wrong state in the memory test   8.91:1 on --ground */
  }
}

/* ---------- Type tokens ----------
   Scale ratio 1.25 (major third), base 1.125rem (18px).
   Six steps: -1, 0, 1, 2, 3, 4. */

:root {
  --t-1: 0.9rem;    /* metadata, captions */
  --t0:  1.125rem;  /* body */
  --t1:  1.406rem;  /* h3, lede */
  --t2:  1.758rem;  /* h2 */
  --t3:  2.197rem;  /* large display */
  --t4:  clamp(2.197rem, 1.4rem + 3.5vw, 2.747rem); /* h1, fluid */

  /* Recommended faces (see README.md): Familjen Grotesk for display,
     Literata for body. Both OFL. Until the files exist in fonts/,
     these stacks are the deliberate system fallback, not a default. */
  --font-display: "Familjen Grotesk", "Avenir Next", Avenir, Seravek,
                  "Segoe UI", system-ui, sans-serif;
  --font-body: Literata, Charter, "Bitstream Charter", "Sitka Text",
               Cambria, Georgia, serif;
}

/* @font-face blocks, commented out until the files are downloaded.
   Download from Google Fonts (fonts.google.com), "Download family",
   and copy the variable-font TTFs into fonts/:
   - Familjen Grotesk: FamiljenGrotesk[wght].ttf
     (specimen: fonts.google.com/specimen/Familjen+Grotesk)
   - Literata: Literata[opsz,wght].ttf and Literata-Italic[opsz,wght].ttf
     (specimen: fonts.google.com/specimen/Literata)

@font-face {
  font-family: "Familjen Grotesk";
  src: url("../fonts/FamiljenGrotesk[wght].ttf") format("truetype-variations");
  font-weight: 400 700;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("../fonts/Literata[opsz,wght].ttf") format("truetype-variations");
  font-weight: 200 900;
  font-display: swap;
}
@font-face {
  font-family: "Literata";
  src: url("../fonts/Literata-Italic[opsz,wght].ttf") format("truetype-variations");
  font-weight: 200 900;
  font-style: italic;
  font-display: swap;
}
*/

/* ---------- Base ---------- */

* {
  box-sizing: border-box;
}

html {
  color-scheme: light dark;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t0);
  line-height: 1.6;
}

.wrap {
  max-width: 42rem; /* about 65-70 characters of body text */
  margin-inline: auto;
  padding-inline: clamp(1rem, 4vw, 2rem);
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 650;
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin-block: 0 0.5em;
}

h1 { font-size: var(--t4); }
h2 { font-size: var(--t2); margin-block-start: 0; }
h3 { font-size: var(--t1); }

p {
  margin-block: 0 1em;
}

main > section {
  margin-block-end: 5rem;
}

/* ---------- Links ---------- */

a {
  color: var(--signal);
  text-decoration-thickness: 0.06em;
  text-underline-offset: 0.15em;
}

a:hover {
  text-decoration-thickness: 0.14em; /* motion moment 4: one property */
}

/* ---------- Focus ----------
   Native fallback first. Every interactive element keeps this
   outline so the site passes 2.4.7 and 2.4.11 with JavaScript off.
   3px solid --signal: 8.49:1 (light) and 8.12:1 (dark) against
   --ground, well past the 3:1 non-text minimum. */

:focus-visible {
  outline: 3px solid var(--signal);
  outline-offset: 2px;
}

/* js/focus-ring.js adds .focus-ring-active to <html> only after it
   has initialized. Only then does the travelling ring replace the
   native outline. If the script never runs, this rule never applies. */
.focus-ring-active :focus-visible {
  outline-color: transparent;
}

/* The travelling ring itself (created by js/focus-ring.js). */
.focus-ring {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  border: 3px solid var(--signal);
  border-radius: 0.5rem;
  opacity: 0;
  pointer-events: none;
  z-index: 100;
  /* motion moment 1: the transit, 180ms with a slight overshoot */
  transition:
    transform 180ms cubic-bezier(0.2, 0.85, 0.3, 1.15),
    width 180ms cubic-bezier(0.2, 0.85, 0.3, 1.15),
    height 180ms cubic-bezier(0.2, 0.85, 0.3, 1.15),
    opacity 120ms ease-out;
}

.focus-ring.is-visible {
  opacity: 1;
}

/* ---------- Skip link ---------- */

.skip-link {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  padding: 0.5rem 1rem;
  background: var(--signal);
  color: var(--ground); /* 8.49:1 light, 8.12:1 dark */
  border-radius: 0.25rem;
  transform: translateY(-300%);
}

.skip-link:focus {
  transform: none;
}

/* ---------- Header ---------- */

.site-header {
  padding-block: 1.5rem 4rem;
}

.site-header nav {
  display: flex;
  gap: 1.5rem;
  justify-content: flex-end;
  font-family: var(--font-display);
  font-size: var(--t-1);
}

/* ---------- Hero ---------- */

.hero {
  margin-block-end: 3rem;
}

.hero .lede {
  font-size: var(--t1);
  margin-block-end: 0.25em;
}

/* motion moment 2: hero lines reveal once, on load only */
.hero > * {
  animation: hero-rise 480ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
}

.hero > *:nth-child(2) { animation-delay: 90ms; }
.hero > *:nth-child(3) { animation-delay: 180ms; }

@keyframes hero-rise {
  from {
    opacity: 0;
    transform: translateY(0.5rem);
  }
}

/* ---------- Tab hint ---------- */

.tab-hint {
  font-size: var(--t-1);
  color: var(--muted);
  margin-block-end: 5rem;
}

.tab-hint button {
  font: inherit;
  color: var(--signal);
  background: none;
  border: none;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 0.15em;
  cursor: pointer;
}

/* ---------- Project list ---------- */

.projects {
  list-style: none;
  margin: 0;
  padding: 0;
}

.project {
  margin-block-end: 3rem;
}

.project-meta {
  font-size: var(--t-1);
  color: var(--muted);
  margin-block: 0.25em 0.5em;
}

.project img {
  max-width: 100%;
  height: auto;
  border-radius: 0.5rem;
}

/* motion moment 3: scroll reveal. js/motion.js adds .will-reveal only
   to cards below the fold, and only when reduced motion is off, so
   nothing is ever hidden without JavaScript. */
.will-reveal {
  opacity: 0;
  transform: translateY(0.75rem);
}

.will-reveal.revealed {
  opacity: 1;
  transform: none;
  transition:
    opacity 450ms ease-out,
    transform 450ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* ---------- Case studies ---------- */

.case-study h1 {
  margin-block-end: 0.15em;
}

.case-study > section {
  margin-block-end: 3.5rem;
}

.facts dt {
  font-family: var(--font-display);
  font-weight: 650;
}

.facts dd {
  margin: 0 0 0.75em;
}

/* Marked scaffolding for unfinished content. Deliberately looks like
   a placeholder, not like the site's design. */
.placeholder {
  border: 2px dashed var(--muted);
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
  color: var(--muted);
  font-size: var(--t-1);
}

/* ---------- Forms (new-project.html) ---------- */

fieldset {
  border: none;
  padding: 0;
  margin: 0 0 2.5rem;
}

legend {
  font-family: var(--font-display);
  font-weight: 650;
  font-size: var(--t1);
  padding: 0;
  margin-block-end: 0.75rem;
}

.field {
  margin-block-end: 1rem;
}

.field label {
  display: block;
  font-family: var(--font-display);
  font-size: var(--t-1);
  margin-block-end: 0.25rem;
}

.field .hint {
  font-size: var(--t-1);
  color: var(--muted);
  margin-block: 0.25rem 0;
}

input,
select,
textarea {
  font: inherit;
  width: 100%;
  padding: 0.5rem;
  color: var(--ink);
  background: var(--ground);
  border: 1px solid var(--muted);
  border-radius: 0.25rem;
}

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 2rem;
}

@media (max-width: 40rem) {
  .form-grid {
    grid-template-columns: 1fr;
  }
}

a.button {
  display: inline-block;
  text-decoration: none;
  margin-inline-end: 0.75rem;
  margin-block-end: 0.5rem;
}

/* The action links under a case study's first section. */
.button-group {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-block: 0 3.5rem;
}

.button-group .button {
  margin: 0;
}

.button {
  font: inherit;
  font-family: var(--font-display);
  font-weight: 650;
  width: auto;
  background: var(--signal);
  color: var(--ground); /* 8.49:1 light, 8.12:1 dark */
  border: none;
  border-radius: 0.25rem;
  padding: 0.5rem 1.25rem;
  cursor: pointer;
}

.form-errors ul {
  margin: 0;
  padding-inline-start: 1.25rem;
}

.json-out {
  background: var(--surface);
  padding: 1rem;
  border-radius: 0.5rem;
  overflow-x: auto;
  font-family: ui-monospace, Menlo, Consolas, monospace;
  font-size: var(--t-1);
}

/* ---------- Memory test (work/color.html) ---------- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.mt-panel h3 {
  margin-block-start: 0;
}

.mt-panel h3:focus {
  outline: none; /* the panel heading is a focus target, not a control;
                    focus-ring.js and :focus-visible never match here
                    because focus() is programmatic */
}

.mt-timer {
  font-family: var(--font-display);
  font-weight: 650;
  font-size: var(--t1);
}

.mt-image {
  max-width: 100%;
  height: auto;
  border-radius: 0.5rem;
}

.mt-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  margin-block: 1rem;
}

.mt-controls .button {
  margin: 0;
}

.button.mt-quiet {
  background: transparent;
  color: var(--signal); /* 8.49:1 light, 8.12:1 dark */
  text-decoration: underline;
  text-underline-offset: 0.15em;
  padding-inline: 0.25rem;
}

.mt-form {
  display: flex;
  gap: 0.75rem;
  margin-block: 0.5rem 1rem;
}

.mt-form input {
  flex: 1;
  min-width: 0;
}

.mt-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.mt-list li {
  padding-block: 0.15rem;
}

/* The distractor's number chain. The last number is the current one. */
.mt-chain {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.75rem;
  font-family: var(--font-display);
  font-weight: 650;
  font-size: var(--t1);
}

/* Result entries. The word and the mark carry the meaning, color is
   an addition. Ratios are on the --ok and --err tokens. */
.mt-entries li {
  opacity: 1;
}

.mt-entries li.mt-pending {
  opacity: 0.5;
}

.mt-entries .mt-verdict {
  font-family: var(--font-display);
  font-size: var(--t-1);
  margin-inline-start: 0.5rem;
}

.mt-entries li.mt-ok .mt-verdict { color: var(--ok); }
.mt-entries li.mt-err .mt-verdict { color: var(--err); }
.mt-entries li.mt-dup .mt-verdict { color: var(--muted); }

.mt-entries li.mt-ok.mt-animate {
  animation: mt-rise 250ms ease-out;
}

.mt-entries li.mt-err.mt-animate {
  animation: mt-shake 250ms ease-in-out;
}

@keyframes mt-rise {
  from { transform: translateY(4px); }
  to   { transform: translateY(0); }
}

@keyframes mt-shake {
  0%   { transform: translateX(0); }
  20%  { transform: translateX(-3px); }
  45%  { transform: translateX(3px); }
  70%  { transform: translateX(-3px); }
  100% { transform: translateX(0); }
}

.mt-chart {
  width: 100%;
  height: auto;
  margin-block: 1rem;
}

.mt-chart text {
  font-family: var(--font-body);
  font-size: 14px;
  fill: var(--ink);
}

.mt-objects {
  list-style: none;
  margin: 0;
  padding: 0;
}

.mt-objects li {
  margin-block-end: 0.5rem;
}

.mt-objects .mt-accepted {
  display: block;
  font-size: var(--t-1);
  color: var(--muted);
}

/* ---------- Footer ---------- */

.site-footer {
  padding-block: 2rem 3rem;
  font-size: var(--t-1);
  color: var(--muted);
}

/* ---------- Reduced motion ----------
   All four motion moments off. js/focus-ring.js checks the same
   query with matchMedia for the script-driven parts. */

@media (prefers-reduced-motion: reduce) {
  .focus-ring {
    transition: none;
  }

  .hero > * {
    animation: none;
  }

  a {
    transition: none;
  }

  .will-reveal,
  .will-reveal.revealed {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .mt-entries li.mt-ok.mt-animate,
  .mt-entries li.mt-err.mt-animate {
    animation: none;
  }
}
