/* ==========================================================================
   tanasevskyi.com — styles.css
   Step 3b: desktop base. No media queries here; the 1100px section-number
   fallback and the 600px metrics breakpoint belong to Step 4.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonts — five subset files from assets/fonts/, latin plus U+2192.
      U+2191-2193 is a range on purpose: the standard latin set lists U+2191
      and U+2193 separately, and the gap is U+2192, the arrow in "90 → 13 min".
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-sans-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122,
    U+2191-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-sans-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122,
    U+2191-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-sans-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122,
    U+2191-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122,
    U+2191-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('assets/fonts/ibm-plex-mono-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122,
    U+2191-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   2. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* colour — SPEC §3 */
  --bg:       #101014;
  --text:     #e8e8ec;
  --muted:    #9a9aa3;
  --accent:   #c3a5f9;
  --rule:     #26262c;
  --rule-ui:  #60606a;
  /* A dark grey, not a hue: on a dark page the heading dropping from --text
     to this and back reads as a flash, because the eye follows the luminance
     change in both directions. It sits there for about a second, so it must
     clear 4.5:1 on its own — this measures 4.66:1. #797983 was tried first
     and fails at 4.41:1; the darkest passing value, #7b7b85, clears by only
     0.7%, which is not the margin SPEC §3 asks for. */
  --highlight: #7d7d87;

  /* families */
  --font-sans: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* type scale — geometric, ratio 1.2, base 17px */
  --fs-xs:      0.75rem;    /* 12    */
  --fs-sm:      0.875rem;   /* 14    */
  --fs-base:    1.0625rem;  /* 17    */
  --fs-md:      1.275rem;   /* 20.4  */
  --fs-lg:      1.53rem;    /* 24.5  */
  --fs-xl:      1.835rem;   /* 29.4  */
  --fs-display: 3.175rem;   /* 50.8  */

  /* line heights */
  --lh-tight:  1.1;
  --lh-snug:   1.3;
  --lh-ui:     1.5;
  --lh-body:   1.65;
  --lh-prose:  1.7;

  /* spacing — token number is px / 4 */
  --space-1:   0.25rem;   /*  4 */
  --space-2:   0.5rem;    /*  8 */
  --space-3:   0.75rem;   /* 12 */
  --space-4:   1rem;      /* 16 */
  --space-5:   1.25rem;   /* 20 */
  --space-6:   1.5rem;    /* 24 */
  --space-8:   2rem;      /* 32 */
  --space-11:  2.75rem;   /* 44 — minimum touch target */
  --space-14:  3.5rem;    /* 56 — between experience entries */
  --space-18:  4.5rem;    /* 72 */

  /* radii */
  --radius-sm: 4px;
  --radius-md: 6px;

  /* layout */
  --container:      800px;
  --gutter:         20px;
  --header-h:       56px;
  --scroll-offset:  72px;
  --measure-prose:  58ch;
  --measure-hero:   52ch;

  /* section rhythm — the gap above a heading is exactly 3x the gap below it,
     at every viewport width, because both derive from one variable */
  --gap-section: clamp(3.5rem, 7vw, 4.5rem);    /* 56 → 72 */
  --gap-heading: calc(var(--gap-section) / 3);  /* 18.67 → 24 */
}

@supports (color: oklch(0.78 0.12 300)) {
  :root { --accent: oklch(0.78 0.12 300); }
}

/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: 400;
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, ul, ol, dl, dd { margin: 0; }
ul { padding: 0; }

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

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

::selection {
  /* Selected text takes --text rather than keeping its own colour: --muted on
     this ground measures 3.45:1 and fails 1.4.3, --text measures 7.87:1.
     The hex is the sRGB value of the oklch below, for engines without oklch. */
  background: #503975;
  background: oklch(0.4 0.1 300);
  color: var(--text);
}

/* anchor targets clear the sticky header */
#top, #projects, #contact { scroll-margin-top: var(--scroll-offset); }

/* --------------------------------------------------------------------------
   4. Skip link — first focusable element, off-screen until focused
   -------------------------------------------------------------------------- */

.skip-link {
  position: absolute;
  top: var(--space-2);
  left: var(--gutter);
  z-index: 20;
  transform: translateY(-250%);
  display: inline-flex;
  align-items: center;
  min-height: var(--space-11);
  padding: 0 var(--space-4);
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: var(--lh-ui);
}
.skip-link:focus { transform: translateY(0); }
.skip-link:hover { text-decoration: none; }

