/* Saalt — house style is Sol LeWitt (see docs/design.md).
 *
 * Rules that this file must keep:
 *   1. Depth never comes from simulated light. No gradients-as-lighting, no
 *      shadows, no blur. Surfaces separate by colour at a hard edge.
 *   3. Line is a primary element. Hairlines and bands are structure.
 *   4. Four to six colours per screen.
 *   5. The frame does the work — heavy bands and wide margins.
 *   8. WCAG AA or it doesn't ship.
 *
 * The colour tokens below are generated from internal/ink. Do not hand-edit a
 * hex here: change the ink sequence in internal/ink/palette.go and run
 * `make css`, which rewrites the :root block and re-runs the contrast tests.
 */

/* @generated-palette-start */
:root {
  --background: #f5f3ef;
  --surface: #f4edd7;
  --text: #3f4d5a;
  --muted: #616466;
  --accent: #b02c17;
  --rule: #305c96;
}

[data-app="just-tides"] {
  --background: #10306f;
  --text: #f5f3ef;
  --muted: #b3c6d7;
  --surface: #1c4281;
  --on-surface: #f5f3ef;
  --accent: #f0db8b;
  --rule: #8eb0cd;
  --band-1: #dde3e7;
  --band-2: #8eb0cd;
  --band-3: #527fb0;
  --band-4: #305c96;
  --band-5: #1c4281;
}

[data-app="rise-and-run"] {
  --background: #d0d6da;
  --text: #3f4d5a;
  --muted: #525150;
  --surface: #f5f3ef;
  --on-surface: #3f4d5a;
  --accent: #8b2933;
  --rule: #6c6b69;
  --band-1: #e7e5e1;
  --band-2: #bab9b6;
  --band-3: #8e8d8a;
  --band-4: #6c6b69;
  --band-5: #3f4d5a;
}

[data-app="five-dice"] {
  --background: #f3e7c2;
  --text: #3f4d5a;
  --muted: #525150;
  --surface: #f5f3ef;
  --on-surface: #3f4d5a;
  --accent: #b33027;
  --rule: #81555b;
  --band-1: #f4edd7;
  --band-2: #ebc450;
  --band-3: #da845b;
  --band-4: #c6503c;
  --band-5: #722e22;
}
/* @generated-palette-end */

:root {
  --measure: 34rem;      /* reading measure — roughly 70 characters */
  --frame: clamp(1rem, 5vw, 3.5rem);
  --grid-gap: 6px;       /* the gutter between cards — a drawn line, not a shadow */
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--background);
  color: var(--text);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  font-size: clamp(1.0625rem, 0.98rem + 0.4vw, 1.1875rem);
  line-height: 1.6;
}

/* ---- The frame -------------------------------------------------------- */

/* There is no top navigation bar — see base.html. The band across the top of
 * the page is frame, not chrome: rule 5, the frame does the work.
 *
 * Every colour here is a token, so the footer inverts with an app's ground
 * without a single per-app rule. */


.frame-bottom {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  max-width: 72rem;
  margin: clamp(3.5rem, 9vw, 6rem) auto 0;
  padding: 1.25rem var(--frame) clamp(2rem, 5vw, 3rem);
  border-top: 2px solid var(--rule);
  font-size: 0.95rem;
}
.frame-bottom p { margin: 0; }

