/* ==========================================================================
   ag0osW3b — reset, base elements, layout primitives

   Load order is fixed by the layer declaration in themes.css:
     tokens → base → layout → components → utilities

   Nothing in this file may hardcode a color, a radius, or a font family.
   Everything comes from the semantic contract. See themes.css.
   ========================================================================== */

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

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

  body {
    margin: 0;
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
    background-color: var(--bg);
    /* The preset's ground: a drawing grid, a paper grain, or nothing. Always
       CSS-generated, never an asset. */
    background-image: var(--texture);
    background-size: var(--texture-size);
    color: var(--text);
    font-family: var(--font-body);
    font-size: var(--text-base);
    line-height: var(--leading-body);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  /* Hover and press feedback on interactive things. Layout properties are
     never animated.

     This deliberately excludes body. When the theme attributes change on
     <html>, a transition on body animates `color` and `background-color`
     toward their new values at the same time, and every paragraph on the page
     inherits that animating colour. On a light/dark flip the text is moving
     from cream to near-black while the ground moves from near-black to cream,
     so the two cross in the middle and contrast collapses to roughly 1:1 for
     the length of the transition. Components with their own `color` snapped
     while inherited text faded, so the page came apart as well as dimming.

     The theme change is a view transition instead. See below. */
  .btn,
  .swatch,
  .mode-toggle,
  .card {
    transition:
      background-color var(--dur) var(--ease),
      border-color var(--dur) var(--ease),
      color var(--dur) var(--ease);
  }

  /* Switching preset or mode repaints the whole page. A view transition
     crossfades two complete renderings rather than interpolating colour
     channels, so every intermediate frame is a blend of two legible states.

     The custom properties here resolve against the incoming preset, because
     the pseudo-elements are built after the attribute has changed. Arriving at
     console still snaps in steps and arriving at workshop still drifts, which
     was the point of having a crossfade at all. */
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: var(--dur-enter);
    animation-timing-function: var(--ease);
  }

  /* ---- Entrance ----------------------------------------------------------
     One orchestrated moment per page rather than motion scattered everywhere.
     Timing and easing come from the preset, so the same markup arrives
     differently in each: console clicks on in steps, workshop drifts up.

     `both` fill matters: the element holds its start state through its delay
     and its end state afterwards, so nothing flickers at either end. */
  @keyframes rise {
    from {
      opacity: 0;
      transform: translateY(0.7rem);
    }

    to {
      opacity: 1;
      transform: none;
    }
  }

  .enter > * {
    animation: rise var(--dur-enter) var(--ease) both;
  }

  .enter > :nth-child(1) { animation-delay: calc(var(--stagger) * 0); }
  .enter > :nth-child(2) { animation-delay: calc(var(--stagger) * 1); }
  .enter > :nth-child(3) { animation-delay: calc(var(--stagger) * 2); }
  .enter > :nth-child(4) { animation-delay: calc(var(--stagger) * 3); }
  .enter > :nth-child(n + 5) { animation-delay: calc(var(--stagger) * 4); }

  h1,
  h2,
  h3,
  h4 {
    font-family: var(--font-display);
    font-weight: var(--weight-display);
    letter-spacing: var(--tracking-display);
    line-height: 1.15;
    text-wrap: balance;
    margin: 0;
  }

  p,
  ul,
  ol,
  figure,
  blockquote {
    margin: 0;
  }

  a {
    color: var(--accent);
    text-decoration: none;
  }

  a:hover {
    color: var(--accent-hover);
    text-decoration: underline;
    text-underline-offset: 0.18em;
  }

  code,
  kbd,
  samp,
  pre {
    font-family: var(--font-mono);
  }

  img,
  svg,
  video {
    max-width: 100%;
    height: auto;
  }

  hr {
    height: 0;
    margin: var(--space-6) 0;
    border: 0;
    border-top: var(--border-width) solid var(--border);
  }

  ::selection {
    background: var(--accent-subtle);
    color: var(--text);
  }

  /* One focus treatment, defined once, verified against all eight surfaces.
     Components never restyle focus. */
  :focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
  }

  :focus:not(:focus-visible) {
    outline: none;
  }

  .skip-link {
    position: absolute;
    left: var(--space-4);
    top: var(--space-4);
    z-index: 100;
    padding: var(--space-2) var(--space-4);
    background: var(--surface);
    color: var(--text);
    border: var(--border-width) solid var(--border-strong);
    border-radius: var(--radius-sm);
    transform: translateY(-200%);
  }

  .skip-link:focus-visible {
    transform: translateY(0);
  }

  /* Motion is always an enhancement. Nothing here is the only signal that
     something happened. */
  @media (prefers-reduced-motion: reduce) {
    html {
      scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }

    /* The universal selector does not reach the view-transition pseudo tree,
       so the theme crossfade needs disabling by name. */
    ::view-transition-old(root),
    ::view-transition-new(root) {
      animation: none !important;
    }
  }
}

