/* ─── Portfolio template — bold, minimal, creative-showcase ───
   Black/white minimalism with one bold accent. Custom display
   (Inter Tight) + monospace accents.
   ----------------------------------------------------------------- */

@import url("https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;700;800;900&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap");

:root {
  /* newsite-shell D11: bind the two sc-components tokens the
     library deliberately leaves literal to this template's own
     palette, so a dropped-in component doesn't announce itself
     as foreign (brewery blue on every site). One identical
     fallback chain across all 7 templates -- covers both naming
     schemes in use (--accent-hover vs local-services' own
     --accent-dark). */
  --sc-bar: var(--accent);
  --sc-foot-bg: var(--bg-deep, var(--accent-hover, var(--accent-dark, var(--accent))));
  --bg:        #fafaf7;
  --bg-alt:    #f0f0eb;
  --bg-deep:   #0a0a0a;
  --fg:        #0a0a0a;
  --fg-soft:   #555555;
  /* Darkened from #999999, same neutral hue -- 4.80:1 against
     --bg-alt / 5.49:1 against #ffffff (2026-09-14, newsite-shell
     --fg-fade contrast fix). Nothing in this template's own CSS reads
     --fg-fade today (grepped whole tree) -- this is a dead-token
     correction, not a render fix: the value ships anyway so a future
     rule that binds it doesn't inherit the same defect QA measured on
     classes/community/shop's real usages. */
  --fg-fade:   #696969;
  --fg-on-dark: #fafaf7;
  /* Darkened from #ff3b30 (newsite-quality-audit F1, 2026-09-26):
     this template's own unguarded a:hover rule, .hero h1 .accent and
     .cta:hover etc. (all color: var(--accent)) read 3.39:1 on --bg
     and 3.10:1 on --bg-alt -- well below AA, and unreachable by the
     override-path contrast guards in parallel_build.py, which only
     ever run when a build's own palette overrides --accent. 20%
     darker clears 4.5:1 on both light grounds -- the least push, not
     a hue change. --accent-on-dark is pinned to the ORIGINAL value as
     its own literal (same shape as classes'/community's static
     --accent-on-dark) so .section.dark .section-head .eyebrow /
     .stat .num -- painted on --bg-deep, a genuinely different ground
     -- keep the contrast they already had (5.58:1); the darkened
     --accent alone would have dropped that pairing to 3.77:1, the
     exact dual-ground trap _accent_text_overrides's own docstring
     warns about. NOTE: keep every comment in this root block free of
     literal curly-brace characters -- token_resolve.py's root-block
     regex is non-greedy and stops at the first one it meets, silently
     truncating every declaration parsed after it (an earlier draft of
     this very comment quoted a CSS rule verbatim with its braces and
     broke this file's own contrast tests, 2026-09-26). */
  --accent:    #cc2f26;
  --accent-on-dark: #ff3b30;
  --accent-hover: #d93229;
  --border:    #e0e0db;

  --font-display: "Inter Tight", -apple-system, system-ui, sans-serif;
  --font-body:    "Inter", -apple-system, system-ui, sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, monospace;

  --type-xs:   0.75rem;
  --type-sm:   0.9375rem;
  --type-base: 1.0625rem;
  --type-lg:   1.25rem;
  --type-xl:   1.5rem;
  --type-2xl:  2rem;
  --type-3xl:  3rem;
  --type-4xl:  clamp(2.5rem, 6vw, 4.5rem);
  --type-display: clamp(3.5rem, 12vw, 9rem);

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --space-20: 5rem;
  --space-24: 6rem;
  --space-32: 8rem;

  --ease:      cubic-bezier(0.22, 1, 0.36, 1);
  --dur:       320ms;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--type-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a  { color: var(--fg); transition: color var(--dur) var(--ease); }
a:hover { color: var(--accent); }
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1.0;
  letter-spacing: -0.04em;
  margin: 0;
}

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 10;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--space-6) var(--space-8);
  mix-blend-mode: difference;
  color: var(--fg-on-dark);
}
.nav a { color: inherit; text-decoration: none; }
.brand {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--type-base);
  letter-spacing: -0.02em;
}
.nav-links { display: flex; gap: var(--space-6); font-size: var(--type-sm); font-weight: 500; }
.nav-links a:hover { color: var(--accent); }

