/* jeremyaf.com: one font pairing, evergreen on warm white, no decoration for its own sake.
   Palette locked 2026-08-04 (evergreen): ground #fafaf7, ink #1e352b, accent #2c6e4f. */

:root {
  --paper: #fafaf7;
  --ink: #1e352b;
  --ink-soft: #4a544c;
  --accent: #2c6e4f;
  --rule: #dfe4dc;
  --paper-bright: #ffffff;
  /* Section alternate. Derived by lightening the locked Rule #dfe4dc, so it
     stays inside the evergreen system instead of importing a new hue. */
  --band: #e9ede7;

  /* One stack per family. Sixteen rules used a truncated sans stack that
     silently dropped Segoe UI, Helvetica and Arial. */
  --serif: "Source Serif 4", Georgia, "Times New Roman", serif;
  --sans: "Source Sans 3", -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

/* Clears the sticky nav when an in-page anchor is followed. */
:target { scroll-margin-top: 5.5rem; }
[id] { scroll-margin-top: 5.5rem; }

/* Focus. Every link on this site has text-decoration:none, so without this a
   keyboard user has no idea where they are. Added 2026-08-04. */
:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

.photo-cta :focus-visible,
header.pill :focus-visible { outline-color: #eef2ec; }

/* Skip link: clipped until focused. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--ink);
  color: #f2f5f0;
  font-family: var(--sans);
  font-size: 0.85rem;
  padding: 0.75rem 1.25rem;
  text-decoration: none;
  border-radius: 0 0 0.4rem 0;
}

.skip:focus { left: 0; }

/* 106.25% of a 16px default is 17px, but it scales with the user's setting. */
html { font-size: 106.25%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  line-height: 1.6;
}

/* One column for the whole site: 70rem container, text held to 46rem inside it.
   Every page uses this, so pages do not change shape as you click between them. */
main {
  max-width: 70rem;
  margin: 0 auto;
  padding: 0 1.75rem;
}

/* Every page uses the same 70rem frame and text fills it. Fish rejected the
   narrow reading column three times: filling the browser wins over the
   45-75ch guideline here. Body size is raised to keep the longer lines
   comfortable. */
body:not(.home) main > * { max-width: none; }

/* Lines now run the full frame, so they get a larger size and more leading to
   stay comfortable. This is the tradeoff for filling the browser. */
body:not(.home) main > p {
  font-size: 1.2rem;
  line-height: 1.7;
}

.prose > p { max-width: 46rem; }

nav {
  font-family: var(--sans);
  font-size: 0.9rem;
  letter-spacing: 0.01em;
}

/* Both navs are flex with their own gap, so the legacy margin-right double-spaced
   the links AND left 1.1rem of dead space after the last one, which made the pill
   sit visibly off-centre. Gap owns the spacing now. */
nav a {
  color: var(--ink-soft);
  text-decoration: none;
  display: inline-block;
  padding: 0.15rem 0;
}

nav a:hover { color: var(--ink); }

nav a[aria-current="page"] {
  color: var(--ink);
  border-bottom: 2px solid var(--accent);
}

/* Type */
/* One page title treatment: serif, light, letterspaced. Matches the hero name
   and the resume name so all three pages open the same way. */
/* ROLE 1, Display. Positive tracking on a large light serif reads as a
   wordmark; display type wants zero or slightly negative. */
h1, .name, .rs-name {
  font-family: var(--serif);
  font-size: clamp(2.1rem, 3.6vw, 2.9rem);
  font-weight: 400;
  letter-spacing: -0.005em;
  line-height: 1.1;
}

h1 { margin: 1.75rem 0 0.9rem; }

h2 { margin: 1.9rem 0 0.5rem; }

p { margin: 0 0 1.1rem; }

a { color: var(--accent); }

hr {
  border: none;
  border-top: 1px solid var(--rule);
  margin: 2.5rem 0;
}

/* Images: real photos only */
img.photo {
  width: 100%;
  max-width: 22rem;
  height: auto;
  display: block;
  margin: 2.5rem 0 0;
}

figcaption {
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--ink-soft);
  margin-top: 0.5rem;
}

figure { margin: 2.5rem 0 0; }

/* Core values. One-word name, a short subhead, then the explanation. The
   paragraphs sit inside .value so they lose the `main > p` size rule; it is
   restated here. */
/* Four columns on wide screens, two at tablet, one on phones. */
.values-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2.5rem 2rem;
  margin: 2rem 0 2.5rem;
}

