/* FitCalc — "Coach's notebook", FitActo colour pass.
   Warm paper, a coach's hand. The result is written out as the session you are
   about to do rather than a data dump, and the certificates are pinned to the
   page as scans — the proof is the decoration.
   Design imported from claude.ai/design, direction C ("C Notebook", FitActo colours).
   Source of truth for every value below: FitCalc/design/CONTRACT.md — built to it exactly. */

/* Palette = FitActo brand colours on C's warm paper (CONTRACT.md §0–1):
   navy #262262 ("--ink"), orange #F29A1F ("--accent-bright"), on C's cream paper.
   The dark-brown #241F1A that still appears in the design file's own inline
   styles is a stale fallback — not used anywhere here.

   LIGHT ONLY (owner decision, 2026-08-20) — see FitCalc/CLAUDE.md. The design
   source itself opens light (`state = { dark: false }`); a prefers-color-scheme
   dark variant was never reviewed or approved, so it has been removed rather
   than left to render unreviewed on visitors whose OS is set to dark. The dark
   token values remain documented in design/CONTRACT.md §1 for the record.

   ORANGE IS A FILL, NEVER TEXT. --on-accent is pinned to navy rather than
   following --ink, which would go unreadable on orange (~1.9:1) if this
   palette ever grows a second mode again. */
:root {
  color-scheme: light;
  --paper:      #F7F3E9;
  --surface:    #FFFDF7;
  --surface-2:  #F0EADA;
  --ink:        #262262;
  --ink-2:      #565383;
  --ink-3:      #8A88A6;
  --rule:       #DCD3BF;
  --accent:     #B3660A;
  --accent-bright: #F29A1F;
  --accent-wash: #FCEEDA;
  --link:       var(--accent);
  --warn:       #9A4A1E;
  --warn-wash:  #F6EBDF;
  --on-accent:  #262262;
  --steel:      #B9B0A0;
  --chrome:     #DCDCD6;

  --r-lg: 16px;
  --r:    14px;
  --r-sm: 12px;
  --r-xl: 18px;
  --pill: 999px;

  --shadow-card: 0 26px 60px -42px rgba(60,45,20,.6);
  --shadow-pin:  0 10px 22px -16px rgba(50,38,18,.9);
  --shadow-ink:  0 12px 26px -18px rgba(38,34,98,.7);

  --font-display: Petrona, Georgia, "Times New Roman", serif;
  --font-body: Karla, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --wrap: 45rem;      /* 720px — content column, CONTRACT.md §3 */
  --wrap-wide: 68rem;

  --pad-page: clamp(14px, 3vw, 32px); /* page margin, CONTRACT.md §3 */
  --pad-card: clamp(16px, 3vw, 30px); /* inside a card, CONTRACT.md §3 */
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* The blanket rule above (.01ms, not 0) exists for an old Safari quirk where a
   literal 0s transition sometimes never fired transitionend. It gets every
   animation on the site imperceptibly close to instant. For the "Letterpress
   Settle" motion added below, that is not good enough on its own — this second
   rule takes the specific new elements to genuinely no animation at all, so a
   reduced-motion visitor never sees so much as a flicker of movement. */
@media (prefers-reduced-motion: reduce) {
  .result,
  .ladder-row,
  .plate,
  .card,
  .certpins figure img,
  .faq summary::after,
  .btn,
  .chip span {
    animation: none !important;
    transition: none !important;
  }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 var(--pad-page); }
main { max-width: var(--wrap); margin: 0 auto; padding: 2.4rem var(--pad-page) 4rem; }
main.wide { max-width: var(--wrap-wide); }

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: 1rem; top: 1rem; z-index: 30;
  background: var(--surface); color: var(--ink);
  padding: .7rem 1.1rem; border-radius: var(--pill); border: 1px solid var(--rule);
}

:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; border-radius: 4px; }

