/* gaff.net: one stylesheet. Tokens and decisions come from docs/BUILD_BRIEF.md section 3. */

/* ---------- Fonts (self-hosted, Latin subset, SIL OFL; licenses alongside) ---------- */

/* Variable file: the opsz axis is what makes the 104px headline match the reference. */
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-weight: 700 800;
  font-display: swap;
  src: url("../fonts/bricolage-grotesque-latin-var.woff2") format("woff2");
}

/* One variable file covers 400, 500 and 600, smaller than three static files. */
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url("../fonts/ibm-plex-sans-latin-var.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-latin-400.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-latin-500.woff2") format("woff2");
}

/* ---------- Tokens ---------- */

:root {
  --ink: #14181a;
  --bg: #f3f4f1;
  --surface: #ffffff;
  --muted: #4f5a58;
  --body-2: #3a4442;
  --line: #c4ced3;
  /* Fill or decoration only: about 2:1 as text on light backgrounds. */
  --accent: #22c55e;
  --accent-hover: #16a34a;
  --accent-text: #15803d;
  --go-blue: #1565a0;
  --go-green: #3a9e2f;
  --placeholder: #e3e8ea;

  /* Dark (contact) section */
  --dark-text: #f3f4f1;
  --dark-muted: #c9d0cd;
  --dark-faint: #a9b3b0;
  --dark-field: #1f2527;
  --dark-border: #4a5452;
  --dark-rule: #2e3537;

  --font-display: "Bricolage Grotesque", system-ui, sans-serif;
  --font-body: "IBM Plex Sans", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;

  --radius-control: 6px;
  --radius-card: 8px;
  --control-h: 48px;

  /* Width of the content itself; .wrap adds the gutters outside it, as the reference does. */
  --content-max: 1120px;
  --gutter: 24px;
  --section-pad: 88px;
  /* The hero runs about a third tighter than other sections (#40). */
  --hero-pad: 60px;
  --display-size: clamp(52px, 8vw, 104px);

  /* Must equal the rendered header height so nav jumps clear it. Fixed, not measured:
     nav links have a fixed min-height and the row never wraps. */
  --header-h: 69px;
}

@media (max-width: 640px) {
  :root {
    --gutter: 16px;
    --section-pad: 56px;
    --hero-pad: 40px;
    --header-h: 61px;
  }
}

/* ---------- Base ---------- */

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

img {
  display: block;
  max-width: 100%;
  height: auto;
}

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

a:hover {
  color: var(--go-blue);
}

h1,
h2,
h3 {
  margin: 0;
}

h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(32px, 6vw, 44px);
  line-height: 1.08;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

h3 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(26px, 4vw, 30px);
  line-height: 1.2;
  letter-spacing: -0.015em;
}

p {
  margin: 0;
}

:focus-visible {
  outline: 2px solid var(--go-blue);
  outline-offset: 3px;
}

