/* Oscar Echesirim, portfolio
   ---------------------------------------------------------------------------
   One stylesheet, no build step, no dependencies.

   The governing rule for colour: it is never decoration. Teal means data. Red
   means something was removed. Amber means something was flagged and left
   unfixed. If a value needs a colour for any other reason, it does not get one.

   Contrast against the paper colour is measured, not assumed, and enforced by
   checks/site-checks.sh. Every token below that carries text clears 4.5:1.
   --------------------------------------------------------------------------- */

:root {
  /* Colour. Ratios are against --paper. */
  --paper: #FFFFFF;
  --ink: #111317;        /* 18.59:1  primary text */
  --muted: #5B6168;      /*  6.26:1  secondary text, captions */
  --rule: #E2E4E3;       /*  1.28:1  hairlines and borders only, never text */
  --data: #1F6F5C;       /*  6.02:1  every data mark */
  --removed: #9A3324;    /*  7.31:1  marks something removed */
  --flagged: #8A5D0C;    /*  5.75:1  marks flagged and not fixed */

  /* Type scale */
  --t-13: 0.8125rem;
  --t-15: 0.9375rem;
  --t-17: 1.0625rem;
  --t-21: 1.3125rem;
  --t-27: 1.6875rem;
  --t-34: 2.125rem;
  --t-47: 2.9375rem;

  /* Spacing */
  --gutter: 24px;
  --measure: 65ch;
  --col: 1080px;
}

@media (min-width: 768px) {
  :root { --gutter: 40px; }
}


/* Base
   --------------------------------------------------------------------------- */

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

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: Archivo, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: var(--t-17);
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }

a { color: inherit; }

img, svg { display: block; max-width: 100%; }

/* Long single words in a large heading must not force the page sideways. */
h1, h2, h3 { overflow-wrap: break-word; }

:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 1px;
}


/* Layout
   --------------------------------------------------------------------------- */

.wrap {
  width: 100%;
  max-width: var(--col);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Prose is capped by characters, not pixels, so the measure holds at any
   font size. */
.measure { max-width: var(--measure); }


/* Utilities
   --------------------------------------------------------------------------- */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  background: var(--ink);
  color: var(--paper);
  padding: 10px 16px;
  font-size: var(--t-15);
  font-weight: 500;
  text-decoration: none;
}

.skip-link:focus {
  left: var(--gutter);
  top: 8px;
}

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


/* Top bar
   ---------------------------------------------------------------------------
   No collapsible menu and no JavaScript. The four labels are short enough to
   sit on their own row at 320px, so nothing is ever hidden from anyone and
   there is no toggle to fail. The bar wraps from one row to two as the
   viewport narrows.
   --------------------------------------------------------------------------- */

.topbar {
  border-bottom: 1px solid var(--rule);
}

.topbar > .wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px 28px;
  padding-block: 18px;
}

.identity {
  font-size: var(--t-17);
  font-weight: 600;
  letter-spacing: -0.011em;
  text-decoration: none;
  white-space: nowrap;
}

.identity .role {
  font-weight: 400;
  color: var(--muted);
}

.topbar nav {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 22px;
  margin-left: auto;
}

.topbar nav a {
  font-size: var(--t-15);
  font-weight: 500;
  color: var(--muted);
  text-decoration: none;
  padding-block: 6px;
  text-decoration-color: transparent;
}

.topbar nav a:hover,
.topbar nav a:focus-visible {
  color: var(--ink);
}

/* CV and Email sit apart from the section links: same row, separated by a
   hairline, so a convinced reader has somewhere to go without scrolling. */
.topbar .direct {
  display: flex;
  gap: 18px;
  padding-left: 22px;
  border-left: 1px solid var(--rule);
}

.topbar .direct a {
  color: var(--ink);
  font-weight: 500;
}

@media (max-width: 639px) {
  .topbar nav { margin-left: 0; width: 100%; }
  .topbar .direct {
    margin-left: auto;
    padding-left: 18px;
  }
}


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

.site-footer {
  margin-top: 96px;
  border-top: 1px solid var(--rule);
}

.site-footer > .wrap {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 18px 32px;
  padding-block: 32px 44px;
}

.site-footer ul {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 22px;
}

.site-footer a {
  font-size: var(--t-15);
  font-weight: 500;
  text-decoration: none;
  text-underline-offset: 3px;
  padding-block: 6px;
}

.site-footer a:hover,
.site-footer a:focus-visible { text-decoration: underline; }