/* --------------------------------------------------------------------------
   5. Sticky header
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  /* Alpha 0.94, not lower: nav text sits over whatever scrolls beneath, and
     the worst case beneath is --text #e8e8ec. At 0.94 the composite is
     rgb(29,29,33) — the header name clears 13.75:1 and the nav links 8.06:1.
     See the Step 3b report for the full calculation. */
  background: rgba(16, 16, 20, 0.94);
  /* Prefixed first: Safari shipped the unprefixed property only in v18, and
     -webkit-backdrop-filter does not accept custom properties, so the radius
     is a literal in both declarations and must stay one. */
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--rule);
}

.site-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--header-h);
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.site-name {
  display: inline-flex;
  align-items: center;
  min-height: var(--space-11);
  color: var(--text);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-ui);
}
.site-name:hover { color: var(--accent); text-decoration: none; }

.site-nav-list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1);
  list-style: none;
}
.site-nav-list a {
  display: inline-flex;
  align-items: center;
  min-height: var(--space-11);
  padding: 0 var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
}

/* --------------------------------------------------------------------------
   6. Content column
   -------------------------------------------------------------------------- */

main {
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 var(--gutter);
}
main:focus { outline: none; }

/* --------------------------------------------------------------------------
   7. Section rhythm and the numbered index
   -------------------------------------------------------------------------- */

main > section { padding-top: var(--gap-section); }

section > h2 {
  margin: 0 0 var(--gap-heading);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: var(--lh-snug);
}

/* Mandatory, not cosmetic: padding-top does not stop the last child's bottom
   margin collapsing through the section boundary and adding to the next
   section's gap, which would make the ratio 3.85:1 and vary per section. */
section > :last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   8. Hero
   -------------------------------------------------------------------------- */

.hero { padding: clamp(4rem, 10vw, 7.5rem) 0 var(--space-8); }

.hero h1 {
  font-size: clamp(2.125rem, 6vw, var(--fs-display));
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: -0.02em;
}

.hero-title {
  margin-top: var(--space-4);
  font-size: clamp(var(--fs-md), 3vw, var(--fs-lg));
  font-weight: 500;
  line-height: var(--lh-snug);
}

/* Both positioning lines are Sans: "9 years building test frameworks and CI
   infrastructure." is prose, and mono never leaks into prose. */
.hero-line-1,
.hero-line-2 {
  max-width: var(--measure-hero);
  font-size: var(--fs-base);
  font-weight: 400;
  line-height: var(--lh-prose);
}
.hero-line-1 { margin-top: var(--space-5); }

.hero-location {
  margin-top: var(--space-3);
  color: var(--muted);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-8);
  list-style: none;
}

/* --------------------------------------------------------------------------
   9. Buttons — four equal navigation targets, no primary variant. An accent
   border read as a pressed state, because --accent was carrying primary,
   focus and hover at once. It now means links and focus only, which leaves
   the focus ring as the sole accent on a focused button.
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  min-height: var(--space-11);
  padding: 0 var(--space-5);
  border: 1px solid var(--rule-ui);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
  transition: color 120ms linear, border-color 120ms linear;
}
.btn:hover {
  color: var(--accent);
  border-color: var(--accent);
  text-decoration: none;
}

/* --------------------------------------------------------------------------
   10. Metric blocks — three numbers inside an experience entry, attributed by
   the employer name above them. Values sit on one baseline whatever their
   phrase does: each item is a top-packed column, so a phrase that wraps grows
   downward and never moves its own value or its neighbours'.
   Step 4b stacks these by changing grid-template-columns and nothing else.
   -------------------------------------------------------------------------- */

.job-metrics {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: var(--space-6);
  margin-top: var(--space-4);
  list-style: none;
}
.job-metrics li { display: flex; flex-direction: column; }

.metric-value {
  font-family: var(--font-mono);
  font-size: var(--fs-md);
  font-weight: 400;
  line-height: var(--lh-snug);
}
.metric-phrase {
  margin-top: var(--space-1);
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
}

/* --------------------------------------------------------------------------
   11. Summary
   -------------------------------------------------------------------------- */

.lead {
  max-width: var(--measure-prose);
  font-size: clamp(var(--fs-base), 2.5vw, var(--fs-md));
  font-weight: 400;
  line-height: var(--lh-prose);
}

/* --------------------------------------------------------------------------
   12. Skills
   -------------------------------------------------------------------------- */

.skills {
  display: grid;
  grid-template-columns: minmax(110px, 160px) 1fr;
  gap: var(--space-5) var(--space-6);
}
.skills dt {
  /* centres the label on the first line of its taller 1.8 definition */
  padding-top: calc((1.8 - var(--lh-ui)) * var(--fs-sm) / 2);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-ui);
}
.skills dd {
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: 1.8;
}

/* --------------------------------------------------------------------------
   13. Experience
   -------------------------------------------------------------------------- */