/* ---------------------------------------------------------------- header */
.site-head { background: var(--surface-2); border-bottom: 1px solid var(--rule); }
.head-inner {
  max-width: var(--wrap-wide);
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; min-height: 64px;
  padding-top: .7rem; padding-bottom: .7rem;
}
.brand-group { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.brand { display: flex; align-items: center; gap: .7rem; text-decoration: none; color: inherit; }
.brand-name { font-family: var(--font-display); font-size: 20px; font-weight: 700; letter-spacing: -.01em; }
/* "part of FitActo" — a white plate the wordmark sits on regardless of theme,
   the same fixed-white device the certificate pins already use. No coach
   photo here (CONTRACT.md §6): the chip is the only credibility mark. */
.brand-chip {
  display: inline-flex; align-items: center; gap: .4rem;
  background: #fff; border-radius: var(--pill);
  padding: .3rem .7rem .3rem .8rem; text-decoration: none;
}
.brand-chip span { font-size: 11px; font-weight: 600; letter-spacing: .02em; color: #6b6690; }
.brand-chip img { display: block; height: 15px; width: auto; }
.site-nav { display: flex; gap: .5rem; flex-wrap: wrap; }
.site-nav a {
  font-size: 14px; color: var(--ink-2); text-decoration: none;
  padding: .45rem .9rem; border-radius: var(--pill);
}
.site-nav a:hover { color: var(--ink); }
.site-nav a[aria-current="page"] { color: var(--ink); font-weight: 600; background: var(--surface); }

/* ------------------------------------------------------------------ type */
h1 {
  font-family: var(--font-display);
  font-size: clamp(30px, 5.2vw, 46px);
  line-height: 1.08; letter-spacing: -.02em; font-weight: 700;
  margin: 0 0 .5rem; text-wrap: balance;
}
h2 {
  font-family: var(--font-display);
  font-size: 28px;
  line-height: 1.15; letter-spacing: -.015em; font-weight: 700;
  margin: 2rem 0 .6rem; text-wrap: balance;
}
h3 { font-family: var(--font-display); font-size: 1.2rem; line-height: 1.25; font-weight: 700; margin: 1.5rem 0 .45rem; }
p { margin: 0 0 1.05rem; }
ul, ol { margin: 0 0 1.15rem; padding-left: 1.3rem; }
li { margin-bottom: .45rem; }
a { color: var(--link); text-underline-offset: 3px; }
/* Colour alone cannot carry a link here — the accent is a mid-contrast orange
   against pale paper — so the underline is the primary signal and hover
   thickens it rather than relying on a colour shift. */
a:hover { color: var(--link); text-decoration-thickness: 2px; }
strong { font-weight: 700; color: var(--ink); }
em { font-style: italic; }
hr { border: 0; border-top: 1px solid var(--rule); margin: 2.2rem 0; }
code { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: .9em; background: var(--surface-2); padding: .1em .4em; border-radius: 4px; }

.lede { font-size: 19px; line-height: 1.65; color: var(--ink-2); margin-bottom: 1.4rem; max-width: 56ch; text-wrap: pretty; }
main p { max-width: 64ch; color: var(--ink-2); line-height: 1.7; font-size: 18px; }
main li { color: var(--ink-2); }
.meta { font-size: .85rem; color: var(--ink-3); margin: 0 0 1.3rem; }

/* breadcrumb trail — an <ol> for the schema, so the list styling has to be undone */
.crumbs { margin: 0 0 1.4rem; }
.crumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: .1rem .5rem;
  list-style: none; margin: 0; padding: 0; }
.crumbs li { margin: 0; font-size: .82rem; color: var(--ink-3); line-height: 1.4; }
.crumbs li + li::before { content: "›"; margin-right: .5rem; color: var(--rule); }
.crumbs a { color: var(--ink-3); text-decoration: none; border-bottom: 1px solid var(--rule); }
.crumbs a:hover { color: var(--link); border-bottom-color: currentColor; }
.crumbs [aria-current] { color: var(--ink-2); }

/* pill eyebrow — "Strength · Tool 5 of 5" */
.eyebrow {
  display: inline-block; font-size: 12px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-wash);
  border-radius: var(--pill); padding: .38rem .8rem; margin-bottom: 1rem;
}
.microlabel {
  font-size: 12px; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3);
}