@layer layout {
  .container {
    width: 100%;
    max-width: var(--maxw);
    margin-inline: auto;
    padding-inline: var(--gutter);
  }

  .site-main {
    flex: 1 0 auto;
    position: relative;
    /* Keeps the grid layer's negative z-index inside this element, so it lands
       behind the content but still in front of the page's own background. */
    isolation: isolate;
    /* Feeds the section numbering that console and spec print in their
       headings. Reset per page so numbering starts at 01 every time. */
    counter-reset: section;
  }

  /* The preset's drawing grid, tracking the content column instead of the
     viewport. Same max-width and gutter as .container, and background-origin
     plus background-clip pin the lines to the content box, so the first rule
     lands on the rail's edge and every --grid-column after it: twelve columns
     at full width, with the text column starting on the third. Only spec
     resolves --grid to anything. */
  .site-main::before {
    content: "";
    position: absolute;
    inset: 0;
    max-width: var(--maxw);
    margin-inline: auto;
    padding-inline: var(--gutter);
    background-image: var(--grid);
    background-origin: content-box;
    background-clip: content-box;
    z-index: -1;
    pointer-events: none;
  }

  /* Vertical rhythm varies on purpose: sections breathe, groups stay tight. */
  .stack > * + * {
    margin-top: var(--space-7);
  }

  .stack-tight > * + * {
    margin-top: var(--space-4);
  }

  .page {
    padding-block: var(--space-7) var(--space-8);
  }

  /* The cap moves onto the children: on the header itself it would have
     clamped the whole rail grid to 46ch and collapsed both tracks. */
  .page__header {
    margin-bottom: var(--space-6);
  }

  .page__header > * {
    max-width: 46ch;
  }

  .page__header h1 {
    font-size: var(--text-2xl);
    margin-bottom: var(--space-3);
  }

  /* ---- The rail ----------------------------------------------------------
     Two tracks: a metadata margin and the text column.

     Every block that sits directly in the content column declares the same two
     tracks itself rather than inheriting them through a parent grid. They all
     live inside .container at the same width, so their columns line up without
     any nesting, subgrid, or block knowing what the rail is for.

     .rail-indent is for the few blocks that have nothing to put in the margin
     but still have to start on the text edge. */
  .railed {
    display: grid;
    grid-template-columns: var(--rail) minmax(0, 1fr);
    column-gap: var(--rail-gap);
    align-items: baseline;
  }

  .railed > * {
    grid-column: 2;
  }

  .railed > .rail {
    grid-column: 1;
    justify-self: var(--rail-align);
    text-align: var(--rail-align);
  }

  .rail-indent {
    padding-left: var(--rail-indent);
  }

  /* Below this the rail costs more than it gives: the text column would drop
     under the 65ch floor, so the margin folds back into the flow and its
     contents sit above what they annotate. 60rem is where 10 columns still
     hold 68ch in the widest-set preset. */
  @media (max-width: 60rem) {
    .railed {
      grid-template-columns: minmax(0, 1fr);
    }

    .railed > *,
    .railed > .rail {
      grid-column: 1;
      justify-self: stretch;
      text-align: left;
    }

    .rail-indent {
      padding-left: 0;
    }
  }

  /* Reading measure. Expressed in ch so it self-corrects per preset: the serif
     body of workshop and the mono of terminal need different pixel widths for
     the same number of characters. */
  .measure {
    max-width: 68ch;
  }

  .measure-narrow {
    max-width: 52ch;
  }

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

@layer utilities {
  .muted {
    color: var(--text-muted);
  }

  .faint {
    color: var(--text-faint);
  }

  .mono {
    font-family: var(--font-mono);
    font-size: 0.92em;
  }

  /* The one place --case-label is consumed. In every preset but console it
     resolves to `none`, so this is a label, not a shouted label. */
  .label {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-label);
    text-transform: var(--case-label);
    color: var(--text-muted);
  }
}