/* One hairline down the section, with a marker where each entry starts. The
   gap between entries is wider than any gap inside one, so the boundary is
   legible, but stays below the section gap so entries never read as sections. */
.timeline {
  display: grid;
  gap: var(--space-14);
  padding-left: var(--space-8);
  border-left: 1px solid var(--rule);
}

.job { position: relative; }

/* Drawn as a pseudo-element, so it is not in the DOM, not in the string dump
   and not announced. --rule-ui is the border colour already used on buttons
   and the card — never --accent, which stays links and the focus ring. */
.job::before {
  content: "";
  position: absolute;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--rule-ui);
  /* the 1px rule sits just outside the padding box; centre the marker on it */
  left: calc(-1 * (var(--space-8) + 0.5px) - 4px);
  /* centre on the first line of the employer heading */
  top: calc((var(--lh-snug) * var(--fs-base) - 8px) / 2);
}

.job h3,
.edu h3 {
  font-size: var(--fs-base);
  font-weight: 600;
  line-height: var(--lh-snug);
}
.job-city { color: var(--muted); font-weight: 400; }

.job-meta {
  margin-top: var(--space-2);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
}

.job-points {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding-left: var(--space-5);
}
.job-points li {
  max-width: var(--measure-prose);
  font-size: var(--fs-base);
  font-weight: 400;
  line-height: var(--lh-body);
}

.timeline-note {
  max-width: var(--measure-prose);
  margin-top: var(--space-8);
  color: var(--muted);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
}

/* --------------------------------------------------------------------------
   14. Selected projects
   -------------------------------------------------------------------------- */

.project {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 430px;              /* the tech line needs 350px inside 24px padding */
  padding: var(--space-6);
  border: 1px solid var(--rule-ui);
  border-radius: var(--radius-md);
}

/* Cards stack, one per row: side by side, two cards would get ~368px each and
   the repository URL would wrap again. 24px between borders equals the card's
   own padding and is twice the 12px between lines inside a card, so the space
   reads as a boundary between two boxes, never as a gap within one. A sibling
   rule rather than a wrapper: any further card needs no new CSS, and the
   last card carries no bottom margin into the section rhythm. */
.project + .project { margin-top: var(--space-6); }

.project-name {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 500;
  line-height: var(--lh-ui);
}
.project-desc {
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: 1.6;
}
.project-tech {
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 400;
  line-height: var(--lh-ui);
}
.project-link {
  align-self: flex-start;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
  overflow-wrap: anywhere;       /* the URL is longer than the card */
}

/* --------------------------------------------------------------------------
   15. Education, certifications, languages
   -------------------------------------------------------------------------- */

.education { display: grid; gap: var(--space-6); }

.edu-meta {
  max-width: var(--measure-prose);
  margin-top: var(--space-2);
  color: var(--muted);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
}

.cert,
.languages {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
}

/* --------------------------------------------------------------------------
   16. Contact
   -------------------------------------------------------------------------- */

#contact { padding-bottom: var(--space-18); }

/* Same two-column pairing as Skills. Labels match the skills dt; values are
   Sans, and the links inside them are Mono because an address and a profile
   name are technical content while "Vienna, Austria" is not. */
.contact {
  display: grid;
  grid-template-columns: minmax(110px, 160px) 1fr;
  gap: var(--space-5) var(--space-6);
}
.contact dt {
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-ui);
}
.contact dd {
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: var(--lh-ui);
}
/* A value built out of links is technical content, so the whole value is Mono:
   the bare "·" between LinkedIn and GitHub is a text node in the dd and would
   otherwise inherit Sans while the links either side are Mono. Location and
   Status hold prose and stay Sans. The rule on the links themselves is kept
   rather than folded into this one — without :has() they would lose the Mono. */
.contact a { font-family: var(--font-mono); }
.contact dd:has(a) { font-family: var(--font-mono); }

/* --------------------------------------------------------------------------
   17. Back to top — the last thing inside main, outside every section, so it
   cannot enter the 3:1 heading rhythm or the gap between sections. It is a
   link and is styled as one: no border, no background, no button padding.
   #contact carries 72px of padding below its last row, which is a full
   section gap, so the link needs no margin of its own to stand apart from
   the Status row; the same 72px below it closes the page.
   -------------------------------------------------------------------------- */

.to-top {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  line-height: var(--lh-ui);
  padding-bottom: var(--space-18);
}