/* Components set display (.btn is inline-flex), which would beat the UA's [hidden]. */
[hidden] {
  display: none !important;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- Layout ---------- */

.wrap {
  max-width: calc(var(--content-max) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

section[id] {
  scroll-margin-top: var(--header-h);
}

.section {
  border-top: 1px solid var(--line);
}

.section > .wrap {
  padding-block: var(--section-pad);
}

.section--surface {
  background: var(--surface);
}

.section--dark {
  border-top: 0;
  background: var(--ink);
  color: var(--dark-text);
}

/* The contact section is the one ink-background block. Go-blue hover and focus are
   about 3:1 on ink, so these switch to the accent, which is allowed as text here
   because the background is dark. */
.section--dark a {
  color: var(--dark-text);
}

.section--dark a:hover {
  color: var(--accent);
}

.section--dark :focus-visible {
  outline-color: var(--accent);
}

/* ---------- Skip link ---------- */

.skip-link {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 100;
  padding: 10px 16px;
  background: var(--ink);
  color: var(--bg);
  border-radius: var(--radius-control);
  font-weight: 500;
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: none;
}

.skip-link:hover {
  color: var(--bg);
}

/* ---------- Header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgb(243 244 241 / 0.92);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}

.site-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-block: 12px;
}

/* On index.html the header name is the page's one h1; it keeps the wordmark's look.
   display: flex centers the wordmark in the row: as a block, the h1 kept the body's
   27.2px line box and the wordmark sat on its baseline, 3.6px above the nav links. */
.site-title {
  display: flex;
  flex: none;
  font-size: inherit;
  font-weight: inherit;
  line-height: inherit;
}

.wordmark {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-mono);
  font-size: 18px;
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
}

/* The header mark is a "TG" tab styled like the chapter numbers (#44); the shared tab
   rule is in the Story section. Generated content keeps "TG" out of the h1's text, and
   the empty alt after the slash keeps screen readers from announcing it (the first
   line is the fallback for browsers without alt text on content). */
.wordmark::before {
  content: "TG";
  content: "TG" / "";
  flex: none;
}

.nav-links {
  display: flex;
  gap: 28px;
  min-width: 0;
  /* Room for the focus outline inside the scrolling list, without changing layout. */
  margin: -5px;
  padding: 5px;
  list-style: none;
  font-size: 15px;
  /* Phones scroll the row sideways instead of wrapping into a tall header. */
  overflow-x: auto;
  /* Focus scrolling aligns the link's edge, not its outline; keep the outline in view. */
  scroll-padding-inline: 5px;
  scrollbar-width: none;
}

.nav-links::-webkit-scrollbar {
  display: none;
}

.nav-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  white-space: nowrap;
  text-decoration: none;
}

/* Sized so the name and all five links fit one row at 390px; narrower phones scroll. */
@media (max-width: 640px) {
  .site-nav {
    gap: 12px;
    padding-block: 8px;
  }

  .wordmark {
    font-size: 15px;
  }

  .wordmark::before {
    display: none;
  }

  .nav-links {
    gap: 12px;
    font-size: 14px;
  }
}

/* ---------- Shared components ---------- */

/* Section label: mono text with a 10px green square, never green text. */
.label {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 12px;
  font-family: var(--font-mono);
  font-size: 13px;
  /* Explicit so a heading-as-label doesn't get a synthesized bold (there is no Mono 700). */
  font-weight: 400;
  color: var(--muted);
}

.label::before {
  content: "";
  flex: none;
  width: 10px;
  height: 10px;
  background: var(--accent);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: var(--control-h);
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  font: inherit;
  font-weight: 500;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
}

.btn--ink {
  background: var(--ink);
  color: var(--bg);
}

.btn--ink:hover {
  background: var(--go-blue);
  color: var(--surface);
}

.btn--outline {
  background: transparent;
  border-color: var(--ink);
  color: var(--ink);
}

.btn--outline:hover {
  border-color: var(--go-blue);
  color: var(--go-blue);
}

/* Green buttons always carry ink text (7.8:1), never white. The dark-section selector
   is here because `.section--dark a` would otherwise turn the text light. */
.btn--accent,
.section--dark .btn--accent {
  background: var(--accent);
  color: var(--ink);
}

.btn--accent:hover,
.section--dark .btn--accent:hover {
  background: var(--accent-hover);
  color: var(--ink);
}

.section--dark .btn--outline {
  border-color: var(--dark-border);
  color: var(--dark-text);
}

.section--dark .btn--outline:hover {
  border-color: var(--dark-text);
}

.card {
  padding: 28px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
}

/* ---------- Hero ---------- */

.hero-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 56px;
  /* The display face has about 0.16em of empty space above its capitals, so the top
     padding gives that back: the visible gap above "Less hype." then matches the
     visible gap below the buttons. */
  padding-block: calc(var(--hero-pad) - 0.16 * var(--display-size)) var(--hero-pad);
}

.hero-text {
  flex: 999 1 560px;
  min-width: 0;
}

.display {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: var(--display-size);
  line-height: 1.08;
  letter-spacing: -0.035em;
}

/* Ink on green (7.8:1), on "done." only. inline-block with its own line-height sizes the
   green box independently of the headline's 1.08 leading, leaving a gap below the
   descenders of the line above. */
.highlight {
  display: inline-block;
  padding: 0.02em 0.12em;
  border-radius: 0.08em;
  background: var(--accent);
  color: var(--ink);
  line-height: 1;
}

/* pretty keeps a short line like "automation." from ending up alone (#65). */
.lead {
  max-width: 640px;
  margin-top: 32px;
  font-size: 22px;
  font-weight: 500;
  line-height: 1.45;
  text-wrap: pretty;
}

.hero-support {
  max-width: 620px;
  margin-top: 14px;
  color: var(--body-2);
  text-wrap: pretty;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 40px;
}

/* The backdrop is the page and header background (#62), so the cutout blends into the
   hero. No radius: on an invisible frame it would only notch the shirt's bottom
   corners. */
.hero-photo {
  flex: 1 1 340px;
  max-width: 480px;
  margin: 0;
  overflow: hidden;
  background: var(--bg);
}

/* Phones: a smaller, centered photo so the story starts sooner. */
@media (max-width: 640px) {
  .hero-inner {
    gap: 40px;
  }

  .hero-photo {
    max-width: 280px;
    margin-inline: auto;
  }
}

.hero-photo img {
  width: 100%;
}

/* ---------- Section intro (label + h2) ---------- */

