/* ==========================================================================
   Joe Myers — Portfolio
   ==========================================================================
   System, in one place:

   - Two content widths, used consistently everywhere: 720px for all text
     and quotes, 1080px for the "recent directions" card grid only.
   - Section rhythm is a single number (--space-section) applied as
     margin-top on every section, no one-off gaps.
   - The fluid values below scale continuously between a 390px viewport
     (the approved mobile design) and a 1440px viewport (the approved
     desktop design), using the standard linear-interpolation clamp()
     formula, so nothing snaps at an arbitrary breakpoint — a laptop at
     100% zoom or a tablet in between gets an interpolated value, not
     whichever fixed design is "closer."
   - Below 390px or above 1440px, values hold at the design's edges.
   ========================================================================== */

:root {
  color-scheme: light dark;

  /* interpolation range */
  --vw-min: 390;
  --vw-max: 1440;

  /* palette (light, the default) — dark values live in the
     prefers-color-scheme block right below. Every color used anywhere
     in this file is a variable defined in one of these two places, so
     dark mode never needs a second override site-wide. */
  --color-bg: #FFFFFF;
  --color-text: #14171F;
  --color-body: #2B2F38;
  --color-muted: #5B6270;
  --color-recess: #F4F4F2;
  --color-border: #E6E8EC;
  --color-accent: #5B3A9B; /* small-scale only: labels, links — never a fill */
  --color-accent-bright: #7C56C9; /* hover state for accent-colored links: lighter/brighter, never darker, so it reads as "lighting up" */
  --color-selection: #E7E2F2;
  --color-quote-mark: rgba(20, 23, 31, 0.05);
  /* card hover surface: white in light mode (cards sit on --color-recess,
     so lifting to bg already reads as "coming forward"); dark mode gets
     its own value below since --color-bg is darker than --color-recess
     there, not lighter, so reusing it would read as sinking, not lifting */
  --color-surface: #FFFFFF;
  --shadow-card: 0 12px 28px rgba(20, 23, 31, 0.12), 0 2px 8px rgba(20, 23, 31, 0.06);

  /* content widths — fixed tokens, not fluid */
  /* 780px measured against the real body copy (Source Sans 3) lands
     right at ~66 characters per line at the 24px large-screen body
     size below — the reading-comfort ceiling — with hero paragraphs
     comfortably under it. */
  --width-narrow: 780px;
  /* ceiling for the wide (card grid) column. Since --space-page-x tops
     out at 96px (at 1440px viewport), the grid keeps growing with the
     viewport past that point too, until the section hits this max-width
     — at 1600px content width, 4 cards + 3 gaps of 48px lands each card
     at ~364px, comfortably inside a 300–400px target, and the section
     stops growing once the viewport reaches ~1792px (1600 + 96*2). */
  --width-wide: 1600px;

  /* fluid tokens: clamp(min, min + (max-min) * (100vw - minVW) / (maxVW-minVW), max) */
  --space-page-x: clamp(24px, calc(24px + 72 * (100vw - 390px) / 1050), 72px);
  --space-section: clamp(72px, calc(72px + 88 * (100vw - 390px) / 1050), 120px);
  --space-quote-y: clamp(64px, calc(64px + 76 * (100vw - 390px) / 1050), 100px);
  --space-footer-y: clamp(24px, calc(24px + 8 * (100vw - 390px) / 1050), 32px);
  --space-card-pad: clamp(16px, calc(16px + 4 * (100vw - 390px) / 1050), 20px);
  /* gap between consecutive paragraphs within a section — scales with
     --font-body (16-24px) so paragraphs keep breathing room at the
     larger large-screen body size instead of a fixed value going
     tight; applied as a flex `gap`, never paragraph margin, so it
     can't stack/double up the way a self-margin would. */
  --space-paragraph: clamp(20px, calc(20px + 8 * (100vw - 390px) / 1050), 28px);
  --nav-gap: clamp(16px, calc(16px + 24 * (100vw - 390px) / 1050), 40px);
  --header-h: clamp(72px, calc(72px + 24 * (100vw - 390px) / 1050), 96px);

  --font-eyebrow: clamp(11px, calc(11px + 2 * (100vw - 390px) / 1050), 13px);
  --font-wordmark: clamp(22px, calc(22px + 8 * (100vw - 390px) / 1050), 30px);
  /* h1, hero-p and body are ~25-30% larger at full desktop size than
     before, for a more comfortable reading experience on large screens
     — mobile sizes are unchanged, only the desktop ceiling moved. */
  --font-h1: clamp(36px, calc(36px + 44 * (100vw - 390px) / 1050), 72px);
  --font-hero-p: clamp(16px, calc(16px + 10 * (100vw - 390px) / 1050), 26px);
  --font-body: clamp(16px, calc(16px + 8 * (100vw - 390px) / 1050), 24px);
  --font-quote: clamp(27px, calc(27px + 17 * (100vw - 390px) / 1050), 44px);
  --font-quote-mark: clamp(140px, calc(140px + 140 * (100vw - 390px) / 1050), 280px);
  --font-card-title: clamp(22px, calc(22px + 3 * (100vw - 390px) / 1050), 25px);
  --font-card-desc: clamp(14px, calc(14px + 1 * (100vw - 390px) / 1050), 15px);
}

