/* Hallmark · macrostructure: Photographic · H6 hero knobs: image=full-bleed, caption=none, text=below
 * genre: editorial · verb: redesign (single-page) · mood: photographic / place-led · tone: austere-editorial
 * theme: custom · vibe: "river slate, cool paper, concrete arches"
 * paper: oklch(97% 0.006 240) · accent: oklch(45% 0.130 250) · focus: oklch(50% 0.180 250)
 * type: IBM Plex Sans 700 (display) / 400 (body) / 500 (emphasis) · IBM Plex Mono (code, 1 slot)
 *   Single-family by request (rev 2). Permitted under typography.md line 7 — IBM Plex is an
 *   institutional/engineering face on an IEEE workshop page, so the one voice IS the choice.
 *   Hierarchy therefore rests on weight (700 vs 400, a 300-unit step) + tracking + size, with
 *   no second face to lean on. Do not soften heads to 500/600; the delta is the hierarchy.
 * axes: light / grotesk-sans / cool
 * nav: N6 Newspaper masthead (issue-line=none, wordmark=2xl, rule=double)
 * footer: Ft1 Mast-headed (wordmark=2xl, tagline=roman, links=none)
 * sections: S2 Hanging heads · F1 Bento (topics, tiles=10, spans=irregular, border=hairline-all)
 *           F4 Step sequence as a timeline (dates, layout=horizontal >=64rem / vertical below,
 *             4 nodes evenly spaced, connector=rail)
 *           T4 Stat strip (submissions, 3-up, number=display, qualifier=under)
 *           C3 Typographic link (emails, underline=solid, hover=thicken, arrow=none)
 * enrichment: real photography (user-supplied), 1 full-bleed hero fold, <picture> art-direction
 *   (portrait < 768px, landscape >= 768px — both assets still ship, one per viewport)
 * motion: colour-shift on nav links · underline thicken on links · no scroll reveals (macro is motion-off)
 *
 * text=below, not overlaid: a headline over this photograph needs a >=0.87-alpha scrim to hold
 * 4.5:1 against the bright water reflections (gate 40), which would have hidden the photograph.
 * The image keeps the fold; the headline sits in the paper band beneath it.
 *
 * rev 2 (2026-08-09): collapsed to one type family · instructions 2x2 keeps the per-block top
 * rule but drops the internal border-cross (blocks are unequal height, so it never aligned) ·
 * mid-page photo band removed — one photograph is the whole photographic budget now.
 *
 * rev 3 (2026-08-09): dark→light slate palette, amber accent out, slate-blue in · lede no longer
 * indented one grid column (it read as a misalignment against every flush-left section) ·
 * topics F3 list → F1 bento · dates → to-scale horizontal timeline.
 *   Palette note: --muted had to move from L52% to L45%. On dark paper L66% cleared 6.2:1; the
 *   naive inversion to L52% on light paper lands at 3.9:1 and fails body text. --rule went to
 *   L80% for the same reason — at L88% a hairline is 1.26:1 on L97 paper, effectively invisible,
 *   and hairlines are this page's entire divider language.
 *
 * rev 4 (2026-08-09): bento tile 10 back to text-md — span varies, type size doesn't · timeline
 * evened to 4 nodes (not 5: 08 Jan carries two deadlines, and separate even positions would
 * imply separate days) · Contact normalised to text-base / pt-2xl, matching every other section.
 *   Key-info emphasis, one system: accent = a number the reader must obey. Timeline dates and
 *   page limits are accent + weight 500/700, and the timeline date now outranks its own label.
 *
 * rev 5 (2026-08-09): hero meta strip reverted to the quiet 12.8px uppercase row of rev 3 —
 * the 20px key-facts band from rev 4 was not wanted. Emphasis for key info therefore rests on
 * the timeline dates and the page limits alone; the strip stays a quiet caption under the photo.
 *
 * rev 6 (2026-08-09): the logo PNG carries a white wordmark baked in for the old dark theme,
 * invisible on light paper. Both instances now crop to the coloured mark via .u-logo — see the
 * comment there. Any future light-mode asset should be checked for hard-coded white artwork.
 *
 * rev 8 (2026-08-09): photography moved to WebP, no JPEG fallback in <picture>. That omission is
 * deliberate and depends on an earlier decision: because the headline sits in a paper band BELOW
 * the photograph rather than over it, a failed image degrades to an empty band with every word on
 * the page still readable. WebP is ~97% of browsers; insuring the remaining 3% by shipping both
 * formats would double the image count for a decorative asset. If the headline is ever moved back
 * over the photo, that trade no longer holds — add `<source type="image/webp">` + a JPEG <img>.
 * The logo and favicon stay PNG: alpha, and favicons want PNG/ICO for the widest support.
 *
 * rev 7 (2026-08-09): extracted from index.html's inline <style>. The single-file constraint was
 * lifted by the web chair to keep the repo readable. Three files now: css/tokens.css (values),
 * css/style.css (this — base + components), js/tailwind-config.js (token → utility mapping).
 * The token block previously existed twice (inline + an unlinked tokens.css at the repo root);
 * that duplicate is gone.
 *
 * slop test: 58/58 · contrast: pass (40–41) · nav/footer/hero: pass (42–45) · honest: pass (46)
 * chrome: pass (47) · tokens: pass (48) · responsive: pass (49) · icons: pass (30)
 * mobile: pass (34, 49, 50–57) @ 320/375/414/768 px
 * pre-emit critique: P5 H5 E4 S5 R5 V4
 * studied: no · context: user-provided (mood + palette source chosen by web chair) · v0.8.0
 *
 * Values live in css/tokens.css — never hard-code a colour or font-family below this line.
 */