/* --------------------------------------------------------------------------
   18. Anchor highlight — the single exception to "no animations" in SPEC §3.
   A section arrived at from a nav link announces itself by colouring its own
   heading, then fading back. Colour is the only animated property: no
   background, no border, no underline, no transform, and the section body is
   untouched. --highlight clears 4.5:1 on --bg, so the transient state is
   readable throughout. The reduced-motion variant is the absence of the rule,
   not an override.
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  #projects:target > h2,
  #contact:target > h2 {
    animation: anchor-highlight 2s step-end 1;
  }

  /* A blink, not a fade: two dark pulses, every phase 0.5s, switching hard.
     step-end is what makes it hard — with any interpolating timing function
     the browser ramps between the stops and the result reads as a pulse
     rather than a switch. animation-delay is still not used; the first
     half-second is a keyframe stop at --text, so it stays one animation.
       0    - 0.5s  --text        the reader is still watching the scroll
       0.5  - 1.0s  --highlight   first pulse
       1.0  - 1.5s  --text
       1.5  - 2.0s  --highlight   second pulse
       2.0s         --text, and the animation ends                          */
  @keyframes anchor-highlight {
    0%   { color: var(--text); }
    25%  { color: var(--highlight); }
    50%  { color: var(--text); }
    75%  { color: var(--highlight); }
    100% { color: var(--text); }
  }
}

/* --------------------------------------------------------------------------
   19. Responsive. Media queries only — every desktop rule above is untouched.
   Widths are derived from measured intrinsic text, not from device names.
   -------------------------------------------------------------------------- */

/* Below 768 the card takes the column, and everything becomes a touch target.
   The standalone links were 18px tall, which fails SPEC §8's 44px floor. */
@media (max-width: 768px) {
  .project { max-width: none; }

  /* Touch targets that do not inflate the line box. A 44px min-height on these
     links pushed the contact list's Email and Profiles values 22.5px below
     their labels against 11px for the plain-text rows, so the pairs stopped
     reading as pairs. The link keeps its natural text height and an absolutely
     positioned pseudo-element carries the hit area instead: out of flow, so it
     changes no spacing, and empty, so nothing announces it. */
  .cert a,
  .contact dd a,
  .project-link,
  .to-top a { position: relative; }

  .cert a::after,
  .contact dd a::after,
  .project-link::after,
  .to-top a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: calc(50% - var(--space-11) / 2);
    min-width: var(--space-11);
    height: var(--space-11);
  }

  /* Two 44px bands on consecutive rows need 44px between the rows' centres.
     A 21px value line plus the 20px row gap gives 41px, so the Profiles band
     took 3.1px off the bottom of the Email one and Email measured 40.90px at
     768. A 24px row gap puts the centres 45px apart and both bands stand
     clear. Below 600 the list is one column and the labels already separate
     them, which is why the measurement passed at 375 and failed at 768. */
  .contact { row-gap: var(--space-6); }

  /* The <wbr>s in the card link put break opportunities at the slashes;
     .nowrap stops the browser preferring the hyphen inside the repository
     name, which is a later opportunity and splits the name. */
  .project-link .nowrap { white-space: nowrap; }

  /* CV is the one label narrower than the 44px floor, as button and as nav link */
  .btn { min-width: var(--space-11); justify-content: center; }
  .site-nav-list a { min-width: var(--space-11); justify-content: center; }
}

/* Three metric columns need 3 x 171.28px — the widest value, "130+ E2E tests" —
   plus two 24px gaps: 561.84px of column, so 602px of viewport. Two columns
   below that; all three blocks switch together so the section stays uniform. */
@media (max-width: 640px) {
  .job-metrics {
    grid-template-columns: repeat(2, 1fr);
    row-gap: var(--space-5);
  }
}

/* The name plus the five nav links measure 552.87px and the column is 560px at
   600px of viewport. Below that the nav is dropped rather than wrapped: wrapped
   it needs three rows and a header over 130px tall, which on a 568px screen is
   a quarter of the viewport. The same five targets are hero buttons at the top
   of the page. The name stays, still linking to #top. */
@media (max-width: 600px) {
  .site-nav-list { display: none; }

  .skills,
  .contact {
    grid-template-columns: 1fr;
    row-gap: var(--space-2);
  }
  .skills dt,
  .contact dt { padding-top: var(--space-3); }
  .skills dt:first-of-type,
  .contact dt:first-of-type { padding-top: 0; }
  .contact dd a { overflow-wrap: anywhere; }
}

/* Two columns need 2 x 171.28 + 24 = 366.56px of column, so 407px of viewport.
   One column below that. The buttons lose half their side padding here too:
   at full padding five of them land one per row at 320, at reduced padding
   they land 2-2-1. */
@media (max-width: 480px) {
  .job-metrics { grid-template-columns: 1fr; }
  .btn { padding: 0 var(--space-3); }
}

/* The counterpart to the no-preference wrapper above: no smooth scrolling and
   no button transition. Those are the only two motions in the file. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .btn { transition: none; }
}