.value { margin: 0; }

/* Line-art marks. No image files, so nothing to 404 and nothing to load. */
.value-mark {
  width: 2.4rem;
  height: 2.4rem;
  display: block;
  margin: 0 0 0.85rem;
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.value h3 {
  font-family: var(--serif);
  font-size: 1.35rem;
  font-weight: 400;
  letter-spacing: -0.005em;
  line-height: 1.2;
  margin: 0;
}

.value-sub {
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0.3rem 0 0.6rem;
}

.value > p:not(.value-sub) {
  font-size: 1rem;
  line-height: 1.6;
  margin: 0;
}

/* Assessment table. Real table markup so a screen reader announces the row and
   column, with the rows stacking into labelled blocks on narrow screens. */
.assess {
  width: 100%;
  border-collapse: collapse;
  margin: 1.5rem 0 1.75rem;
  font-size: 1rem;
}

.assess thead th {
  font-family: var(--sans);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-align: left;
  padding: 0 1.25rem 0.6rem 0;
  border-bottom: 2px solid var(--ink);
}

.assess tbody th,
.assess tbody td {
  vertical-align: top;
  padding: 1.1rem 1.25rem 1.1rem 0;
  border-bottom: 1px solid var(--rule);
  text-align: left;
}

.assess tbody th {
  font-family: var(--serif);
  font-size: 1.15rem;
  font-weight: 600;
  width: 22%;
}

.assess tbody td:nth-of-type(1) {
  font-weight: 600;
  width: 30%;
}

.assess tbody td:nth-of-type(2) {
  color: var(--ink-soft);
  line-height: 1.55;
}

/* Sub-line inside the first column (the "also called OCEAN" note). */
.assess-alt {
  display: block;
  margin-top: 0.3rem;
  font-family: var(--sans);
  font-size: 0.8rem;
  font-weight: 400;
  font-style: italic;
  color: var(--ink-soft);
}

/* The assessment name is the link. It carries an underline so it is not
   distinguished by colour alone, which the rest of the site's links avoid by
   sitting in obvious link positions. */
.assess tbody th a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--rule);
  text-underline-offset: 0.25em;
}

.assess tbody th a:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}

@media (max-width: 1000px) {
  .values-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 760px) {
  .values-grid { grid-template-columns: 1fr; gap: 2rem; }
  .assess thead { position: absolute; left: -9999px; }
  .assess tbody th,
  .assess tbody td { display: block; width: auto; padding: 0; border: none; }
  .assess tbody tr {
    display: block;
    padding: 1.1rem 0;
    border-bottom: 1px solid var(--rule);
  }
  .assess tbody td { margin-top: 0.4rem; }
}

/* Ask AI, demoted from a closing section to a footnote. It was the last thing
   on both the home page and About, so every visit ended on a gadget. */
.ask-ai-note {
  max-width: 70rem;
  margin: 0 auto;
  padding: 2.5rem 1.75rem 0;
  font-family: var(--sans);
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

.ask-ai-note a { font-weight: 600; }

/* Booking overlay. Pure CSS via :target, deliberately: a JS popup fell through to
   its fallback link and opened a new tab, which is the one thing Fish ruled out.
   `display:none` until :target also keeps the cal.com iframe from loading on page
   views where nobody opened it. The frame takes the whole viewport because a plain
   iframe cannot resize to its content and cal.com's page grows at each step. */
.cal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 60;
  padding: 0.75rem;
  background: rgba(30, 53, 43, 0.55);
}

.cal-overlay:target { display: flex; align-items: stretch; justify-content: center; }

.cal-frame {
  position: relative;
  width: min(78rem, 100%);
  height: 100%;
  background: var(--paper-bright);
  border-radius: 0.8rem;
  overflow: hidden;
  box-shadow: 0 1.5rem 3rem rgba(30, 53, 43, 0.28);
}

.cal-frame iframe { display: block; width: 100%; height: 100%; border: 0; }

