/* always.Agog — the workbench.
 *
 * Colour, type scale and spacing come from `/brand/tokens.css` — the SAME file
 * apps/pulsar aliases as `$brand/tokens.css`, not a copy of it, so the two
 * sites cannot drift. That file is the brand handoff verbatim; additions belong
 * here or in the app's own tokens.local.css, never in it.
 *
 * Font faces come from /brand/fonts.css, shared with apps/pondering.
 *
 * NO JAVASCRIPT. The page is served with `script-src 'none'` and
 * scripts/check-agog.ts fails the gate if a <script> appears, so every bit of
 * motion and reveal below is CSS. That constraint is the point, not a
 * limitation: it means the page cannot acquire a tracker by accident.
 *
 * The intent is a work surface seen slightly from above — measured, annotated,
 * a bit cosmic at the edges — rather than a product landing page. Accents are
 * PER TILE (`--accent`, set on the tile class below) so no project's colour
 * leaks into the page chrome. They cannot be set inline: `style-src 'self'`
 * drops style attributes silently.
 */

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

:root {
  --gutter: var(--space-6);
  --measure: 62rem;
  --hairline: #241d30;
  /* 4.60:1 on the tile background, 4.79:1 on the page. The previous #6f6580
     measured 3.55:1 on tiles — under the 4.5:1 floor for text this small,
     which is what every annotation, status line and section label uses. */
  --annotation: #827790;
}

body {
  position: relative;
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  padding: var(--space-12) var(--gutter) var(--space-8);
  background: var(--space-base);
  color: var(--text-primary);
  font-family: var(--font-interface);
  font-size: var(--text-16);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

/* ---- The work surface -------------------------------------------------
 * A measured grid, deliberately near the threshold of visibility: it should
 * register as "squared paper" only once you look for it. Fading it out toward
 * the bottom stops it reading as a repeating tile. */

.surface {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--hairline) 1px, transparent 1px),
    linear-gradient(to bottom, var(--hairline) 1px, transparent 1px);
  background-size: 96px 96px;
  opacity: 0.5;
  mask-image: radial-gradient(120% 90% at 50% 0%, #000 30%, transparent 100%);
}

main,
footer {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--measure);
  margin: 0 auto;
}

main {
  flex: 1;
  display: grid;
  gap: var(--space-16);
  align-content: start;
}

/* ---- Masthead ---------------------------------------------------------- */

.masthead {
  display: grid;
  justify-items: start;
  gap: var(--space-4);
}

.wordmark {
  /* Sized by width so the SVG's own aspect ratio drives the height; its viewBox
     is cropped tight, so a fixed height would letterbox it. */
  width: min(100%, 19rem);
  height: auto;
}

/* The dated stamp: a workbench snapshot, not a marketing date. */
.stamp {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  color: var(--annotation);
  font-size: var(--text-12);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.stamp i {
  width: var(--space-8);
  height: 1px;
  background: currentColor;
  opacity: 0.5;
}

.intro {
  margin: var(--space-2) 0 0;
  max-width: 30ch;
  color: var(--text-secondary);
  font-size: var(--text-20);
  line-height: var(--leading-heading);
}

/* ---- Section labels ---------------------------------------------------- */

.label {
  margin: 0 0 var(--space-6);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--hairline);
  color: var(--annotation);
  font-size: var(--text-12);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

/* ---- The bench --------------------------------------------------------- */

.tiles {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-4);
}