html, body { overflow-x: clip; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  background: var(--color-paper);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
[id] { scroll-margin-top: var(--space-3xl); }

h1, h2, h3, h4 {
  font-style: normal;          /* headers are roman, always */
  overflow-wrap: anywhere;     /* gate 51 — long compound words break rather than overflow */
  min-width: 0;
}

:focus-visible {
  outline: var(--rule-thick) solid var(--color-focus);
  outline-offset: 3px;         /* never transitioned — appears instantly */
}
::selection { background: var(--color-accent); color: var(--color-accent-ink); }

/* C3 · typographic link — one signal: the underline thickens. No transition needed. */
.u-link {
  color: var(--color-accent);
  text-decoration-line: underline;
  text-decoration-thickness: var(--rule-hair);
  text-underline-offset: 0.2em;
}
.u-link:hover,
.u-link:focus-visible { text-decoration-thickness: var(--rule-thick); }
.u-link:active        { color: var(--color-ink); }

/* N6 · masthead nav link — colour shift only */
.u-navlink {
  color: var(--color-ink-2);
  white-space: nowrap;
  transition: color var(--dur-short) var(--ease-out);
}
.u-navlink:hover,
.u-navlink:focus-visible { color: var(--color-accent); }
.u-navlink:active        { color: var(--color-ink); }

/* Disabled register — anchors have no :disabled, so aria-disabled carries it.
   Three channels, never opacity alone. For the submission link before the CFP opens. */
.u-link[aria-disabled="true"],
.u-navlink[aria-disabled="true"] {
  color: var(--color-neutral);
  text-decoration-line: none;
  opacity: 0.55;
  cursor: not-allowed;
  pointer-events: none;
}

/* F4 · timeline. ONE DOM tree — a chronological <ol> that reads as a vertical timeline up to
   64rem, then becomes a horizontal rail with each node placed by its actual date. No duplicated
   markup, so screen readers get the dates once, in chronological order, at every width.

   --pos  node position along the rail. FOUR nodes, evenly spaced across a 4–96% track so the
          first and last labels don't overhang the container: 4% · 34.67% · 65.33% · 96%.
          Four, not five, because 08 Jan carries two deadlines — giving them separate even
          positions would imply they fall on different days.
   --tier 1 drops a label to the lower band. Only the second 08 Jan label needs it, since it
          shares a node with the first. Even spacing clears every other label by 60px+.
   data-align  start (default) · center · end. The first label runs right from its node, the
          last hangs left from its node, the middle two straddle theirs — that's what keeps
          the end labels inside the container without narrowing the track. */
.u-timeline { list-style: none; }

.u-timeline > li {
  position: relative;
  padding-inline-start: var(--space-lg);
  padding-block-end: var(--space-lg);
  border-inline-start: var(--rule-hair) solid var(--color-rule);
}
.u-timeline > li:last-child { padding-block-end: 0; }
.u-timeline > li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.55em;
  width: var(--space-xs);
  height: var(--space-xs);
  margin-inline-start: calc(var(--space-xs) / -2);
  background: var(--color-accent);
}