.foot-links {
  display: flex;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.foot-links a {
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
}
.foot-links a:hover {
  color: var(--text);
  border-bottom-color: var(--accent);
}

/* The only route home now that the top bar is gone, so it is the loud one. */
.foot-home a {
  color: var(--accent);
  font-weight: 700;
  text-decoration: none;
  border-bottom: 2px solid var(--accent);
  padding-bottom: 2px;
}
.foot-home a:hover { color: var(--text); border-bottom-color: var(--text); }

/* ---- Content ---------------------------------------------------------- */

/* main does not constrain width; the blocks inside it do. That is what lets an
 * app's hero run edge to edge while its prose stays at a reading measure. */
main { display: block; }

/* The shared content measure. Anything that should line up with the frame uses
 * this rather than setting its own margins. */
.prose,
.apps,
.document {
  margin-inline: auto;
  padding-inline: var(--frame);
}
.apps { max-width: 72rem; }

.hero {
  position: relative;
  text-align: center;
  padding: clamp(3rem, 10vw, 6rem) 1rem clamp(2rem, 6vw, 4rem);
  overflow: hidden;
}

h1 {
  font-size: clamp(2.25rem, 1.4rem + 4vw, 4rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 0.5rem;
  font-weight: 800;
  position: relative;
}

.tagline {
  font-size: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
  color: var(--accent);
  font-weight: 600;
  margin: 0 auto;
  max-width: 30ch;
  position: relative;
}

/* App pages set --accent to their own ink, so the CTA and rules pick up the
 * app's identity with no per-app rules here. */

/* Site motif — superimposed line directions.
 *
 * LeWitt's four basic directions (vertical, horizontal, and the two diagonals)
 * laid over one another. Every app in the family claims a different part of the
 * vocabulary; Tides has sweeping bands, Rise & Run isometric geometry, Five
 * Dice grids, and the site takes the directions.
 *
 * This is flat colour on a hard edge — repeating-linear-gradient here is a
 * ruled line, not a light source, so rule 1 holds.
 */
.directions {
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.16;
  background-image:
    repeating-linear-gradient(0deg,    var(--rule) 0 1px, transparent 1px 28px),
    repeating-linear-gradient(90deg,   var(--rule) 0 1px, transparent 1px 28px),
    repeating-linear-gradient(45deg,   var(--accent) 0 1px, transparent 1px 40px),
    repeating-linear-gradient(-45deg,  var(--accent) 0 1px, transparent 1px 40px);
  /* Fade the field out at the edges by masking, never by lightening the ink. */
  -webkit-mask-image: radial-gradient(ellipse at center, #000 35%, transparent 78%);
  mask-image: radial-gradient(ellipse at center, #000 35%, transparent 78%);
}

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

/* An app page is that app's page, not a page in a suite: the ground colour runs
 * the full width and continues behind the content below. Tokens come from the
 * [data-app] block, so there are no per-app rules here.
 *
 * The copy is in normal flow with the ground behind it — nothing is layered
 * over it. An earlier version put the motif in an absolutely positioned layer
 * behind the copy and the copy could not be seen; this structure removes the
 * stacking question entirely. */
/* No divider under the hero. The hero and the content below it share a ground,
 * so a rule across the page only chops it in two — the motif already marks
 * where one ends and the other begins. */
.hero-app {
  background: var(--background);
  margin-bottom: clamp(2.5rem, 7vw, 4.5rem);
}

.hero-copy {
  max-width: 44rem;
  margin-inline: auto;
  padding: clamp(3.5rem, 11vw, 7rem) var(--frame) clamp(2.5rem, 7vw, 4.5rem);
  text-align: center;
}
.hero-copy > * { margin-inline: auto; }
.hero-copy h1 { color: var(--text); }
.hero-copy .tagline { color: var(--accent); margin-top: 0.75rem; }

/* The motif is a band at the foot of the hero — a horizon, not a backdrop. It
 * is decorative structure: aria-hidden, and the page reads identically without
 * it. */
.motif { display: block; line-height: 0; }
.motif svg {
  display: block;
  width: 100%;
  height: clamp(7rem, 16vw, 12rem);
}

/* Tides: the wave layers drift horizontally as the page scrolls.
 *
 * Scroll-driven CSS animation, so there is no JavaScript — which matters,
 * because the Content-Security-Policy is default-src 'none' with no script-src
 * and would block a script outright. Browsers without animation-timeline get
 * the static stepped field, which is the design regardless.
 *
 * The SVG is drawn 200% wide so translating left never exposes its right edge.
 * Motion is linear translation: steady, not physical. */
.motif-bands svg { width: 200%; }

@keyframes wave-drift {
  to { transform: translateX(var(--drift)); }
}

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    .motif-bands .wave {
      animation: wave-drift linear both;
      animation-timeline: scroll(root block);
      will-change: transform;
    }
    /* Nearer layers travel further: the parallax depth cue. */
    .motif-bands .wave-1 { --drift: -3%; }
    .motif-bands .wave-2 { --drift: -6%; }
    .motif-bands .wave-3 { --drift: -10%; }
    .motif-bands .wave-4 { --drift: -16%; }
  }
}

/* Accent fill, ground-coloured label — the pair is contrast-tested in both
 * directions, so the button works on a light or a dark page unchanged. */
.cta {
  display: inline-block;
  margin-top: 1.75rem;
  padding: 0.7rem 1.6rem;
  background: var(--accent);
  color: var(--background);
  font-weight: 700;
  text-decoration: none;
  border: 2px solid var(--accent);
}
.cta:hover { background: var(--background); color: var(--accent); }

/* ---- Prose ------------------------------------------------------------ */

.prose {
  max-width: calc(var(--measure) + var(--frame) * 2);
  color: var(--text);
}
.prose h2 {
  font-size: 1.4rem;
  margin: 2.5rem 0 0.75rem;
  padding-bottom: 0.35rem;
  border-bottom: 2px solid var(--rule);
}
.prose ul { padding-left: 1.2rem; }
.prose li { margin-bottom: 0.5rem; }
.prose a { color: var(--accent); text-underline-offset: 0.15em; }

/* Lists carry a rule per item rather than a bullet glyph: line is a primary
 * element here, and a feature list is exactly the kind of enumerated thing the
 * house style likes to make visible. */
.prose ul {
  list-style: none;
  padding-left: 0;
}
.prose ul li {
  position: relative;
  padding: 0.45rem 0 0.45rem 1.6rem;
  border-top: 1px solid var(--rule);
}
.prose ul li:last-child { border-bottom: 1px solid var(--rule); }
.prose ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.05em;
  width: 0.9rem;
  height: 2px;
  background: var(--accent);
}

/* Reading surfaces sit in the quiet register (rule: loud for moments, quiet for
 * surfaces people work on). */
/* A reading surface: a panel on the app's ground, delineated by its border.
 * The border is what does the work — TestAppPalettesClearAA pins it at 3:1
 * against both the ground and the surface, and deliberately does not demand
 * that the two large fields contrast with each other. */
.document {
  max-width: var(--measure);
  margin: clamp(2rem, 6vw, 4rem) auto 0;
  background: var(--surface);
  color: var(--on-surface, var(--text));
  border: 1px solid var(--rule);
  padding: clamp(1.5rem, 5vw, 3rem);
  padding-inline: clamp(1.5rem, 5vw, 3rem);
}
.document .prose { max-width: none; padding-inline: 0; }
.document h1 { font-size: clamp(1.75rem, 1.4rem + 1.5vw, 2.5rem); text-align: left; }
.document .updated {
  color: var(--muted);
  font-size: 0.9rem;
  margin: 0 0 2rem;
  padding-bottom: 1rem;
  border-bottom: 2px solid var(--rule);
}

/* ---- App cards -------------------------------------------------------- */

.apps {
  list-style: none;
  padding: 0;
  margin: clamp(2.5rem, 8vw, 4.5rem) 0 0;
  display: grid;
  gap: var(--grid-gap);
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

.app-card a {
  display: block;
  height: 100%;
  padding: 1.75rem;
  background: var(--surface);
  border: 1px solid var(--rule);
  color: inherit;
  text-decoration: none;
}
/* Rule 1: the hover state changes colour, it does not raise the card. */
.app-card a:hover { background: var(--accent); color: var(--background); }
.app-card a:hover .tagline { color: var(--background); }
.app-card h2 { margin: 0 0 0.25rem; font-size: 1.5rem; }
.app-card .tagline { font-size: 1rem; text-align: left; margin: 0 0 0.75rem; max-width: none; }
.app-card p:last-child { margin: 0; color: inherit; }

/* ---- Accessibility ---------------------------------------------------- */

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus {
  left: var(--frame);
  top: 0.5rem;
  z-index: 10;
  background: var(--accent);
  color: var(--background);
  padding: 0.5rem 1rem;
}

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

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