@media (min-width: 48rem) {
  .tiles {
    /* Uneven on purpose: two objects side by side, one laid across the bench
       below them. A uniform 3-up would read as a pricing table. */
    grid-template-columns: 1fr 1fr;
  }

  .tile-wide {
    grid-column: 1 / -1;
  }

  /* Laid across the bench, so it gets a different internal arrangement: the
     object sits BESIDE its label rather than under it. Stacking here would
     leave the right half of a full-width tile empty, which reads as unfinished
     rather than as deliberate space. */
  .tile-wide .tile-face {
    grid-template-columns: 1fr minmax(0, 24rem);
    /* Flexible rows 1 and 5 are empty spacers. They exist so the text group
       (idx / name / desc) sits vertically CENTRED against the figure beside it:
       the figure is taller than the text, and without them the slack all
       collected below the description and read as a gap someone forgot to
       close. Doing it this way keeps the markup identical to the narrow tiles —
       the alternative was a wrapper element only this tile needs. */
    grid-template-rows: 1fr auto auto auto 1fr auto;
    column-gap: var(--space-12);
  }

  .tile-wide .idx {
    grid-area: 2 / 1;
  }

  .tile-wide .name {
    grid-area: 3 / 1;
  }

  .tile-wide .desc {
    grid-area: 4 / 1;
  }

  .tile-wide .figure {
    grid-area: 1 / 2 / 6 / 3;
    align-self: center;
    margin: 0;
  }

  .tile-wide .meta {
    grid-area: 6 / 1 / 7 / -1;
  }
}

/* Per-project accents. These live HERE, not on a style attribute in the HTML:
   the page is served with `style-src 'self'`, which blocks inline styles
   outright — an inline `--accent` is dropped silently and every accent falls
   back to inherited text colour. scripts/check-agog.ts now fails on style
   attributes so this cannot come back. */
.tile-pulsar {
  /* The brand violet is #7657ff, but at index-number size it measures 4.22:1
     on the tile background. This is the token set's own accent-interactive,
     one step lighter, at 4.78:1 — same hue, reaches the floor. */
  --accent: #8064ff;
}

.tile-parlance {
  --accent: #8adfff; /* photon-ion: data, openness */
}

.tile-top {
  --accent: #ffd166; /* warm, for divination and artifacts */
}

.tile {
  position: relative;
}

/* Registration marks: two corner crops per tile, so a tile reads as something
   placed on the surface and aligned to it rather than a card floating on it. */
.tile::before,
.tile::after {
  content: '';
  position: absolute;
  width: 9px;
  height: 9px;
  border: 1px solid var(--accent);
  opacity: 0.35;
  transition: opacity var(--motion-standard) var(--ease-standard);
  pointer-events: none;
  z-index: 2;
}

.tile::before {
  top: -1px;
  left: -1px;
  border-right: 0;
  border-bottom: 0;
}

.tile::after {
  right: -1px;
  bottom: -1px;
  border-left: 0;
  border-top: 0;
}

.tile:hover::before,
.tile:hover::after,
.tile:focus-within::before,
.tile:focus-within::after {
  opacity: 1;
}

.tile-face {
  position: relative;
  display: grid;
  grid-template-rows: auto auto auto 1fr auto;
  gap: var(--space-3);
  height: 100%;
  padding: var(--space-6);
  background: color-mix(in srgb, var(--space-elevated) 82%, transparent);
  border: 1px solid var(--hairline);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--motion-standard) var(--ease-standard);
}

a.tile-face:hover {
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
}

a.tile-face:focus-visible {
  outline: var(--focus-ring-width) solid var(--accent);
  outline-offset: var(--focus-ring-offset);
}

.idx {
  color: var(--accent);
  font-size: var(--text-12);
  font-weight: var(--font-weight-semibold);
  letter-spacing: 0.16em;
  font-variant-numeric: tabular-nums;
}

.name {
  margin: 0;
  font-size: var(--text-32);
  font-weight: var(--font-weight-medium);
  line-height: var(--leading-tight);
}

.desc {
  margin: 0;
  max-width: 46ch;
  color: var(--text-secondary);
}

.meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin: var(--space-2) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--hairline);
  color: var(--annotation);
  font-size: var(--text-12);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.go {
  color: var(--accent);
  font-size: var(--text-16);
  transition: transform var(--motion-standard) var(--ease-standard);
}