/* coach note — italic serif aside (no avatar — CONTRACT.md §6 drops the photo,
   keeps the accent-wash box and the italic voice) */
.coachnote {
  background: var(--accent-wash); border-radius: var(--r);
  padding: .9rem 1.15rem; margin: 0 0 1.9rem;
  box-shadow: var(--shadow-card);
}
.coachnote p {
  font-family: var(--font-display); font-style: italic;
  font-size: 1.06rem; line-height: 1.55; color: var(--ink-2); margin: 0; max-width: none;
}

/* --------------------------------------------------------------- the tool */
.calc {
  background: var(--surface-2); border: 1px solid var(--rule);
  border-radius: var(--r-lg); padding: 1.4rem var(--pad-card) 1.25rem; margin: 0 0 1.9rem;
  box-shadow: var(--shadow-card);
}
.calc > .microlabel { display: block; margin-bottom: 1rem; }

.field-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr)); gap: 16px 18px; }
.field { display: flex; flex-direction: column; gap: .35rem; }
.field.span-2 { grid-column: 1 / -1; }
/* Option labels carry the meaning ("0.25% / week — slow, easiest to hold"), and a
   native select clips rather than wraps. Give every select the full row so the
   selected option stays readable; the max-width stops it sprawling on desktop. */
.field-grid > .field:has(select) { grid-column: 1 / -1; }
.field select { max-width: 30rem; }
.field label, .field .field-name { font-size: 13px; font-weight: 600; color: var(--ink-2); }
.field .hint { font-size: 12.5px; color: var(--ink-3); line-height: 1.45; }

input[type="number"], input[type="time"], select {
  font-family: var(--font-body); font-size: 19px; font-weight: 600;
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule); border-radius: var(--pill);
  padding: 13px 18px; width: 100%;
}
/* The native select arrow is drawn flush against the border box, so on a full
   999px capsule it lands inside the corner curve — reported as "the arrow is
   falling out of the frame". appearance:none drops the native arrow (which
   differs across Windows/macOS/Android anyway) and this SVG redraws it clear
   of the curve, ~18px in from the edge to match the field's own padding.
   Colour is --ink-2 hardcoded to #565383 because a data: URI can't read a CSS
   custom property — keep this in sync if the token ever changes. padding-right
   leaves room for the arrow plus a gap so a long option label never runs
   under it (.field select { max-width: 30rem } still caps the field width). */
select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 2.75rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23565383' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 18px center;
  background-size: 12px 8px;
}

/* chipset — radio pills */
.chipset { display: flex; gap: .5rem; flex-wrap: wrap; }
.chip { position: relative; }
.chip input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.chip span {
  display: inline-block; font-size: 14px; cursor: pointer;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--rule); border-radius: var(--pill);
  padding: 9px 16px;
  transition: background-color 150ms ease-out, border-color 150ms ease-out, color 150ms ease-out;
}
.chip input:checked + span {
  background: var(--ink); color: var(--surface); border-color: var(--ink); font-weight: 600;
}
.chip input:focus-visible + span { outline: 2px solid var(--link); outline-offset: 2px; }

.btn-row { display: flex; align-items: center; gap: 1.1rem; margin-top: 1.25rem; flex-wrap: wrap; }
.btn {
  font-family: var(--font-body); font-size: 15px; font-weight: 700;
  background: var(--accent-bright); color: var(--on-accent);
  border: 0; border-radius: var(--pill); padding: 16px 34px; cursor: pointer;
  transition: filter 150ms ease-out;
}
.btn:hover { filter: brightness(1.08); }
.btn-note { font-size: 13.5px; color: var(--ink-3); line-height: 1.5; max-width: 38ch; }

/* -------------------------------------------------------------- the result */
/* One addressable block — hero + body live inside this single container so a
   future reveal animation has one thing to trigger, not several. */
.result {
  margin: 1.9rem 0; border: 1px solid var(--rule);
  border-radius: var(--r-lg); overflow: hidden; background: var(--surface);
  box-shadow: var(--shadow-ink);
}
.result[hidden] { display: none; }