.cal-close {
  position: absolute;
  top: 0.6rem;
  right: 0.8rem;
  z-index: 2;
  font-family: var(--sans);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--ink);
  background: var(--paper-bright);
  border: 1px solid var(--rule);
  border-radius: 999px;
  padding: 0.15rem 0.55rem 0.3rem;
  text-decoration: none;
}

.cal-close:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

/* Footer: name left, links right, hairline, then the copyright line centred.
   Same markup on every page. */
footer.site {
  max-width: none;
  margin-top: 0;
  padding: 0 0 1.75rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* The other pages need a rule between content and footer. Home closes on the
   dark CTA, so it gets breathing room instead of a rule: without this the icons
   sat directly against the green band. */
/* No hairline above the footer (Fish, 2026-08-05: not necessary). Whitespace
   does the separating, so the pages need a little more of it and home needs
   less, because it already closes on the dark CTA band. */
body:not(.home) footer.site { margin-top: 2rem; }

body.home footer.site { margin-top: 0; }

.footer-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 1rem 2rem;
  padding: 3.25rem 1.75rem 2rem;
}

/* `.footer-top` also carries `.wide`, and `.wide` is DEFINED LATER with an equal
   specificity `padding: 0 1.75rem` shorthand, so it won and flattened the vertical
   padding: the footer icons sat directly on the border rule. A shorthand alone did
   not fix it (the comment that used to sit here claimed otherwise and was wrong).
   `footer.site` raises specificity to (0,2,1) so order stops mattering. */
footer.site .footer-top { padding: 1.75rem 1.75rem 1.5rem; }

/* Home closes on the dark CTA, so it does not need the full run-up. */
body.home footer.site .footer-top { padding: 1.75rem 1.75rem 2rem; }

/* ============================================= */
/* Home: one-page layout (index.html only)       */
/* ============================================= */

body.home main { max-width: none; padding: 0; }

/* The mission strip used to close the home page and provide this gap.
   Removed 2026-08-04, so the footer takes the normal spacing again. */

.wide {
  max-width: 70rem;
  margin: 0 auto;
  padding: 0 1.75rem;
}

/* Prose blocks share the wide container so headings line up left with the cards,
   while the text itself stays at a readable measure. */
.prose {
  max-width: 70rem;
  margin: 0 auto;
  padding: 0 1.75rem;
}

.prose > *, .prose > .ask-ai { max-width: 46rem; }

/* Pill nav */
header.pill {
  max-width: none;
  display: flex;
  justify-content: center;
  padding: 1rem 1rem;
  /* Was static, so jumping to #projects or #help stranded the reader with no
     visible navigation and no way back up. */
  position: sticky;
  top: 0;
  z-index: 50;
}

/* The pill is var(--accent), the same value as the See my resume button (Fish,
   2026-08-05). Flat rather than a gradient, so the two objects match exactly.
   Dropping the gradient also raised contrast: the old light stop (#357a59) put
   link text at 4.80:1, flat --accent puts it at 5.67:1. The shadow still earns
   its place because the closing CTA is --ink and the sticky bar crosses it. */
header.pill nav {
  background: var(--accent);
  border-radius: 999px;
  padding: 0.65rem 1.6rem;
  /* Five layers, cheap and all GPU-composited. Top inset = lit edge, bottom inset =
     the pill's own thickness, then contact + mid + ambient shadows so it floats
     rather than sitting flat on the paper. Kept flat (no gradient) so the pill is
     still exactly var(--accent) and matches a.btn-primary. */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.30),
    inset 0 -1px 0 rgba(0, 0, 0, 0.20),
    0 1px 2px rgba(30, 53, 43, 0.22),
    0 6px 14px rgba(30, 53, 43, 0.26),
    0 16px 34px rgba(30, 53, 43, 0.18);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
}