a.tile-face:hover .go {
  transform: translate(2px, -2px);
}

/* ---- Diagrams ----------------------------------------------------------
 * Fragments of the thing, not illustrations of it. Everything is drawn in
 * currentColor-ish greys except the one element the accent is reserved for, so
 * a tile reads as a technical note with one thing highlighted. */

.figure {
  margin: var(--space-2) 0;
}

/* Sized by WIDTH only. An earlier `max-height` fought `width: 100%`: the box
   stayed full-width while the drawing scaled down to fit the height and centred
   itself inside, so the figure floated in dead space. Let the viewBox drive the
   height and cap the width instead. */
.dia {
  display: block;
  width: 100%;
  max-width: 21rem;
  height: auto;
  overflow: visible;
}

.tile-wide .dia {
  max-width: 24rem;
}

.dia [class*='ring'],
.dia .board,
.dia .plug,
.dia .chip,
.dia .bubble,
.dia .sieve,
.dia .sieve-tick,
.dia .layer,
.dia .card rect,
.dia .wire,
.dia .trace,
.dia .flow {
  stroke: #4a4157;
  stroke-width: 1.25;
}

.dia .trace,
.dia .flow,
.dia .sieve-tick {
  stroke-width: 1;
}

.dia .pad,
.dia .core {
  fill: #4a4157;
}

/* Annotations stay hidden until you look closer. */
.dia .notes {
  opacity: 0;
  transition: opacity var(--motion-standard) var(--ease-standard);
}

.dia .lead {
  stroke: var(--accent);
  stroke-width: 1;
  opacity: 0.6;
}

.dia .note {
  fill: var(--annotation);
  font-family: var(--font-interface);
  font-size: 8px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.dia .note-end {
  text-anchor: end;
}

.tile-face:hover .notes,
.tile-face:focus-visible .notes {
  opacity: 1;
}

/* -- 01 Pulsar: the arcs are the emission, so they carry the accent. */
.dia-pulsar .arc {
  stroke: var(--accent);
  stroke-width: 1.5;
  opacity: 0.25;
}

.tile-face:hover .dia-pulsar .arc,
.tile-face:focus-visible .dia-pulsar .arc {
  animation: emit 2.4s var(--ease-standard) infinite;
}

.dia-pulsar .a2 {
  animation-delay: 0.25s !important;
}

.dia-pulsar .a3 {
  animation-delay: 0.5s !important;
}

@keyframes emit {
  0%,
  100% {
    opacity: 0.2;
  }
  40% {
    opacity: 1;
  }
}

/* -- 02 Common Parlance: the redactions are the point. */
.dia-parlance .redact {
  fill: var(--accent);
  stroke: none;
  opacity: 0.3;
  transform-origin: left center;
  transform: scaleX(1);
  transition:
    transform var(--motion-standard) var(--ease-standard),
    opacity var(--motion-standard) var(--ease-standard);
}

.tile-face:hover .dia-parlance .redact,
.tile-face:focus-visible .dia-parlance .redact {
  opacity: 0.9;
}

.dia-parlance .r2 {
  transition-delay: 0.08s;
}

.dia-parlance .r3 {
  transition-delay: 0.16s;
}

.dia-parlance .l3 {
  stroke: var(--accent);
}

/* -- 03 Divining Top: cards orbit the concentric top. */
.dia-top .core {
  fill: var(--accent);
}

.dia-top .o1 {
  stroke: var(--accent);
  opacity: 0.55;
}

.dia-top .card rect {
  stroke: #4a4157;
  fill: var(--space-base);
}

.dia-top .orbit {
  transform-origin: 150px 75px;
}

/* Each card is parked at its own angle around the top; the whole group turns.
   Rotating the GROUP and counter-nothing keeps the cards tangent, which reads
   as orbit rather than as a spinning image. */
.dia-top .c1 {
  transform: rotate(0deg);
  transform-origin: 150px 75px;
}

.dia-top .c2 {
  transform: rotate(90deg);
  transform-origin: 150px 75px;
}

.dia-top .c3 {
  transform: rotate(180deg);
  transform-origin: 150px 75px;
}

.dia-top .c4 {
  transform: rotate(270deg);
  transform-origin: 150px 75px;
}

.tile-face:hover .dia-top .orbit,
.tile-face:focus-visible .dia-top .orbit {
  animation: orbit 22s linear infinite;
}

@keyframes orbit {
  to {
    transform: rotate(360deg);
  }
}

/* ---- Notebook ---------------------------------------------------------- */

.rooms {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-4);
}