/* Automatic dark mode, driven by the OS-level preference only, no
   manual toggle. Structure, spacing, and type scale are unchanged;
   only the palette swaps. --color-accent is lifted lighter than the
   light-mode purple since the original #5B3A9B doesn't have enough
   contrast against a near-black background to stay legible at the
   small sizes it's used at (eyebrows, links). */
@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #101216;
    --color-text: #F3F4F6;
    --color-body: #C8CCD6;
    --color-muted: #8B92A3;
    --color-recess: #191C22;
    --color-border: #2A2E38;
    --color-accent: #9B84DA;
    --color-accent-bright: #B8A3EE;
    --color-selection: #3A2F57;
    --color-quote-mark: rgba(255, 255, 255, 0.05);
    /* --color-recess (#191C22) is already lighter than --color-bg here
       (dark-mode elevation reads as "lighter", the opposite direction
       from light mode) — so the card hover surface needs its own step
       up rather than reusing either existing token. */
    --color-surface: #242835;
    --shadow-card: 0 12px 28px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.3);
  }
}

/* ---------- reset ---------- */

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: 'Source Sans 3', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }

h1, h2, h3, p, blockquote { margin: 0; }

::selection { background: var(--color-selection); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--color-text);
  /* var(--color-bg), not a hardcoded #fff: color-text/color-bg are
     always each theme's max-contrast pair, so this stays correct in
     both themes. A fixed #fff here would go invisible in dark mode,
     where --color-text itself becomes a near-white background. */
  color: var(--color-bg);
  padding: 12px 20px;
  z-index: 10;
}
.skip-link:focus {
  left: var(--space-page-x);
  top: 12px;
}

/* ---------- layout primitives ---------- */

/* .section is applied directly to the <section> element (no separate
   wrapper) — margin:auto + a padding-inclusive max-width centers it and
   insets its edges in one box, since box-sizing:border-box means the
   padding is already part of that max-width. */
.section {
  margin-top: var(--space-section);
  margin-left: auto;
  margin-right: auto;
  padding: 0 var(--space-page-x);
}

.section--narrow {
  max-width: calc(var(--width-narrow) + var(--space-page-x) * 2);
  display: flex;
  flex-direction: column;
  gap: var(--space-paragraph);
}

/* .inset is different: it caps an element that is already sitting inside
   a padded parent (a quote panel, the "recent directions" intro), so it
   gets the plain content width with no extra padding budget added. */
.inset {
  width: 100%;
  max-width: var(--width-narrow);
}

/* .wide-inset is the same idea as .inset, one tier up: it caps the
   recent-directions content (intro + card-grid) inside the full-bleed
   .section--recess band, the way .inset caps quote-inner inside
   .quote-panel. */
.wide-inset {
  width: 100%;
  max-width: var(--width-wide);
  display: flex;
  flex-direction: column;
  gap: 48px;
}

/* full-bleed recess band, same pattern as .quote-panel: recent
   directions gets the same gray alternation down the page, just with
   wide content (.wide-inset) centered inside instead of narrow. */
.section--recess {
  margin-top: var(--space-section);
  width: 100%;
  background: var(--color-recess);
  padding: var(--space-quote-y) var(--space-page-x);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.recent-intro {
  /* .wide-inset is a flex column stretching its children full-width by
     default (so the card-grid fills it) — but the intro text should
     center like the narrow .inset content in quote panels does, not
     hug the left edge of a much wider row of cards. */
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* ---------- header ---------- */

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-h);
  padding: 0 var(--space-page-x);
}

.wordmark {
  /* larger, heavier and tighter-tracked than the eyebrow/nav labels it
     used to share a size with — enough presence to read as a name, not
     another nav item, without shouting */
  font-size: var(--font-wordmark);
  font-weight: 900;
  letter-spacing: 0.06em;
}

.site-nav {
  display: flex;
  gap: var(--nav-gap);
}

.site-nav a {
  font-size: var(--font-eyebrow);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-muted);
  transition: color 0.25s ease;
}

/* light up toward the accent color, don't fade toward transparent:
   opacity here would just make an already-muted color harder to
   read, the opposite of hover feedback */
.site-nav a:hover { color: var(--color-accent); }

/* ---------- typography ---------- */

.eyebrow {
  font-size: var(--font-eyebrow);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-accent);
}