header.pill nav a {
  color: #f4f8f4;
  margin: 0;
  padding: 0;
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* #8fb39d was tuned for the ink pill and measures under 2:1 on this one. */
header.pill nav a:hover { color: #ffffff; }

/* Current page needs a mark, not just a colour: hover used to be the same
   value, so hovering anything looked like the current page. */
header.pill nav a[aria-current="page"] {
  color: #ffffff;
  border-bottom: 2px solid #cfe3d6;
}

/* Hero */
.hero {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  align-items: center;
  gap: 2rem;
  padding-top: 2rem;
  padding-bottom: 0;
}

/* The hero row is as tall as the portrait, so any leftover under Explore is
   grid slack, not padding. Keep the first section tight against it. */
#more.section { padding-top: 1.25rem; }

/* No photos in site/images/ yet. A missing image still reserves its box and
   leaves a dead column, so the slots are hidden until real files land.
   Drop the file in, delete the no-photo class, done. */
.hero.no-photo .portrait { display: none; }

/* The hero keeps two columns with or without a photograph: the facts card
   holds the right side until images/portrait.jpg lands, then swaps out. */
.hero:not(.no-photo) .hero-card { display: none; }

.hero-card {
  background: var(--paper-bright);
  border: 1px solid var(--rule);
  border-radius: 0.6rem;
  padding: 1.5rem 1.6rem 1.25rem;
  font-family: var(--sans);
}

.hero-card-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 1rem;
}

.hero-card dl { margin: 0; }

.hero-card dl > div {
  display: grid;
  grid-template-columns: 6.5rem minmax(0, 1fr);
  gap: 0.75rem;
  padding: 0.6rem 0;
  border-top: 1px solid var(--rule);
}

.hero-card dl > div:first-child { border-top: none; padding-top: 0; }

.hero-card dt {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding-top: 0.15rem;
}

.hero-card dd {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.4;
  color: var(--ink);
}

figure.no-photo { display: none; }
/* A section whose only content is a hidden photo collapses to bare padding. */
.section:has(> .wide > figure.no-photo:only-child) { display: none; }

.hero h1 { margin: 0; }

.name { display: block; }

.reader-line {
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--ink-soft);
  margin-top: 0.9rem;
  max-width: 30rem;
}

.tagline {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-top: 1.6rem;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--accent);
  max-width: 34rem;
}

.tagline::before, .tagline::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--rule);
  min-width: 1.5rem;
}

.tagline .bar { opacity: 0.45; }

.hero .lead {
  font-size: 1.15rem;
  line-height: 1.55;
  margin-top: 1.4rem;
  max-width: 34rem;
}

.hero .lead a { color: var(--accent); font-weight: 600; }

/* One definition shared by all four footers, so the sets cannot drift. */
.socials { display: flex; gap: 0.7rem; }

/* Outlined, not filled: as solid ink squares these were the heaviest objects
   in the hero and pulled the eye past the actual buttons. */
.socials a {
  width: 2.4rem;
  height: 2.4rem;
  /* Flex items shrink by default; these must stay square at any width. */
  flex: 0 0 2.4rem;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: 0.55rem;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.18s ease, border-color 0.18s ease;
}

.socials a:hover { background: var(--accent); border-color: var(--accent); }

.socials svg { width: 1.15rem; height: 1.15rem; fill: var(--ink-soft); }

.socials a:hover svg { fill: #f2f5f0; }

.portrait { display: flex; justify-content: center; }

.portrait img {
  width: 100%;
  max-width: 310px;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: 0.6rem;
}

/* Sections */
.section { padding: 2.75rem 0; }

/* SITE-WIDE SECTION DEVICE (locked 2026-08-04, Fish's call).
   Every section opens the same way: an uppercase eyebrow label, and on selling
   surfaces a serif headline under it whose second line is italic. The eyebrow
   is the shared element; the resume and About use it alone, home pairs it with
   a headline. The Connect panel and Ask AI keep their own h2 on purpose: they
   are components, not page sections. */
/* ROLE 4b, Section label. Every h2 on the site resolves here, including the
   ones inside .ask-ai and the Connect pane, which previously fell through to a
   third undesigned treatment on the same page. */
.eyebrow,
.rs-section > h2,
body:not(.home) main > h2 {
  font-family: var(--sans);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 1rem;
  padding-bottom: 0.4rem;
  border-bottom: 1px solid var(--rule);
}

/* Eyebrow on home sits above a headline, so no rule under it. */
/* A section label, not a header. At 1rem/600 uppercase with tracking it read
   LOUDER than the 1.7rem serif headline under it, which inverted the hierarchy.
   Smaller and lighter, so the headline is unambiguously the header. */
.eyebrow {
  border-bottom: none;
  padding-bottom: 0;
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--accent);
  margin-bottom: 0.5rem;
}