/* "Letterpress Settle" — the coach stamping a finished answer into place. Content
   is at full opacity in every frame (nothing here animates opacity/display/
   visibility) so a crawler that never runs JS still sees the complete result the
   instant innerHTML is set; only transform and box-shadow move. .is-revealed is
   added by a few lines of JS at each calculator's "show the result" point — see
   src/content/calculators.mjs — never on page load. The "to" value below matches
   .result's own resting box-shadow (--shadow-ink, not --shadow-card) so the
   animation settles into the same shadow the block already has once JS is done,
   instead of snapping to a different one when the animation's fill mode ends. */
.result.is-revealed {
  animation: result-settle 240ms cubic-bezier(.16,1,.3,1);
}
@keyframes result-settle {
  from { transform: translateY(-8px); box-shadow: none; }
  to   { transform: translateY(0);    box-shadow: var(--shadow-ink); }
}

.result-hero { background: var(--ink); color: var(--paper); padding: 1.6rem var(--pad-card) 1.5rem; }
.result-hero .microlabel { color: inherit; opacity: .6; display: block; margin-bottom: .75rem; }
.result-headline { display: flex; align-items: baseline; gap: .9rem; flex-wrap: wrap; }
.result-figure {
  font-family: var(--font-display); font-size: clamp(52px, 9vw, 80px);
  line-height: .85; font-weight: 700; letter-spacing: -.03em;
}
.result-sub { font-size: 1.05rem; opacity: .75; line-height: 1.4; }
.result-note {
  font-family: var(--font-display); font-style: italic;
  font-size: 1.1rem; line-height: 1.55; margin: 1.25rem 0 0;
  opacity: .85; max-width: 54ch; color: inherit;
}
.result-body { padding: 1.5rem var(--pad-card) 1.6rem; }
.result-body .microlabel { display: block; margin-bottom: .85rem; }
.result-body .microlabel + * { margin-top: 0; }
.result-body .microlabel:not(:first-child) { margin-top: 1.6rem; }
.result-foot { font-size: 13px; color: var(--ink-3); margin: .75rem 0 0; }

.statgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 148px), 1fr)); gap: .65rem; }
.stat { background: var(--surface-2); border-radius: var(--r-sm); padding: .85rem 1rem .95rem; }
.stat dt { font-size: 12.5px; color: var(--ink-3); font-weight: 600; margin-bottom: .1rem; }
.stat dd { margin: 0; font-family: var(--font-display); font-size: 29px; font-weight: 700; line-height: 1.2; }
/* Orange cannot tint the number, so the key stat is flagged by an orange rule
   across the top of its card — the same device FitActo uses on its own cards. */
.stat.is-key { background: var(--accent-wash); box-shadow: inset 0 3px 0 var(--accent-bright); }

/* the warm-up ladder — pill rows, work set in accent */
.ladder { display: flex; flex-direction: column; gap: 2px; }
.ladder-row {
  display: flex; align-items: center; gap: 1rem;
  padding: .68rem 1rem; background: var(--surface-2); border-radius: var(--pill);
}
.ladder-n {
  width: 26px; height: 26px; border-radius: 50%; background: var(--accent-bright);
  display: grid; place-items: center; font-size: 12.5px; font-weight: 700;
  color: var(--on-accent); flex: none;
}
.ladder-load { font-size: 1rem; font-weight: 700; min-width: 5rem; }
.ladder-desc { font-size: .95rem; color: var(--ink-2); }
.ladder-row.is-work {
  background: var(--accent-bright); color: var(--on-accent); margin-top: .4rem; padding: .8rem 1rem;
}
.ladder-row.is-work .ladder-n { background: rgba(38,34,98,.16); color: inherit; }
.ladder-row.is-work .ladder-load { font-size: 1.06rem; }
.ladder-row.is-work .ladder-desc { color: inherit; opacity: .9; }
.ladder-row.is-work .ladder-desc strong { color: inherit; }

/* Rows settle in one after another once the parent result reveals — no extra JS,
   the .result.is-revealed trigger above does the work. `backwards` holds each
   row at its `from` state during its delay so nothing flashes at the wrong spot
   first. Longest warm-up ladder in the current calculators is 5 rows (the 1RM
   tool); extend the +35ms pattern if a 6th row is ever added. */