/* No width cap: headings run on one line where they fit, and h2's text-wrap: balance
   evens them out where they don't. */
.section-intro {
  max-width: none;
}

/* ---------- Story ---------- */

/* One column template for every chapter row, so each column starts at the same x in
   every row. */
.chapters {
  --chapter-cols: 200px minmax(0, 1fr) 260px;
  margin: 56px 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--line);
}

@media (max-width: 1000px) {
  .chapters {
    --chapter-cols: 200px minmax(0, 1fr);
  }

  .chapter-aside {
    grid-column: 2;
  }
}

@media (max-width: 640px) {
  .chapters {
    --chapter-cols: minmax(0, 1fr);
  }

  .chapter-aside {
    grid-column: auto;
  }
}

.chapter {
  display: grid;
  grid-template-columns: var(--chapter-cols);
  align-items: start;
  gap: 16px 40px;
  padding: 36px 0;
  border-bottom: 1px solid var(--line);
}

.chapter-meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-family: var(--font-mono);
  font-size: 14px;
}

/* Number tab: ink on green, never green text. One rule for the chapter numbers and the
   header's TG mark, so the two can't drift apart. */
.chapter-num,
.wordmark::before {
  padding: 1px 10px;
  border-radius: 4px;
  background: var(--accent);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.6;
}

.chapter-num {
  align-self: flex-start;
}

.chapter-years {
  color: var(--muted);
}

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

/* pretty avoids a one-word last line ("bank." on its own); browsers without it wrap normally. */
.chapter-body p {
  max-width: 640px;
  margin-top: 10px;
  color: var(--body-2);
  text-wrap: pretty;
}

.chapter-aside {
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--muted);
}

/* Keeps a long company list from breaking before a "·" separator. */
.nowrap {
  white-space: nowrap;
}

/* Chapter 04 is a regular row since #64 (the GO Tech Labs card below is the one
   highlight). The class stays as the hook for its aside, which holds a link rather than
   company names. */
.chapter--now .chapter-aside {
  font-family: var(--font-body);
  font-size: 15px;
}

/* inline-flex drops the space before the arrow span, so the gap puts it back. Text sits
   at the top of the 44px tap target so it lines up with the tab and the h3. */
.chapter--now .chapter-aside a {
  display: inline-flex;
  align-items: flex-start;
  gap: 0.3em;
  min-height: 44px;
}

/* ---------- Now ---------- */

.feature-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  margin-top: 40px;
  padding: 36px;
  border: 1px solid var(--line);
  border-left: 4px solid var(--go-blue);
  border-radius: var(--radius-card);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
}

/* The base a:hover would turn every word in the card blue; the pill carries the hover. */
.feature-card:hover {
  color: var(--ink);
}

.feature-main {
  flex: 999 1 420px;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 24px;
}

/* The real GO Tech Labs logo (#46): decorative here, since the h3 beside it names it.
   Shown as supplied on the white card: never recolored, cropped or flattened. */
.gtl-logo {
  flex: none;
  width: 114px;
  height: 96px;
}

.feature-label {
  margin-bottom: 6px;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--muted);
}

.feature-title {
  font-size: 32px;
}

.feature-line {
  max-width: 560px;
  margin-top: 8px;
  color: var(--body-2);
}

.feature-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--control-h);
  padding: 0 20px;
  border: 1px solid var(--go-blue);
  border-radius: var(--radius-control);
  color: var(--go-blue);
  font-weight: 500;
}

.feature-card:hover .feature-pill {
  background: var(--go-blue);
  color: var(--surface);
}

@media (max-width: 640px) {
  .feature-card {
    padding: 24px;
  }

  /* Beside the text, the logo squeezes "GO Tech Labs" onto two lines at 390px. */
  .feature-main {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }
}

/* Grid, not flex-wrap: four cards in flex-wrap leave a stretched orphan at some widths. */
.ventures {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
}

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

@media (max-width: 560px) {
  .ventures {
    grid-template-columns: 1fr;
  }
}

.venture {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 24px;
  height: 100%;
  min-height: 180px;
  color: var(--ink);
  text-decoration: none;
}

.venture:hover {
  border-color: var(--go-blue);
  color: var(--ink);
}

/* Card titles are h3 for the outline but take the reference's compact look. */
.card-title {
  font-family: var(--font-body);
  font-size: 19px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0;
}

.card-line {
  margin-top: 8px;
  color: var(--muted);
  font-size: 15px;
}

.card-url {
  font-family: var(--font-mono);
  font-size: 13px;
}

/* ---------- Speaking ---------- */

.speaking {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 48px;
}

.speaking-intro {
  flex: 1 1 320px;
  min-width: 0;
}