/* Section headline: serif, second line italic. */
.headline {
  font-family: var(--serif);
  font-size: clamp(1.7rem, 3.2vw, 2.4rem);
  font-weight: 400;
  line-height: 1.15;
  letter-spacing: 0.01em;
  margin: 0 0 1.25rem;
}

.headline em {
  font-style: italic;
  color: var(--accent);
}

/* Section background variants */
.section.alt { background: var(--band); padding: 3.5rem 0; }

/* Proof stats */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.5rem;
  margin-top: 1.25rem;
}

.stat-num {
  font-family: var(--serif);
  font-size: 2.4rem;
  line-height: 1;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--accent);
  margin: 0 0 0.35rem;
}

.stat-label {
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--ink-soft);
  margin: 0;
  max-width: 24rem;
}

/* Numbered cards: one card per section is inverted so the eye lands. */

/* Nav CTA button */
/* Outlined, not filled. It was the only saturated object in the nav, which
   read as a funnel entrance on a site whose copy says the opposite. */
header.pill nav a.nav-cta {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    0 1px 3px rgba(0, 0, 0, 0.28);
  transition: background 0.14s ease, box-shadow 0.14s ease, transform 0.14s ease;
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 999px;
  padding: 0.3rem 0.9rem;
  color: #f4f8f4;
}