.site-footer .colophon {
  font-size: var(--t-13);
  color: var(--muted);
}


/* Motion
   ---------------------------------------------------------------------------
   One orchestrated moment exists on this site: the hero bars drawing in once
   on load. Everything else is static. Under a reduced-motion preference the
   animation is removed, never the thing it animates.
   --------------------------------------------------------------------------- */

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


/* Hero
   ---------------------------------------------------------------------------
   The page opens on one finding, stated as a sentence, with its evidence
   directly beneath and its exclusions named in the caption. No buttons: a
   convinced reader uses the links already in the top bar.
   --------------------------------------------------------------------------- */

.hero { padding-block: 56px 8px; }

.hero h1 {
  font-size: clamp(2rem, 1.3rem + 3.2vw, var(--t-47));
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.026em;
  max-width: 24ch;
}

.hero-chart { margin-top: 40px; max-width: 760px; }

.hero-chart figcaption {
  margin-top: 16px;
  font-size: var(--t-13);
  color: var(--muted);
  line-height: 1.5;
  max-width: 62ch;
}

/* A proportion bar. The track is a container, not a data series, so it takes
   the hairline colour rather than a second data tint. */
.proportion { display: flex; flex-direction: column; gap: 10px; }

.proportion-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: 8px 16px;
}

.proportion-row .name { font-size: var(--t-15); }
.proportion-row .value { font-size: var(--t-15); font-weight: 600; }
.proportion-row.secondary .name,
.proportion-row.secondary .value { color: var(--muted); font-weight: 400; }

.proportion-track {
  grid-column: 1 / -1;
  height: 18px;
  background: var(--rule);
  overflow: hidden;
}

.proportion-fill {
  height: 100%;
  background: var(--data);
  transform-origin: left center;
  animation: draw-bar 520ms cubic-bezier(0.2, 0.7, 0.3, 1) both;
}

/* The bar's real width is its static CSS width. The animation only overrides
   it from zero, so if the animation never runs the bar is already correct. */
@keyframes draw-bar {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}


/* Sections
   --------------------------------------------------------------------------- */

.section { padding-block: 64px 0; }

.section > h2 {
  font-size: var(--t-21);
  font-weight: 600;
  letter-spacing: -0.012em;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rule);
}

.prose { font-size: var(--t-17); max-width: var(--measure); }
.prose + .prose { margin-top: 14px; }

/* First paragraph after a section heading clears the heading rule. */
.section-lead { margin-top: 22px; }


/* Project list
   ---------------------------------------------------------------------------
   A quiet list, not a grid of cards. Each entry is a title, the one thing it
   found, and the tools, separated by hairlines.
   --------------------------------------------------------------------------- */

.projects > li { border-bottom: 1px solid var(--rule); }

.project {
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px 32px;
  padding-block: 22px;
  text-decoration: none;
}

.project h3 {
  font-size: var(--t-21);
  font-weight: 600;
  letter-spacing: -0.014em;
}

a.project h3 { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
a.project:hover h3 { text-decoration-color: var(--data); }

.project .found { color: var(--muted); max-width: 54ch; }
.project .tools { font-size: var(--t-13); color: var(--muted); }
.project .status { font-size: var(--t-13); color: var(--muted); }

@media (min-width: 768px) {
  .project {
    grid-template-columns: minmax(0, 1fr) 200px;
    align-items: baseline;
  }
  .project h3 { grid-column: 1; }
  .project .found { grid-column: 1; }
  .project .tools,
  .project .status { grid-column: 2; grid-row: 1 / span 2; text-align: right; }
}


/* About
   --------------------------------------------------------------------------- */

.about-grid { display: grid; gap: 36px; margin-top: 28px; }

@media (min-width: 768px) {
  .about-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 56px; }
}

.about-grid h3 {
  font-size: var(--t-15);
  font-weight: 600;
  margin-bottom: 10px;
}

.about-grid li { font-size: var(--t-15); padding-block: 5px; }
.about-grid li .where { color: var(--muted); }

.stack dt { font-size: var(--t-15); font-weight: 600; margin-top: 12px; }
.stack dt:first-child { margin-top: 0; }
.stack dd { font-size: var(--t-15); color: var(--muted); margin: 2px 0 0; }


/* Project pages
   ---------------------------------------------------------------------------
   One template, used by every project page, so a third project needs no new
   design work. The devices come from the Audit Trail direction: provenance
   stated before anything is claimed, colour that means something, and a group
   size beside every proportion.
   --------------------------------------------------------------------------- */