.speaking-text {
  margin-top: 16px;
  color: var(--muted);
}

.speaking-cta {
  margin-top: 28px;
}

/* A divider list like the story chapters: gray cards on the white section read as
   disabled (#40). A rule above every item, none after the last. */
.talks {
  flex: 999 1 520px;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.talk {
  padding-block: 24px;
  border-top: 1px solid var(--line);
}

/* Even out the longer talk titles when they wrap on phones. */
.talk .card-title {
  text-wrap: balance;
}

.talk .card-line {
  margin-top: 6px;
  color: var(--body-2);
}

/* ---------- Play ---------- */

/* Six items: three across fills two even rows on desktop. */
.play {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  margin: 40px 0 0;
  padding: 0;
  list-style: none;
}

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

@media (max-width: 560px) {
  .play {
    grid-template-columns: 1fr;
  }
}

.play-photo {
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-card);
  background: var(--placeholder);
}

.play-photo img {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: cover;
  color: transparent;
}

/* Until a photo exists the img is broken, and a broken img renders pseudo-elements
   (a loaded one never does). This covers the browser's broken-image icon and alt text
   with the placeholder fill, no JS needed. Verified in Chrome only. */
.play-photo img::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--placeholder);
}

.play-item .card-title {
  margin-top: 14px;
  font-size: 18px;
}

.play-item .card-line {
  margin-top: 4px;
}

/* ---------- Contact ---------- */

.contact {
  display: flex;
  flex-wrap: wrap;
  gap: 56px;
}

/* Less bottom padding than other sections: the footer row follows inside it. */
.section > .wrap.contact {
  padding-bottom: 64px;
}

.contact-intro {
  flex: 1 1 320px;
  min-width: 0;
}

.contact-intro h2 {
  font-size: clamp(36px, 6vw, 48px);
  line-height: 1.05;
}

.contact-text {
  margin-top: 16px;
  color: var(--dark-muted);
}

/* The GO Tech Labs pointer, set apart so it reads as an aside to the intro. */
.contact-note {
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--dark-rule);
  font-size: 15px;
  color: var(--dark-muted);
}

/* Padding grows the inline link's tap area to 44px+; the matching negative margin keeps
   the line box where it was. */
.contact-note a {
  display: inline-block;
  padding-block: 12px;
  margin-block: -12px;
}

/* Two equal blocks (email, LinkedIn). Each block spans three subgrid rows, so the label,
   handle and buttons line up across the two halves whatever each one holds. */
.contact-card {
  flex: 999 1 460px;
  min-width: 0;
  align-self: center;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border: 1px solid var(--dark-border);
  border-radius: var(--radius-card);
  background: var(--dark-field);
}

.contact-block {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;
  justify-items: start;
  row-gap: 20px;
  padding: 32px;
}

.contact-block + .contact-block {
  border-left: 1px solid var(--dark-border);
}

@media (max-width: 760px) {
  .contact-card {
    grid-template-columns: minmax(0, 1fr);
  }

  .contact-block + .contact-block {
    border-top: 1px solid var(--dark-border);
    border-left: 0;
  }
}

@media (max-width: 640px) {
  .contact-block {
    padding: 24px;
  }
}

.contact-label {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--dark-faint);
}

/* Sized so "in/tomgaffney" fits a half-width block. */
.contact-handle {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--font-mono);
  font-size: clamp(22px, 2.2vw, 26px);
  font-weight: 500;
  overflow-wrap: anywhere;
  text-decoration: none;
}

.contact-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 16px;
}

/* Copy sits beside the filled button as a quieter text action, still a real button. */
.text-button {
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: none;
  color: var(--dark-muted);
  font: inherit;
  font-size: 15px;
  font-weight: 500;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.text-button:hover {
  color: var(--accent);
}

/* LinkedIn's white wordmark: shown as published, never tinted or stretched. */
.linkedin-logo {
  display: block;
  width: auto;
  height: 22px;
}

/* ---------- Footer (sits inside the dark contact section) ---------- */

/* One left-aligned line since #35 removed the right-hand text. Sized to the content
   column, with the gutter left outside the box, so the top rule spans exactly the
   content width (with padding-inline the rule ran a gutter past both edges, #67). */
.site-footer {
  width: min(var(--content-max), 100% - 2 * var(--gutter));
  margin-inline: auto;
  padding-block: 24px;
  border-top: 1px solid var(--dark-rule);
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--dark-faint);
}

/* ---------- 404 ---------- */

.not-found {
  padding-block: 120px;
}

.not-found p {
  max-width: 640px;
  margin-bottom: 32px;
  font-size: 22px;
  font-weight: 500;
  line-height: 1.45;
}