.hero {
  min-height: 100vh;
  padding: var(--space-32) var(--space-8) var(--space-16);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  position: relative;
}
.hero-inner { max-width: 1280px; margin: 0 auto; width: 100%; }
.hero .eyebrow {
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin-bottom: var(--space-6);
  display: flex;
  gap: var(--space-4);
  align-items: center;
}
.hero .eyebrow::before {
  content: "";
  display: inline-block;
  width: 32px;
  height: 1px;
  background: var(--accent);
}
.hero h1 {
  font-size: var(--type-display);
  margin-bottom: var(--space-8);
  font-weight: 900;
  letter-spacing: -0.06em;
}
.hero h1 .accent { color: var(--accent); font-style: italic; font-weight: 500; }
.hero .lede {
  font-size: clamp(1.125rem, 1.6vw, 1.5rem);
  color: var(--fg-soft);
  max-width: 720px;
  line-height: 1.4;
}
.hero-ctas { margin-top: var(--space-8); display: flex; gap: var(--space-4); flex-wrap: wrap; }
.btn {
  display: inline-block; background: var(--accent); color: var(--fg-on-dark);
  padding: 13px 26px; border-radius: 999px; text-decoration: none;
  font-weight: 700; font-size: var(--type-sm);
  transition: background var(--dur) var(--ease), transform var(--dur) var(--ease);
}
/* --accent-on-dark (#ff3b30) is for TEXT on --bg-deep (5.58:1 there),
   not a hover shade on --bg -- using it here was 3.39:1 (OW181). */
.btn:hover { background: var(--accent-hover, var(--accent)); color: var(--fg-on-dark); transform: translateY(-1px); }
.btn-ghost {
  background: transparent; color: var(--fg);
  box-shadow: inset 0 0 0 2px var(--fg);
}
.btn-ghost:hover { color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); background: transparent; }
.hero .scroll-cue {
  position: absolute;
  bottom: var(--space-8);
  right: var(--space-8);
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  color: var(--fg-soft);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  animation: bounce 2s ease infinite;
}

.section {
  padding: var(--space-24) var(--space-8);
}
.section.alt   { background: var(--bg-alt); }
.section.dark  {
  background: var(--bg-deep); color: var(--fg-on-dark);
  /* Finding 4b, 2026-09-14 -- see templates/community/htdocs/style.css
     for why. */
  --heading-on-dark: var(--fg-on-dark);
  --body-on-dark: var(--fg-on-dark);
}
.section.dark h2 { color: var(--fg-on-dark); }
/* Finding 4, real-build follow-up (2026-09-14, QA build 133,
   footballteam2): .section-head .eyebrow / .stat .num read the raw
   --accent inside .section.dark -- 2.01:1 on the honours page's
   trophy-year numbers, the biggest type on the page.
   _token_overrides already emits --accent-on-dark for portfolio
   whenever it declares --bg-deep, covering an OVERRIDDEN palette.
   Portfolio's own DEFAULT used to clear this pairing at #ff3b30 on
   #0a0a0a (5.58:1) with no static --accent-on-dark literal in :root.
   newsite-quality-audit F1 (2026-09-26) added one anyway, in the
   SAME value (#ff3b30) -- not for this dark-ground pairing, which
   still passed on its own, but because F1 darkened the base --accent
   for an unrelated LIGHT-ground default-palette fix, and the fallback
   below would otherwise have inherited that darker value and dropped
   this pairing to ~3.77:1. See --accent-on-dark's own :root comment. */
.section.dark .section-head .eyebrow { color: var(--accent-on-dark, var(--accent)); }
.section.dark .stat .num { color: var(--accent-on-dark, var(--accent)); }
.container { max-width: 1280px; margin: 0 auto; }
.container-narrow { max-width: 880px; margin: 0 auto; }

.section-head {
  margin-bottom: var(--space-16);
}
.section-head .eyebrow {
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--space-4);
}
.section-head h2 {
  font-size: var(--type-4xl);
  margin-bottom: var(--space-4);
}
.section-head p {
  color: var(--fg-soft);
  font-size: var(--type-lg);
  max-width: 720px;
  margin: 0;
}
/* Finding, real-build follow-up (2026-09-14, QA build "footballteam4"):
   `.section-head p` has no band-scoped counterpart -- unlike
   community/classes, where an unrelated `.section.dark p` rule
   happens to out-specify it by coincidence (their own fix for a
   different element), portfolio has no such rule at all, so this one
   reads --fg-soft directly on --bg-deep inside `.section.dark`,
   measured 2.71:1 on real build paragraphs (index/legacy/about). Same
   --body-on-dark mechanism as .section.dark h2 above. */
.section.dark .section-head p { color: var(--body-on-dark, var(--fg-on-dark, #ffffff)); }

/* Work grid — variable aspect for visual rhythm */
.work-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-8);
}
.work-item {
  position: relative;
  display: block;
  text-decoration: none;
  color: var(--fg);
  overflow: hidden;
}
.work-item .img {
  aspect-ratio: 4/3;
  background: var(--bg-alt);
  background-size: cover;
  background-position: center;
  transition: transform var(--dur) var(--ease);
}
.work-item:hover .img { transform: scale(1.02); }
.work-item .info {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: var(--space-4);
  gap: var(--space-3);
}
.work-item h3 {
  font-family: var(--font-body);
  font-size: var(--type-lg);
  font-weight: 600;
  letter-spacing: -0.01em;
}
.work-item .meta {
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  color: var(--fg-soft);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.work-item .desc {
  color: var(--fg-soft);
  font-size: var(--type-sm);
  margin-top: var(--space-2);
}
.work-item.col-7 { grid-column: span 7; }
.work-item.col-5 { grid-column: span 5; }
.work-item.col-6 { grid-column: span 6; }
.work-item.col-12 { grid-column: span 12; }
.work-item.col-12 .img { aspect-ratio: 16/7; }

/* Stats / numbers */
.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-12);
  padding: var(--space-12) 0;
}
.stat .num {
  font-family: var(--font-display);
  font-size: clamp(3rem, 8vw, 5.5rem);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--accent);
}
.stat .label {
  font-family: var(--font-mono);
  font-size: var(--type-xs);
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin-top: var(--space-3);
}