.project-head { padding-block: 48px 0; }

.back {
  font-size: var(--t-15);
  color: var(--muted);
  text-decoration: none;
  text-underline-offset: 3px;
}
.back:hover, .back:focus-visible { color: var(--ink); text-decoration: underline; }

.project-head h1 {
  margin-top: 18px;
  font-size: clamp(1.75rem, 1.2rem + 2.4vw, var(--t-34));
  font-weight: 600;
  line-height: 1.16;
  letter-spacing: -0.022em;
  max-width: 26ch;
}

.project-head .standfirst {
  margin-top: 16px;
  font-size: var(--t-21);
  color: var(--muted);
  line-height: 1.45;
  max-width: 56ch;
}

/* Record header: provenance as label and value pairs on hairlines. Not boxed
   tiles with large numbers. */
.record {
  margin-top: 40px;
  border-top: 1px solid var(--rule);
}

.record > div {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 4px 24px;
  align-items: baseline;
  padding-block: 11px;
  border-bottom: 1px solid var(--rule);
}

.record dt { font-size: var(--t-15); color: var(--muted); }
.record dd { margin: 0; font-size: var(--t-15); font-weight: 500; text-align: right; }

/* A struck-through count is a real edit, so it takes the colour that means
   "removed" and nothing else on the site uses it. */
.record .was {
  color: var(--removed);
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
  font-weight: 400;
  margin-right: 8px;
}

@media (min-width: 768px) {
  .record { display: grid; grid-template-columns: 1fr 1fr; column-gap: 56px; }
}

/* Findings. Not numbered: they are independent results, not a sequence. */
.findings { margin-top: 8px; }

.finding {
  padding-block: 26px;
  border-bottom: 1px solid var(--rule);
  display: grid;
  gap: 10px;
}

.finding h3 {
  font-size: var(--t-21);
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -0.012em;
  max-width: 50ch;
}

.finding .evidence {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 28px;
  align-items: baseline;
}

.finding .magnitude {
  font-size: var(--t-27);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--data);
  line-height: 1;
}

.finding .group { font-size: var(--t-15); color: var(--muted); }
.finding .caveat { font-size: var(--t-15); color: var(--muted); max-width: 62ch; }

/* Method. Numbered, because the scripts genuinely run in order and are named
   that way in the repository. This is the only numbered list on the site. */
.method { margin-top: 24px; counter-reset: step; }

.method > li {
  display: grid;
  grid-template-columns: 2.6rem minmax(0, 1fr);
  gap: 0 8px;
  padding-block: 14px;
  border-bottom: 1px solid var(--rule);
  align-items: baseline;
}

.method > li::before {
  counter-increment: step;
  content: "0" counter(step);
  font-size: var(--t-15);
  font-weight: 500;
  color: var(--muted);
}

.method .what { font-size: var(--t-17); font-weight: 600; }
.method .detail { grid-column: 2; font-size: var(--t-15); color: var(--muted); max-width: 64ch; margin-top: 3px; }

/* Data-quality log. The differentiator: a decision not to fix something,
   with the reason, is better evidence of judgement than a finding is. */
.log { margin-top: 24px; border-top: 1px solid var(--rule); }

.log > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 4px 24px;
  padding-block: 15px;
  border-bottom: 1px solid var(--rule);
}

.log .issue { font-size: var(--t-17); }
.log .why { font-size: var(--t-15); color: var(--muted); max-width: 64ch; }

.log .decision {
  font-size: var(--t-15);
  font-weight: 600;
  white-space: nowrap;
}
.log .decision.resolved { color: var(--data); }
.log .decision.flagged { color: var(--flagged); }
.log .decision.accepted { color: var(--muted); font-weight: 500; }

@media (min-width: 768px) {
  .log > li { grid-template-columns: minmax(0, 1fr) 150px; }
  .log .decision { grid-column: 2; grid-row: 1 / span 2; text-align: right; }
}

.log-summary {
  margin-top: 18px;
  font-size: var(--t-15);
  color: var(--muted);
}

/* Limits. Four lines stating what a descriptive analysis cannot tell you. */
.limits { margin-top: 24px; }
.limits > li {
  font-size: var(--t-17);
  padding-block: 12px;
  border-bottom: 1px solid var(--rule);
  max-width: 68ch;
}

.repo-link { margin-top: 36px; }
.repo-link a {
  font-size: var(--t-17);
  font-weight: 500;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}
.repo-link a:hover { text-decoration-color: var(--data); }