/* #3d8a64 measured 4.19:1 against white, under the 4.5 AA floor. #245c42 is 7.82. */
header.pill nav a.nav-cta:hover { background: rgba(255, 255, 255, 0.16); color: #ffffff; }

/* Photo break and full-bleed photo CTA. Both hidden until real images exist. */
.photo-break { margin: 0; }

.photo-break img {
  width: 100%;
  max-width: 46rem;
  border-radius: 0.6rem;
  display: block;
}

.photo-break figcaption {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1rem;
  color: var(--ink-soft);
  margin-top: 0.75rem;
}

/* Closing CTA. Solid evergreen today. To run it over a photo, drop the file at
   images/cta.jpg and uncomment the background-image line below. */
.photo-cta {
  position: relative;
  /* Stays --ink, DELIBERATELY, and darker than the nav and buttons (Fish, 2026-08-05:
     tried --accent for consistency and he preferred the dark band). It is a full-width
     section, not a control, so it does not need to match the button token. Reverting
     also restores the muted greens below to ~7:1; on --accent they fell to ~3.3:1. */
  background: var(--ink) center/cover no-repeat;
  /* background-image: linear-gradient(rgba(30,53,43,0.72), rgba(30,53,43,0.72)), url("../images/cta.jpg"); */
  color: #eef2ec;
  padding: 2.75rem 0;
  text-align: center;
}

.photo-cta .eyebrow { color: #a9c6b4; }

.photo-cta .headline {
  margin-left: auto;
  margin-right: auto;
  max-width: 48rem;
  font-size: clamp(1.6rem, 3.2vw, 2.4rem);
  margin-bottom: 0.9rem;
}

.photo-cta .headline em { color: #a9c6b4; }

/* Was losing to a.btn-primary on source order, so the closing CTA button
   rendered identically to the hero buttons. */
a.btn-primary.btn-lg {
  font-size: 1rem;
  padding: 1.05rem 2.4rem;
}

.cta-note {
  font-family: var(--serif);
  font-style: italic;
  font-size: 0.9rem;
  color: #b9c7bc;
  margin: 0.9rem 0 0;
}

.cta-note a { color: #eef2ec; }

/* Cards */
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
  gap: 1.1rem;
  margin-top: 1.1rem;
}

/* Pull quote. With no photographs this is the only mid-page break, so it is
   sized to actually stop the eye. */
.section.pull { padding: 3.25rem 0; }

.pull-quote {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1.5rem, 3.2vw, 2.1rem);
  line-height: 1.3;
  color: var(--accent);
  max-width: 34rem;
  margin: 0;
}

/* Cards fill with the evergreen accent on hover and lift a touch. */
.card {
  background: var(--paper-bright);
  border: 1px solid var(--rule);
  border-radius: 0.6rem;
  padding: 1.25rem 1.25rem 0.75rem;
  transition: border-color 0.18s ease;
}

.card:hover {
  border-color: var(--accent);
}

.card h3 {
  font-size: 1.15rem;
  font-weight: 600;
  margin: 0 0 0.5rem;
  color: var(--accent);
}

.card p { font-size: 0.9rem; line-height: 1.5; color: var(--ink-soft); }

/* Body copy inside a wide section stays at the reading measure. */
.section .wide > p { max-width: 46rem; }

/* Hero buttons */
.hero-actions {
  display: flex;
  flex-wrap: wrap;
  /* without this the .hero-links divider stretches past the buttons */
  align-items: center;
  gap: 0.7rem;
  margin-top: 1.5rem;
}

/* LinkedIn and email ride INSIDE the button row rather than sitting as a separate
   block below it. The earlier version was two bordered squares under the buttons
   and read as an orphaned strip; Fish had them removed. Bare glyphs on the same
   baseline as the buttons, behind a hairline, keep the row as one object. */
.hero-links {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  margin-left: 0.4rem;
  padding-left: 1.1rem;
  border-left: 1px solid var(--rule);
  align-self: stretch;
  min-height: 1.6rem;
}

.hero-links a {
  display: inline-flex;
  color: var(--ink-soft);
  /* 44px target around a 18px glyph, without changing the visual size */
  padding: 0.7rem;
  margin: -0.7rem;
  transition: color 0.14s ease;
}

.hero-links a:hover { color: var(--accent); }

.hero-links svg { width: 1.1rem; height: 1.1rem; display: block; fill: currentColor; }

/* Raised, not flat. Three layered shadows do the work: a 1px inset highlight
   along the top edge reads as a lit surface, a tight contact shadow sits the
   button on the page, and a soft ambient shadow gives it height. Hover lifts,
   active presses. */
a.btn-primary, a.btn-secondary, a.rs-download {
  display: inline-block;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.75rem 1.4rem;
  border-radius: 999px;
  text-decoration: none;
  transform: translateY(0);
  transition: transform 0.14s ease, box-shadow 0.14s ease, background 0.14s ease;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    0 1px 2px rgba(30, 53, 43, 0.20),
    0 4px 10px rgba(30, 53, 43, 0.14);
}

a.btn-primary:hover, a.btn-secondary:hover, a.rs-download:hover {
  transform: translateY(-2px);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.26),
    0 2px 4px rgba(30, 53, 43, 0.22),
    0 10px 20px rgba(30, 53, 43, 0.18);
}

a.btn-primary:active, a.btn-secondary:active, a.rs-download:active {
  transform: translateY(1px);
  box-shadow:
    inset 0 2px 4px rgba(30, 53, 43, 0.28),
    0 1px 1px rgba(30, 53, 43, 0.16);
}

a.btn-primary {
  background: var(--accent);
  color: #f2f5f0;
  border: 1px solid var(--accent);
}

a.btn-primary:hover { background: #245c42; }

a.btn-secondary {
  background: var(--paper-bright);
  color: var(--ink);
  border: 1px solid var(--ink);
}

a.btn-secondary:hover { background: var(--ink); color: var(--paper); }

.photo-cta a.btn-primary {
  background: #eef2ec;
  color: var(--ink);
  border-color: #eef2ec;
}

/* Reduced motion still gets the button, it just does not fade or glide. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* Keep the depth, drop the movement. */
  a.btn-primary, a.btn-secondary, a.rs-download,
  a.btn-primary:hover, a.btn-secondary:hover, a.rs-download:hover,
  a.btn-primary:active, a.btn-secondary:active, a.rs-download:active {
    transform: none;
    transition: background 0.14s ease;
  }
}

/* Resume contents rail. Puts the right side of the frame to work instead of
   leaving it empty, and lets a recruiter jump straight to a section. Sticky,
   so it follows down the page. Hidden below 900px, where it would just push
   the document into a narrow column. */
.rs-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 9rem;
  gap: 2.25rem;
  align-items: start;
}

.rs-body { min-width: 0; }

.rs-toc {
  position: sticky;
  top: 2rem;
  font-family: var(--sans);
  border-left: 1px solid var(--rule);
  padding-left: 1rem;
  /* Recedes while you read, comes back when you reach for it. */
  /* Resting alpha solved against WCAG AA, not eyeballed: the ink at 0.67
     composites to 4.57:1 on the ground. */
  opacity: 0.67;
  transition: opacity 0.25s ease;
}