.result.is-revealed .ladder-row {
  animation: ladder-settle 160ms cubic-bezier(.16,1,.3,1) backwards;
}
.result.is-revealed .ladder-row:nth-child(1) { animation-delay: 0ms; }
.result.is-revealed .ladder-row:nth-child(2) { animation-delay: 35ms; }
.result.is-revealed .ladder-row:nth-child(3) { animation-delay: 70ms; }
.result.is-revealed .ladder-row:nth-child(4) { animation-delay: 105ms; }
.result.is-revealed .ladder-row:nth-child(5) { animation-delay: 140ms; }
@keyframes ladder-settle {
  from { transform: translateY(-4px); }
  to   { transform: translateY(0); }
}

/* barbell plate diagram — working-weight calculator only. Height scales with
   plate weight; colours mark the four sizes CONTRACT.md §4.7 defines, the bar
   assumed is a standard 20 kg — stated in the caption next to it, never
   silently. Decorative, so it is hidden from assistive tech; the caption
   underneath carries the same information as text. */
.barbell { display: flex; align-items: center; gap: 3px; overflow-x: auto; padding: .3rem .1rem 1rem; }
.barbell-sleeve { flex: none; width: 30px; height: 12px; border-radius: 3px 0 0 3px; background: var(--steel); }
.plate { flex: none; width: 15px; border-radius: 3px; box-shadow: var(--shadow-pin); }

/* Plates settle onto the bar the same way the ladder rows settle into the list —
   triggered by the same .result.is-revealed, no extra JS. scaleY is used rather
   than clip-path (as first proposed) because it is compositor-only in every
   browser, where clip-path's hardware acceleration is less consistent. Note the
   first child of .barbell is always .barbell-sleeve, not a plate, so the
   nth-child count below is offset by one (a real plate never lands on
   nth-child(1)). Covers up to 7 plates per side, which the current 25/10/2.5/1.25
   plate set cannot realistically exceed for any weight this calculator can
   output; anything beyond that just animates without a stagger. */
.result.is-revealed .plate {
  transform-origin: bottom center;
  animation: plate-settle 140ms cubic-bezier(.16,1,.3,1) backwards;
}
.result.is-revealed .plate:nth-child(2) { animation-delay: 0ms; }
.result.is-revealed .plate:nth-child(3) { animation-delay: 30ms; }
.result.is-revealed .plate:nth-child(4) { animation-delay: 60ms; }
.result.is-revealed .plate:nth-child(5) { animation-delay: 90ms; }
.result.is-revealed .plate:nth-child(6) { animation-delay: 120ms; }
.result.is-revealed .plate:nth-child(7) { animation-delay: 150ms; }
.result.is-revealed .plate:nth-child(8) { animation-delay: 180ms; }
@keyframes plate-settle {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

.note {
  background: var(--accent-wash); border-radius: var(--r);
  padding: .9rem 1.1rem; font-size: .95rem; color: var(--ink-2);
  line-height: 1.6; margin: 1.1rem 0;
}
.note strong { color: var(--ink); }

/* tables — long ones must survive a 375px screen */
.table-scroll {
  overflow-x: auto; margin: 0 0 1.2rem;
  border: 1px solid var(--rule); border-radius: var(--r); background: var(--surface);
}
table { border-collapse: collapse; width: 100%; font-size: .92rem; }
th, td { text-align: left; padding: .6rem .85rem; border-bottom: 1px solid var(--rule); }
thead th {
  background: var(--surface-2); font-size: 11.5px; font-weight: 700;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink-3); white-space: nowrap;
}
tbody tr:last-child th, tbody tr:last-child td { border-bottom: 0; }
tbody td { color: var(--ink-2); }
tbody th { font-weight: 700; color: var(--ink); }
td.num, th.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
tbody tr.is-break td, tbody tr.is-break th { background: var(--accent-wash); color: var(--ink); }
caption { caption-side: bottom; text-align: left; padding: .7rem .85rem; font-size: .8rem; color: var(--ink-3); line-height: 1.5; }