@media (min-width: 48rem) {
  .rooms {
    grid-template-columns: 1fr 1fr;
  }
}

.room-face {
  display: grid;
  gap: var(--space-2);
  height: 100%;
  padding: var(--space-6) 0;
  border-top: 1px solid var(--hairline);
  color: inherit;
  text-decoration: none;
}

a.room-face {
  transition: border-color var(--motion-standard) var(--ease-standard);
}

a.room-face:hover {
  border-color: var(--accent-focus);
}

a.room-face:focus-visible {
  outline: var(--focus-ring-width) solid var(--accent-focus);
  outline-offset: var(--focus-ring-offset);
}

.room-name {
  margin: 0;
  font-size: var(--text-20);
  font-weight: var(--font-weight-medium);
  line-height: var(--leading-tight);
}

.room-desc {
  margin: 0;
  max-width: 44ch;
  color: var(--text-secondary);
  font-size: var(--text-14);
}

.room-face .go {
  color: var(--accent-focus);
  justify-self: start;
}

a.room-face:hover .go {
  transform: translateX(3px);
}

.tag {
  justify-self: start;
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-control);
  color: var(--annotation);
  font-size: var(--text-12);
  font-weight: var(--font-weight-medium);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ---- Archive ----------------------------------------------------------- */

.archive-note {
  margin: 0;
  max-width: 52ch;
  color: var(--text-secondary);
  font-size: var(--text-14);
}

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

footer {
  margin-top: var(--space-16);
  padding-top: var(--space-6);
  border-top: 1px solid var(--hairline);
  color: var(--text-secondary);
  font-size: var(--text-14);
}

footer p {
  margin: 0;
}

/* ---- The always.X construction ----------------------------------------
 * IMPLEMENTATION.md: Instrument Sans is the stable prefix, Manrope 500 is
 * reserved for the live suffix, and the period is the brand's violet joint.
 * The outlined always-agog.svg still owns the actual signature in the
 * masthead — this is the same construction applied to sibling names. */

.prefix {
  font-family: var(--font-interface);
  font-weight: var(--font-weight-medium);
}

.dot {
  color: var(--agog-mark);
}

.suffix {
  font-family: var(--font-brand-suffix);
  font-weight: var(--font-weight-medium);
}

/* ---- Wider viewports --------------------------------------------------- */

@media (min-width: 48rem) {
  :root {
    --gutter: var(--space-12);
  }

  body {
    padding-top: var(--space-16);
  }

  .wordmark {
    width: min(100%, 23rem);
  }

  .intro {
    font-size: var(--text-32);
    max-width: 26ch;
  }
}

/* ---- Reduced motion ----------------------------------------------------
 * The tokens already zero the durations. What is left is the two infinite
 * animations, which must not merely slow down — they must not run. Annotations
 * and redactions still reveal, just without transition. */

@media (prefers-reduced-motion: reduce) {
  .tile-face:hover .dia-pulsar .arc,
  .tile-face:focus-visible .dia-pulsar .arc,
  .tile-face:hover .dia-top .orbit,
  .tile-face:focus-visible .dia-top .orbit {
    animation: none;
  }

  .tile-face:hover .dia-pulsar .arc,
  .tile-face:focus-visible .dia-pulsar .arc {
    opacity: 0.7;
  }
}