.rs-toc:hover,
.rs-toc:focus-within { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .rs-toc { transition: none; }
}

.rs-toc-label {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 0.75rem;
}

.rs-toc a {
  display: block;
  font-size: 0.82rem;
  line-height: 1.3;
  color: var(--ink);
  text-decoration: none;
  padding: 0.35rem 0;
}

.rs-toc a:hover { color: var(--accent); }

@media (max-width: 900px) {
  .rs-layout { grid-template-columns: 1fr; gap: 0; }
  .rs-toc { display: none; }
}

/* Resume download. The .pdf does not exist yet, so this 404s until Fish drops
   the file into site/. */
/* LinkedIn's green #OpenToWork frame, translated to a quiet badge. Signals
   availability without a sentence that reads as needing work. */

.rs-actions {
  margin: 1.1rem 0 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
}

.otw {
  font-family: var(--sans);
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--accent);
}

a.rs-download {
  display: inline-block;
  background: var(--ink);
  color: #f2f5f0;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.7rem 1.5rem;
  border-radius: 999px;
  text-decoration: none;
}

a.rs-download:hover { background: var(--accent); }

@media (max-width: 760px) {
  }

/* ============================================= */
/* Resume page (resume.html)                     */
/* ============================================= */

/* Resume text sits at the same measure as every other page. */

/* Single-line metadata: label, role, contact and employer lines are not
   running prose, so no reading measure applies. Capping them only forces a
   wrap partway through a phrase. */
/* `body:not(.home) main > p` is (0,1,3) and beats a bare `.rs-label` at (0,1,0),
   so these three lost their font-size to the 1.2rem body rule and rendered ~40%
   oversized at the top of the resume, on screen and in the PDF. The max-width
   half of this fix was here already; the font-size half was missed. */
body.resume main > .rs-contact,
body.resume main > .rs-role,
body.resume main > .rs-label { max-width: none; }

body.resume main > .rs-label { font-size: 0.85rem; }
body.resume main > .rs-role { font-size: 0.85rem; }
body.resume main > .rs-contact { font-size: 1rem; }

/* Skills read as two columns on wide screens, which cuts a lot of scroll */
.rs-skills-grid {
  columns: 2;
  column-gap: 2.25rem;
}

.rs-skills-grid .rs-skill { break-inside: avoid; }

@media (max-width: 760px) {
  .rs-skills-grid { columns: 1; }
}

.rs-label {
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 1.75rem 0 0.4rem;
}

.rs-name { margin: 0 0 0.6rem; }

.rs-role {
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 0.9rem;
}

.rs-dot { opacity: 0.45; }

.rs-contact {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.1rem;
  font-family: var(--sans);
  font-size: 1rem;
  color: var(--ink-soft);
  padding-bottom: 1.1rem;
  border-bottom: 2px solid var(--ink);
  margin-bottom: 1.5rem;
}

.rs-contact a { color: var(--ink); }

.rs-section { margin-top: 2.25rem; }

/* Section heading treatment lives in the site-wide rule above. */

.rs-summary { font-size: 1.25rem; margin: 0; }

.rs-skill {
  font-size: 1.1rem;
  margin: 0 0 0.55rem;
  color: var(--ink-soft);
}

.rs-skill strong { color: var(--ink); }

.rs-job { margin-bottom: 1.5rem; }

.rs-job:last-child { margin-bottom: 0; }

.rs-job-head {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.3rem 1rem;
}

.rs-job h3 {
  font-size: 1.15rem;
  font-weight: 600;
  margin: 0;
}

.rs-when {
  font-family: var(--sans);
  font-size: 0.85rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  white-space: nowrap;
}

.rs-meta {
  font-style: italic;
  font-size: 0.9rem;
  color: var(--ink-soft);
  margin: 0.15rem 0 0;
}

.rs-job ul {
  margin: 0.5rem 0 0;
  padding-left: 1.15rem;
}

.rs-job li {
  font-size: 1.05rem;
  line-height: 1.55;
  margin-bottom: 0.4rem;
}

.rs-job li::marker { color: var(--accent); }