/* ------------------------------------------------------------------- ads */
/* This styling only ever reaches the page once adsense.enabled and clientId
   are set — src/layout.mjs adSlot() emits nothing at all (no container, no
   label, no reserved height) while ads are off, so an empty labelled box
   never ships to a reviewer or a visitor. These rules stay ready for the day
   ads switch on. Sizes from CONTRACT.md §4.9: 336×280 mid-content, 728×90
   divider, both collapsing to a 300×250 box once the row can no longer fit them. */
.adslot { margin: 2.1rem 0; background: var(--surface-2); border-radius: var(--r-lg); padding: .75rem .9rem .9rem; box-shadow: var(--shadow-card); }
.adslot-label { font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); font-weight: 700; margin-bottom: .5rem; }
.adslot-inner { background: var(--surface); border-radius: 10px; display: flex; margin: 0 auto; }
.adslot--rect .adslot-inner { width: 336px; max-width: 100%; height: 280px; }
.adslot--leader .adslot-inner { width: 728px; max-width: 100%; height: 90px; }
@media (max-width: 640px) {
  .adslot--rect .adslot-inner, .adslot--leader .adslot-inner { width: 300px; height: 250px; }
}
.adslot-inner .adsbygoogle { width: 100%; }

/* ------------------------------------------------------------------- faq */
.faq { margin: 1.8rem 0; }
.faq h2 { margin-bottom: .8rem; }
.faq details { background: var(--surface-2); border-radius: var(--r); padding: .25rem 1.15rem; margin-bottom: .5rem; box-shadow: var(--shadow-card); }
.faq summary { cursor: pointer; list-style: none; font-size: 1.05rem; font-weight: 700; padding: .8rem 0; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+"; float: right; font-weight: 400; color: var(--ink-3); font-size: 1.3rem; line-height: 1;
  transition: transform 160ms ease-out;
}
/* CSS `content` strings cannot be interpolated ("+" cannot morph into "–"), so
   the swap itself is instant — what animates is the glyph flipping 180°
   alongside it, which reads as "the mark turns over" rather than a smooth
   morph. Trigger is the native [open] attribute of <details>; no JS involved,
   and the answer text is already in the DOM at page load either way. */
.faq details[open] summary::after { content: "–"; transform: rotate(180deg); }
.faq details p { font-size: 1rem; line-height: 1.65; color: var(--ink-2); margin: 0 0 .9rem; max-width: none; }

/* --------------------------------------------------------------- author */
.authorbox { margin: 2.4rem 0 1.1rem; background: var(--surface-2); border-radius: var(--r-xl); padding: 1.6rem var(--pad-card) 1.5rem; box-shadow: var(--shadow-card); }
.authorbox-kicker { font-size: 11.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); margin-bottom: .35rem; }
.authorbox-name { font-family: var(--font-display); font-size: 1.65rem; font-weight: 700; line-height: 1.2; margin: 0; }
.authorbox-title { font-size: .92rem; color: var(--ink-2); margin: 0 0 .6rem; }
.authorbox-bio { font-size: 1rem; line-height: 1.65; color: var(--ink-2); margin: 0 0 .85rem; max-width: none; }
.credpills { display: flex; flex-wrap: wrap; gap: .38rem; list-style: none; padding: 0; margin: 0; }
.credpills li { margin: 0; }
.credpills span { display: inline-block; font-size: 12.5px; font-weight: 600; background: var(--surface); border-radius: var(--pill); padding: .38rem .75rem; color: var(--ink-2); }

/* certificates pinned to the page — the proof is the decoration. The angle is
   a custom property so a future hover/entrance animation can compose with it
   (e.g. rotate(var(--pin-r)) scale(1.02)) instead of overwriting it. */