/* Skills strip */
.skills {
  font-family: var(--font-mono);
  font-size: var(--type-sm);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
  color: var(--fg-soft);
}
.skills span::before { content: "—"; color: var(--accent); margin-right: var(--space-2); }

/* Contact CTA */
.cta {
  font-size: clamp(2rem, 5vw, 4rem);
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.04em;
  line-height: 1.1;
  color: var(--fg);
  text-decoration: none;
  position: relative;
}
.cta:hover { color: var(--accent); }
.cta::after {
  content: " →";
  display: inline-block;
  transition: transform var(--dur) var(--ease);
}
.cta:hover::after { transform: translateX(8px); }

/* ── Form controls — bare-tag, not scoped to a wrapper class: a worker
   composes its own contact/quote form here with no `.form` convention
   to follow (unlike classes/community), so the wrapper class name is
   not reliable across real builds. ────────────────────────────────── */
label { font-size: var(--type-sm); font-weight: 500; }
input, textarea, select {
  width: 100%; margin-top: var(--space-2);
  padding: var(--space-3) var(--space-4);
  font: inherit; font-size: var(--type-sm); color: var(--fg);
  background: var(--bg); border: 1px solid var(--border);
  outline: none;
  transition: border-color var(--dur) var(--ease);
}
textarea { min-height: 7rem; resize: vertical; }
input:focus, textarea:focus, select:focus { border-color: var(--accent); }
fieldset { border: 0; padding: 0; margin: 0; display: grid; gap: var(--space-4); }
legend { font-size: var(--type-sm); font-weight: 500; padding: 0; margin-bottom: var(--space-2); }

footer {
  border-top: 1px solid var(--border);
  padding: var(--space-12) var(--space-8);
}
footer .footer-grid {
  max-width: 1280px;
  margin: 0 auto;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-6);
}
footer .footer-grid p { margin: 0; color: var(--fg-soft); font-size: var(--type-sm); }
footer .footer-grid a { color: var(--fg); text-decoration: none; }
footer .footer-grid a:hover { color: var(--accent); }

@keyframes bounce {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(6px); }
}

@media (max-width: 880px) {
  .nav { padding: var(--space-4); flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
  /* Wrap, don't hide — no hamburger fallback exists. */
  .nav-links { display: flex; flex-wrap: wrap; order: 3; width: 100%;
               gap: var(--space-2) var(--space-4); font-size: var(--type-xs); }
  .hero { padding: var(--space-24) var(--space-4) var(--space-8); }
  .section { padding: var(--space-16) var(--space-4); }
  .work-item.col-7,
  .work-item.col-5,
  .work-item.col-6,
  .work-item.col-12 { grid-column: span 12; }
  .work-item.col-12 .img { aspect-ratio: 4/3; }
}
@media (max-width: 900px) {
  /* R3-followup, QA 2026-09-20 (site-graphics/g2) + newsite-finish/b2
     item 5 (2026-09-22): `.scroll-cue` is absolutely positioned at the
     bottom of a `100vh` hero, so it is not in normal flow and the
     shared `.hero:has(> .sc-hero-mark)` padding-bottom reservation
     (sc-components.css) cannot move it — that rule only pushes
     normal-flow content, and this cue never was normal-flow. Give the
     cue its OWN reservation, at the SAME breakpoint the mark itself
     uses for this size tier (sc-components.css's `@media
     (max-width: 900px)` block, mark svg up to 5rem, bottom:2%) — the
     original fix only widened this far as 640px, leaving 641–900px
     uncovered; measured at 768px with a real monogram mark and
     Range-rect text boxes (`probe3.py`'s method): the ring sat
     directly on "I'm an independent designer and engineer" (the
     lede), the same defect shape as at 375px. */
  .hero .scroll-cue { bottom: 7.5rem; }
}
@media (max-width: 640px) {
  /* Smaller than the ≤900px tier above because the mark itself shrinks
     at this width (sc-components.css's `@media (max-width: 640px)`
     block, svg up to 3.5rem) — wins by cascade order at ≤640px. */
  .hero .scroll-cue { bottom: 6rem; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
