/* Hallmark · genre: editorial · macrostructure: Manifesto · nav: none · footer: Ft2
 * theme: custom · vibe: "fractured, austere, monochrome, industrial"
 * paper: oklch(13% 0.008 265) · accent: none (neutral — monochrome by brief)
 * display: Northgate Nine mark (Arial Black, outlined) · body: Space Grotesk · mono: JetBrains Mono
 * axes: dark / display-heavy / neutral
 * enrichment: none · studied: no · context: user-provided · v1.1.0
 * pre-emit critique: P5 H5 E5 S5 R5 V4
 *
 * contrast: pass (40–41) — text 5.52:1 to 17.37:1, focus ring 11.49:1
 * slop: pass (42–45) · honest: pass (46) · chrome: pass (47) · tokens: pass (48)
 * icons: pass (30) · responsive: pass (49) · mobile: pass (34, 49, 50–57)
 * verified at 320 / 375 / 414 / 768 / 1280 / 1920 — no axis scrolls at any width
 *
 * Two gates recorded as not-applicable rather than passed:
 *   26 — the page's only control is a mailto link; `:disabled` has no meaning on
 *        an anchor, so default + hover + active + focus-visible is the complete
 *        set here. A dead `a:disabled` rule would be worse than its absence.
 *   44a — asks for padding-block-end >= 1.3x start so a hero settles into the
 *        section beneath it. There is no section beneath: this is a single-screen
 *        poster, and an asymmetric frame would read as a mistake.
 */

@import url("tokens.css");

/* --- Reset ------------------------------------------------------------- */

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

html,
body {
  margin: 0;
  /* clip, never hidden — hidden on the root breaks position:sticky descendants
     and can trap scroll on iOS. */
  overflow-x: clip;
}

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

/* --- Page -------------------------------------------------------------- */

body {
  min-height: 100dvh;
  background-color: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-weight: 300;
  font-size: var(--text-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.page {
  position: relative;
  z-index: 1;
  min-height: 100dvh;
  display: grid;
  /* Content sits low-left with the upper-right field left open: the composition
     is deliberately off-centre, not a centred hero. The middle spacer row is
     what pushes the lock-up below the optical centre. */
  grid-template-rows: 1fr auto minmax(var(--space-xl), 0.42fr) auto;
  padding: var(--space-gutter);
}

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

.masthead { grid-row: 2; }

.masthead__mark {
  margin: 0;
  /* The mark is the display face; the SVG carries the accessible name.
     The 34rem cap keeps it confident on a 1920 canvas — at 27rem it read timid,
     occupying only ~22% of the width. */
  width: clamp(13.5rem, 40vw, 34rem);
  color: var(--color-ink);
}

.masthead__mark svg {
  display: block;
  width: 100%;
  height: auto;
}

.masthead__statement {
  /* Weight contrast is the counterpoint: the mark reads ~900, this reads 300.
     Shouting twice would flatten the hierarchy. */
  margin: var(--space-lg) 0 0;
  max-width: 46ch;
  min-width: 0;
  font-size: var(--text-md);
  font-weight: 300;
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--color-ink-2);
  text-wrap: balance;
  /* Insurance for future copy edits: lets the engine break inside a long
     unbroken word rather than pushing past the viewport. */
  overflow-wrap: anywhere;
}

/* --- Footer · Ft2 inline single line ----------------------------------- */

.colophon {
  grid-row: 4;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-xl);
  align-items: baseline;
  justify-content: space-between;
  padding-top: var(--space-md);
  border-top: var(--rule-hairline) solid var(--color-rule);
  font-family: var(--font-mono);
  /* --text-xs (10.24px) is too small to read comfortably even as micro-type. */
  font-size: var(--text-sm);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-muted);
}

/* The legal line drops to its own row beneath the main colophon line. It is
   de-emphasised by position and order, not by dropping below contrast. */
.colophon__legal {
  flex-basis: 100%;
  order: 3;
}

.colophon a {
  /* Never wraps: a two-line clickable label reads as a styling error. */
  white-space: nowrap;
  color: var(--color-ink-2);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
  transition: color var(--dur-micro) var(--ease-out);
}

/* Exactly one hover signal — a brightness shift. On a monochrome page that
   reads more clearly than thickening the rule, and it does not depend on
   distinguishing a 1px change. */
.colophon a:hover { color: var(--color-ink); }

.colophon a:active { color: var(--color-ink-2); }

/* Focus must appear instantly — never transition the ring itself. */
.colophon a:focus-visible {
  outline: var(--rule-heavy) solid var(--color-focus);
  outline-offset: 0.25em;
  color: var(--color-ink);
}

/* --- Entrance ----------------------------------------------------------
 * The fracture reveal: the lock-up arrives as solid type, then its eight cuts
 * slice through it in sequence, then the text settles. Transform + opacity only.
 * Whole sequence lands inside ~750ms; the cut stagger spans 294ms.
 *
 * The zeroed state lives only in the keyframe `from` (with fill-mode `both`),
 * never in the base rule — so if animation does not run at all, the cuts are
 * present and the mark is correct rather than silently solid.
 */

.cut {
  /* Scale from each cut's own start point: the top-left of its box for a cut
     leaning right, the top-right for one leaning left. */
  transform-box: fill-box;
  transform-origin: 0 0;
}

.cut--left { transform-origin: 100% 0; }

@media (prefers-reduced-motion: no-preference) {
  .cut {
    animation: cut-open 260ms var(--ease-out) both;
    animation-delay: calc(200ms + var(--i, 0) * 42ms);
  }

  .rise {
    animation: rise var(--dur-long) var(--ease-out) both;
    animation-delay: calc(var(--i, 0) * 260ms);
  }
}

@keyframes cut-open {
  from { transform: scale(0); }
  to   { transform: scale(1); }
}

@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* Spatial motion collapses to a crossfade: the cuts are simply already there. */
@media (prefers-reduced-motion: reduce) {
  .rise {
    animation: settle 150ms linear both;
    animation-delay: calc(var(--i, 0) * 60ms);
  }

  @keyframes settle {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  .colophon a { transition-duration: 150ms; }
}

/* --- Narrow viewports -------------------------------------------------- */

@media (max-width: 40rem) {
  .page {
    grid-template-rows: 1fr auto minmax(var(--space-lg), 0.25fr) auto;
  }

  .masthead__mark { width: min(100%, 17rem); }

  .masthead__statement { font-size: var(--text-base); }

  .colophon {
    /* Stack in reading order; the legal line last. `align-items: stretch` matters:
       with the baseline alignment inherited from the row layout, each item
       shrink-wraps and the long legal line overflows instead of wrapping. */
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-xs);
    /* The email is `nowrap` and must never wrap, so the tracking comes down here
       to leave room for an address longer than the placeholder. If the real one
       is very long, tighten this further rather than letting it wrap. */
    letter-spacing: 0.06em;
  }

  /* In column direction flex-basis sizes the height, so the row layout's
     `flex-basis: 100%` must be released here. */
  .colophon__legal { flex-basis: auto; }
}

/* Very short landscape viewports: drop the pushed-down bias so nothing clips. */
@media (max-height: 30rem) {
  .page { grid-template-rows: auto auto minmax(var(--space-md), 0.2fr) auto; }
  .masthead__mark { width: min(60%, 15rem); }
}