@media (min-width: 64rem) {
  .u-timeline { position: relative; min-height: 13rem; }
  .u-timeline::before {                       /* the rail */
    content: "";
    position: absolute;
    inset-inline: 0;
    inset-block-start: 0;
    border-block-start: var(--rule-hair) solid var(--color-rule);
  }
  .u-timeline > li {
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: var(--pos);
    width: 9.5rem;
    padding: 0;
    border: 0;
    padding-block-start: calc(var(--tier) * 4rem + var(--space-md));
  }
  .u-timeline > li[data-align="center"] {     /* middle nodes — label straddles the node */
    transform: translateX(-50%);
    text-align: center;
  }
  .u-timeline > li[data-align="end"] {        /* last node — label hangs to the left */
    transform: translateX(-100%);
    text-align: end;
  }
  .u-timeline > li::before {                  /* node sits on the rail */
    inset-block-start: 0;
    margin-block-start: calc(var(--space-xs) / -2);
  }
  .u-timeline > li[data-align="center"]::before { inset-inline-start: 50%; }
  .u-timeline > li[data-align="end"]::before {
    inset-inline-start: auto;
    inset-inline-end: 0;
    margin-inline-start: 0;
    margin-inline-end: calc(var(--space-xs) / -2);
  }
  .u-timeline > li::after {                   /* leader from node down to its label */
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    width: var(--rule-hair);
    height: calc(var(--tier) * 4rem + var(--space-md));
    background: var(--color-rule);
  }
  .u-timeline > li[data-align="center"]::after { inset-inline-start: 50%; }
  .u-timeline > li[data-align="end"]::after {
    inset-inline-start: auto;
    inset-inline-end: 0;
  }
}
@media (min-width: 90rem) { .u-timeline > li { width: 11rem; } }

/* Logo · images/fairness_logo.png is 1509×1983 (aspect 0.761). The coloured mark occupies the
   top ~76%; below it the file carries a WHITE wordmark, baked in for the previous dark theme.
   On light paper that wordmark is invisible, and under object-contain it still consumed space —
   letterboxing the mark to ~76% of its box.

   Square box + cover + top edge shows exactly the top 1509px of the source: the mark, nothing
   under it. No information is lost — "FAIRNESS 2027" is live text in both the masthead wordmark
   and the h1, so the baked version was redundant (and worse for a11y and search anyway).

   If the wordmark artwork is wanted back, the fix is a dark-ink PNG or an SVG whose text can be
   recoloured — not a change here. Adjust the box's aspect if the crop clips the mark's base. */
.u-logo {
  object-fit: cover;
  object-position: top;
}

.u-tnum { font-variant-numeric: tabular-nums; }
.u-code {
  font-family: var(--font-mono);
  background: var(--color-paper-3);
  padding: var(--space-3xs) var(--space-2xs);
  overflow-wrap: anywhere;
}

@media (pointer: coarse) {
  .u-navlink, .u-link { min-height: 44px; display: inline-flex; align-items: center; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 150ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 150ms !important;
  }
}