.rs-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: baseline;
  gap: 0.2rem 1rem;
  font-size: 1.15rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--rule);
}

.rs-row .rs-when { margin-left: auto; }

.rs-row:last-child { border-bottom: none; }

/* Print: the page IS the fallback PDF */

@media (max-width: 760px) {
  html { font-size: 100%; }

  .hero { grid-template-columns: 1fr; text-align: center; }
  .hero-card { text-align: left; order: 1; }
  .portrait { order: -1; }
  .portrait img { max-width: 240px; }
  .tagline { white-space: normal; justify-content: center; margin-left: auto; margin-right: auto; }
  .hero .lead, .reader-line { margin-left: auto; margin-right: auto; }
  .socials, .hero-actions { justify-content: center; }
  
  /* Line breaks tuned for a desktop measure turn into four-line stacks. */
  .headline br, .pull-quote br { display: none; }

  /* Six uppercase items with wide tracking wrapped to about three rows. */
  header.pill { padding: 1rem 0.75rem 0; }
  header.pill nav { gap: 0.75rem 1rem; padding: 0.6rem 1.1rem; }
  header.pill nav a { font-size: 0.78rem; letter-spacing: 0.09em; }
  header.pill nav a.nav-cta { padding: 0.3rem 0.8rem; }

  /* Keep the button clear of the closing CTA's own button. */
  
  .cards { grid-template-columns: 1fr; }
  .section, .section.alt, .section.pull { padding: 2.25rem 0; }
}

/* Print. The resume page IS the PDF, so this block has to hold up. */
@page {
  size: letter;
  margin: 0.5in 0.55in;
}

@media print {
  /* 13px overflowed onto a third page carrying two lines. 12.5px fit with zero
     slack, so 12px, which leaves room for the resume to grow a little. */
  html { font-size: 12px; }
  body { background: #fff; color: #000; }

  /* .rs-toc is a sticky screen navigation aid. It was printing, and landed as a
     stray CONTENTS block on the last page of the PDF. Hidden at <=1000px already,
     but the letter page is ~710px so that query never governed the print box. */
  header.pill, .cal-overlay, .ask-ai-note, .skip,
  footer.site, .rs-actions, .rs-toc { display: none; }
  a.btn-primary, a.btn-secondary, a.rs-download { box-shadow: none; transform: none; }

  body.resume main { max-width: none; padding: 0; }
  .rs-layout { display: block; }

  /* Everything black, including the bits the old block missed. */
  .rs-name, .rs-role, .rs-section > h2, .rs-contact, .rs-contact a,
  .rs-summary, .rs-skill, .rs-meta, .rs-when, .rs-label { color: #000; }

  .rs-job li::marker { color: #000; }

  /* Multicol fragmenting across a page break is the least reliable thing in
     print CSS. One column on paper. */
  .rs-skills-grid { columns: 1; break-inside: avoid; }

  /* Rules at #dfe4dc are invisible on paper. */
  .rs-section > h2 { border-bottom-color: #000; }
  .rs-contact { border-bottom-color: #000; }
  .rs-row { border-bottom-color: #bbb; }

  /* No orphaned headings, no split jobs. */
  .rs-section > h2, .rs-job-head { break-after: avoid; page-break-after: avoid; }
  .rs-job, .rs-row { break-inside: avoid; page-break-inside: avoid; }

  /* Screen sizes were raised to fill a 70rem frame. Paper is 7.4in, so the
     resume gets its own body sizes here, all inside 8-12pt. */
  .rs-job li, .rs-summary { font-size: 1rem; }
  .rs-row { font-size: 0.95rem; }
  .rs-skill { font-size: 0.95rem; }
  .rs-job h3 { font-size: 1.05rem; }

  /* Tighten to hold two pages. */
  .rs-section { margin-top: 1rem; }
  .rs-job { margin-bottom: 0.9rem; }
  .rs-job li { line-height: 1.4; margin-bottom: 0.2rem; }
  .rs-job ul { margin-top: 0.35rem; }
  body.resume main > .rs-label,
  body.resume main > .rs-role,
  .rs-label, .rs-when, .rs-role { font-size: 0.85rem; }
  .rs-section > h2 { font-size: 1rem; }
  .rs-label { margin-top: 0; }
}