.certpins { display: flex; gap: .9rem; margin-top: 1.35rem; align-items: flex-end; flex-wrap: wrap; }
.certpins figure { margin: 0; }
.certpins img {
  width: 150px; height: auto; display: block; border-radius: 6px;
  border: 5px solid var(--surface); box-shadow: var(--shadow-pin); background: #fff;
  transform: rotate(var(--pin-r, 0deg));
  transition: transform 200ms cubic-bezier(.16,1,.3,1);
}
.certpins figure:nth-child(1) img { --pin-r: -1.5deg; }
.certpins figure:nth-child(2) img { --pin-r: 1.2deg; }
.certpins figure:nth-child(3) img { --pin-r: -.8deg; }
.certpins figure:nth-child(4) img { --pin-r: 1.4deg; }
.certpins figure:nth-child(5) img { --pin-r: -1.1deg; }
/* Hover uncurls the pinned angle to about a third of its rest tilt and lifts the
   scan slightly — reads as "pick the certificate up to look closer," not a
   bounce. No matching :focus-visible: the image itself is not the interactive
   element (there is no <a> around it, see src/layout.mjs certPins()) — only the
   "See all N →" link after it is focusable, and that already gets the site's
   default a:hover treatment. */
.certpins figure:hover img {
  transform: rotate(calc(var(--pin-r, 0deg) * .35)) scale(1.04);
}
.certpins figcaption { position: absolute; left: -9999px; }
.certpins a { font-size: .92rem; font-weight: 600; padding-bottom: .5rem; }
.authorbox-date { font-size: 12.5px; color: var(--ink-3); margin: 1rem 0 0; }

/* ---------------------------------------------------------- disclaimer */
.disclaimer {
  margin: 1.1rem 0 0; display: flex; gap: .9rem;
  background: var(--warn-wash); border-radius: var(--r); padding: 1rem 1.15rem;
  border-left: 4px solid var(--warn);
}
.disclaimer h2 {
  font-family: var(--font-body); font-size: 11.5px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--warn);
  white-space: nowrap; margin: .15rem 0 0; flex: none; line-height: 1.4;
}
.disclaimer div { min-width: 0; }
.disclaimer p { font-size: .88rem; line-height: 1.6; color: var(--ink-2); margin: 0 0 .55rem; max-width: none; }
.disclaimer p:last-child { margin-bottom: 0; }

/* ------------------------------------------------------------ home hero */
.hero { margin-bottom: 2rem; }
.hero-greeting { display: flex; gap: 1rem; align-items: center; margin-bottom: 1.15rem; }
.hero-greeting p { font-size: .92rem; color: var(--ink-2); line-height: 1.5; margin: 0; max-width: none; }
.trustline { display: flex; flex-wrap: wrap; gap: .45rem; margin: 0 0 1.6rem; padding: 0; list-style: none; }
.trustline li { margin: 0; }
.trustline span { display: inline-block; font-size: 12.5px; font-weight: 600; background: var(--surface-2); border-radius: var(--pill); padding: .45rem .85rem; color: var(--ink-2); }

/* --------------------------------------------------------- cards / lists */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: .75rem; margin: 1.3rem 0 2rem; padding: 0; list-style: none; }
.cards li { margin: 0; }
.card {
  display: flex; flex-direction: column; gap: .3rem; height: 100%;
  background: var(--surface-2); border-radius: var(--r); padding: 1rem 1.1rem 1.15rem;
  text-decoration: none; color: inherit; box-shadow: var(--shadow-card);
  transition: transform 150ms ease-out, background-color 150ms ease-out;
}
.card:hover { background: var(--accent-wash); }
.card:hover,
.card:focus-visible {
  transform: translateY(-2px);
}
.card .tag { font-size: 11.5px; text-transform: uppercase; letter-spacing: .09em; color: var(--link); font-weight: 700; }
.card strong { font-family: var(--font-display); font-size: 1.2rem; font-weight: 700; line-height: 1.25; color: var(--ink); }
.card span:not(.tag) { font-size: .9rem; color: var(--ink-2); line-height: 1.5; }

.postlist { list-style: none; padding: 0; margin: 1.2rem 0 2rem; display: flex; flex-direction: column; gap: .5rem; }
.postlist li { margin: 0; }
.postlist a { display: block; padding: 1rem 1.15rem; text-decoration: none; color: inherit; background: var(--surface-2); border-radius: var(--r); box-shadow: var(--shadow-card); }
.postlist a:hover { background: var(--accent-wash); }
.postlist strong { display: block; font-family: var(--font-display); font-size: 1.15rem; line-height: 1.3; color: var(--ink); margin-bottom: .2rem; }
.postlist span { font-size: .9rem; color: var(--ink-2); line-height: 1.5; }