h1 {
  font-family: 'Source Serif 4', serif;
  font-weight: 400;
  font-size: var(--font-h1);
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.hero .body-lg {
  font-size: var(--font-hero-p);
  line-height: 1.65;
  color: var(--color-body);
}

/* plain narrative paragraphs get their own class rather than a blanket
   ".section p" descendant rule — a descendant selector combining a class
   with the element (specificity 0,1,1) would silently outrank single-class
   rules like .eyebrow or .card-desc (0,1,0) wherever they sit inside a
   .section, which is exactly the kind of one-off exception this system
   is trying to avoid. */
.body {
  font-size: var(--font-body);
  line-height: 1.7;
  color: var(--color-body);
}

/* ---------- pull quotes ---------- */

.quote-panel {
  margin-top: var(--space-section);
  width: 100%;
  background: var(--color-recess);
  padding: var(--space-quote-y) var(--space-page-x);
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  overflow: hidden;
}

.quote-mark {
  position: absolute;
  /* -16px at mobile width, -30px at desktop width */
  top: clamp(-30px, calc(-16px - 14 * (100vw - 390px) / 1050), -16px);
  left: var(--space-page-x);
  font-family: 'Source Serif 4', serif;
  font-size: var(--font-quote-mark);
  line-height: 1;
  color: var(--color-quote-mark);
  user-select: none;
}

.quote-inner {
  display: flex;
  flex-direction: column;
  gap: 32px;
  position: relative;
}

.quote-inner blockquote { margin: 0; }

.quote-inner blockquote p {
  font-family: 'Source Serif 4', serif;
  font-style: italic;
  font-weight: 400;
  font-size: var(--font-quote);
  line-height: 1.32;
  color: var(--color-text);
}

.quote-link {
  align-self: flex-start;
  font-size: var(--font-eyebrow);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
  border-bottom: 1px solid currentColor;
  padding-bottom: 1px;
  transition: color 0.25s ease;
}

/* already accent-colored at rest, so "lighting up" means a brighter
   tint of the same purple, not a color swap; the underline (currentColor)
   brightens along with it automatically */
.quote-link:hover { color: var(--color-accent-bright); }

/* ---------- recent directions ---------- */

.card-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(32px, calc(32px + 16 * (100vw - 390px) / 1050), 48px);
}

/* three fixed steps, not an auto-fit grid that keeps adding columns as
   the viewport grows: a single stacked column on mobile, 2 columns
   through tablet and small desktop, then 4 across once there's enough
   width (~1080px content area) for four cards to stay comfortably
   readable rather than just filling space. */
@media (max-width: 640px) {
  .card-grid {
    grid-template-columns: 1fr;
  }
}

@media (min-width: 1200px) {
  .card-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

.card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* the padding is permanent, not hover-triggered (animating padding
     itself would shift the image size and jump the grid around) —
     only the fill/shadow/lift below transition, so the card reads as
     flat and blended into the recess band at rest, then comes forward
     as one connected surface on hover instead of only the title
     underlining while the image and CTA sit inert */
  padding: var(--space-card-pad);
  border-radius: 14px;
  background: transparent;
  box-shadow: none;
  transition: background-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}

.card:hover {
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  transform: translateY(-2px);
}

.card-thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--color-recess);
  border: 1px solid var(--color-border);
  border-radius: 8px;
  overflow: hidden;
}

.card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.card-title {
  font-family: 'Source Serif 4', serif;
  font-weight: 400;
  font-size: var(--font-card-title);
  color: var(--color-text);
}

.card-desc {
  font-size: var(--font-card-desc);
  line-height: 1.55;
  color: var(--color-muted);
}

.card-cta {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-accent);
}

/* ---------- footer ---------- */

.site-footer {
  /* no margin-top here on purpose: the footer always follows the
     recess band directly, and that band already has its own bottom
     padding (--space-quote-y) — an extra section-level margin here
     would stack on top of it and leave an oversized gap before the
     footer's border-top line. */
  border-top: 1px solid var(--color-border);
  padding: var(--space-footer-y) var(--space-page-x);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.copyright {
  font-size: 13px;
  color: var(--color-muted);
}

.footer-links {
  display: flex;
  align-items: center;
  gap: 28px;
  flex-wrap: wrap;
}

.footer-link {
  /* bigger and higher-contrast than the muted 13px it used to share
     with .copyright: this is the site's only real call to action, and
     it was reading as an afterthought this far down the page */
  font-size: 16px;
  color: var(--color-body);
  display: flex;
  align-items: center;
  gap: 10px;
  border-bottom: 1px solid currentColor;
  padding-bottom: 1px;
  transition: color 0.25s ease;
}

.footer-link:hover { color: var(--color-accent); }

/* Below ~480px the footer's mailto address can outgrow the row before
   flex-wrap kicks in gracefully; stack it to match the approved mobile
   layout exactly. */
@media (max-width: 480px) {
  .site-footer {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }
  .copyright { order: 2; }
}

/* Respect reduced-motion / no smooth scroll preference */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