/* byline chip on guides */
.byline {
  display: inline-flex; align-items: center; gap: .7rem;
  background: var(--surface-2); border-radius: var(--pill);
  padding: .4rem 1rem; margin: 0 0 1.6rem;
}
.byline span { font-size: 13.5px; color: var(--ink-2); }

.certgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 1.1rem; margin: 1.4rem 0 1.2rem; }
.certgrid figure { margin: 0; }
/* The scans are a mix of landscape and portrait. A fixed box with object-fit
   keeps every certificate the same height so the captions sit on one line
   instead of one cell running twice as tall as its neighbour. */
.certgrid img { width: 100%; height: 270px; object-fit: contain; object-position: center top;
  display: block; border-radius: 6px; border: 5px solid var(--surface);
  box-shadow: var(--shadow-pin); background: #fff; }
.certgrid figcaption { font-size: .8rem; color: var(--ink-3); margin-top: .5rem; line-height: 1.45; }

/* ---------------------------------------------------------------- footer */
/* FitActo ends every page on a full-bleed navy band — the site's most
   recognisable device after the orange. */
.site-foot { background: var(--ink); color: var(--paper); border-top: 0; margin-top: 3rem; }
.site-foot a { color: var(--paper); }
.site-foot .foot-note { color: var(--paper); opacity: .78; }
.foot-inner {
  max-width: var(--wrap-wide); display: flex; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap; padding-top: 1.5rem; padding-bottom: 1.9rem;
}
.foot-brand { font-family: var(--font-display); font-weight: 700; font-size: 1.05rem; margin: 0 0 .25rem; }
.foot-note { font-size: 13.5px; color: var(--ink-2); margin: 0; max-width: 40ch; line-height: 1.6; }
.foot-nav { display: flex; gap: 1.1rem; flex-wrap: wrap; align-items: flex-start; }
.foot-nav a { font-size: 13.5px; color: var(--ink-2); text-decoration: none; }
.foot-nav a:hover { color: var(--ink); }

/* Very narrow phones (320px): tighten table cells so the food table fits without
   a sideways scroll. Page margin already floors at 14px via --pad-page. */
@media (max-width: 360px) {
  th, td { padding: .5rem .5rem; font-size: .86rem; }
}

/* ---------------------------------------------------------------- mobile */
@media (max-width: 640px) {
  body { font-size: 16px; }
  main { padding-top: 1.6rem; }
  .calc { padding: 1.15rem; }
  .disclaimer { flex-direction: column; gap: .5rem; }
  .foot-inner { flex-direction: column; gap: 1.2rem; }

  /* Ladder rows must stay one line — a wrapped pill reads as a broken shape. */
  .ladder-row { gap: .7rem; padding: .6rem .85rem; border-radius: var(--r); }
  .ladder-load { min-width: 4.4rem; }
  .ladder-row.is-work { padding: .7rem .85rem; }

  /* Long result tables: drop the phase column rather than clip a number in half.
     Maintenance weeks stay readable through the row highlight (see the caption). */
  .col-phase { display: none; }
  th, td { padding: .55rem .7rem; }

  /* The author block becomes a single column: a wide scene photo at portrait
     height crops the subject down to nothing, so it goes shorter and wider. */
  .credpills { gap: .3rem; }
  .credpills span { font-size: 12px; padding: .32rem .6rem; }

  /* Certificates: two per row at half width beats three at postage-stamp size —
     at 128px they were unreadable, which defeats the point of showing them. */
  .certpins { gap: .6rem; align-items: flex-start; }
  .certpins figure { flex: 1 1 calc(50% - .3rem); min-width: 0; }
  .certpins img { width: 100%; border-width: 4px; }
  .certpins figure:nth-child(n) img { --pin-r: 0deg; }
  .certpins a { flex: 1 0 100%; padding-top: .2rem; }
}
