/* ==========================================================================
   Andrew Churchill — portfolio site
   Single shared stylesheet for every page.

   How this file is organised (search for the ==== banners):
     1.  Design tokens (colours, spacing, fonts)  — tweak these first
     2.  Base / reset
     3.  Layout containers
     4.  Top bar (fixed nav)
     5.  Footer
     6.  Homepage hero + project card grid
     7.  Project detail pages
     8.  About page (header block, timeline, honours)
     9.  Skills page
     10. Interests page
     11. Responsive rules (narrow screens)

   Everything uses plain CSS — no build step, no framework.
   ========================================================================== */


/* ==========================================================================
   1. DESIGN TOKENS
   CSS custom properties. Change a value here and it updates everywhere.
   ========================================================================== */

:root {
  /* --- Colours --- */
  --ink:          #1a1a1a;   /* main text */
  --ink-soft:     #555555;   /* secondary text, subtitles */
  --ink-faint:    #888888;   /* dates, captions, footer */
  --accent:       #1f4e79;   /* links + active nav underline (deep blue) */
  --bg:           #ffffff;   /* page background */
  --bg-soft:      #f7f7f5;   /* subtle panel background */
  --rule:         #e4e4e0;   /* hairline borders */
  --placeholder:  #d8d8d5;   /* neutral grey for image placeholders */

  /* --- Type --- */
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  /* --- Spacing --- */
  --page-max:  980px;  /* max content width on wide pages */
  --prose-max: 720px;  /* narrower measure for long-form reading */
  --gap:       24px;
  --section:   64px;   /* vertical space between major sections */

  /* Surfaces that were previously hard-coded white, pulled out so dark mode
     has something to override. */
  --surface:    #ffffff;   /* buttons, raised panels                */
  --topbar-bg:  rgba(255, 255, 255, 0.92);
  --on-accent:  #ffffff;   /* text sitting on top of --accent       */
  --accent-dark:#163a5a;   /* accent hover                          */
  --card-hover: rgba(0, 0, 0, 0.08);
  --card-edge:  #cfcfca;

  /* --- The fixed top bar height. Used in three places, so it lives here. --- */
  --bar-height: 64px;
}


/* ==========================================================================
   1b. DARK THEME
   Only the tokens change; every rule below reads them, so nothing else in
   this file needs a dark-mode variant. theme.js sets data-theme on <html>
   before the first paint.
   ========================================================================== */

html[data-theme="dark"] {
  --ink:          #e4e8ec;
  --ink-soft:     #b0bac2;
  --ink-faint:    #87949d;
  --accent:       #6fb3e8;
  --bg:           #14181b;
  --bg-soft:      #1b2226;
  --rule:         #2b3439;
  --placeholder:  #262f34;

  --surface:      #1e262b;
  --topbar-bg:    rgba(18, 22, 25, 0.92);
  --on-accent:    #0d1115;
  --accent-dark:  #8ec6f2;
  --card-hover:   rgba(0, 0, 0, 0.45);
  --card-edge:    #3a464d;
}

/* Tells the browser to theme scrollbars, form controls and the like. */
html               { color-scheme: light; }
html[data-theme="dark"] { color-scheme: dark; }


/* ==========================================================================
   2. BASE / RESET
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;   /* padding never blows out an element's width */
}

html {
  /* Anchor-link fix #1: when jumping to an #id, leave room for the fixed bar
     so the target heading isn't hidden underneath it. */
  scroll-padding-top: calc(var(--bar-height) + 16px);
  scroll-behavior: smooth;
}

body {
  margin: 0;
  /* Anchor-link fix #2: push all content below the fixed bar. */
  padding-top: var(--bar-height);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.65;
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  line-height: 1.25;
  margin: 0 0 0.4em;
  font-weight: 650;
  letter-spacing: -0.015em;
}

h1 { font-size: 2.2rem; }
h2 { font-size: 1.45rem; }
h3 { font-size: 1.1rem; }

p { margin: 0 0 1.1em; }

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

a:hover { text-decoration: none; }

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

/* A thin horizontal rule used between sections */
.divider {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: var(--section) 0;
}


/* ==========================================================================
   3. LAYOUT CONTAINERS
   ========================================================================== */

/* Standard page wrapper — centres content and adds side padding on mobile. */
.container {
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 24px;
}

/* Narrower wrapper for text-heavy pages (project details, about). */
.container.narrow {
  max-width: var(--prose-max);
}

/* Vertical rhythm for a block of content */
main {
  padding: 56px 0 var(--section);
}

/* Generic section spacing */
.section { margin-bottom: var(--section); }

/* Small uppercase label above a section heading */
.eyebrow {
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 8px;
  font-weight: 600;
}


/* ==========================================================================
   4. TOP BAR (fixed nav — identical on every page)
   ========================================================================== */

.topbar {
  position: fixed;          /* stays put while the page scrolls */
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;             /* sits above page content */
  height: var(--bar-height);
  background: var(--topbar-bg);
  backdrop-filter: blur(8px);          /* frosted effect where supported */
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--rule);
}

/* Inner flex row: name on the left, nav and theme toggle on the right.
   margin-right:auto on the brand does the pushing, which keeps nav and
   toggle sitting together rather than being spread apart. */
.topbar-inner {
  max-width: var(--page-max);
  height: 100%;
  margin: 0 auto;
  padding: 0 24px;
  display: flex;
  align-items: center;
  gap: 16px;
}

.brand { margin-right: auto; }

/* The name on the left */
.brand {
  font-weight: 650;
  font-size: 1.02rem;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}

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

/* The nav links on the right */
.nav {
  display: flex;
  align-items: center;
  gap: 28px;
}

.nav a {
  color: var(--ink-soft);
  text-decoration: none;
  font-size: 0.95rem;
  padding: 4px 0;
  border-bottom: 2px solid transparent;  /* reserve space so nothing shifts */
  transition: color 0.15s ease;
}

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

/* Current-page highlight. Add class="active" to the matching link on each
   page — e.g. about.html marks its About link as active. */
.nav a.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}


/* --- Dark-mode switch, at the right-hand end of the top bar -------------
   A real pill-and-knob switch rather than a button. The whole control is
   the same height as the nav link text and shares the bar's baseline, so it
   reads as part of the menu row rather than an afterthought stuck on the end.
   Both glyphs live inside the knob; CSS shows whichever suits the theme. */

.theme-switch {
  flex: none;
  margin-left: 10px;          /* 16px flex gap + 10px = 26px, matching the
                                 28px rhythm between the nav links */
  padding: 4px 0;             /* same vertical padding as .nav a, so the
                                 control centres on the nav text */
  border: 0;
  background: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  line-height: 1;
}

.ts-track {
  position: relative;
  width: 44px;
  height: 22px;
  border-radius: 999px;
  border: 1px solid var(--rule);
  background: var(--bg-soft);
  display: inline-block;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.ts-knob {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 9px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.22s cubic-bezier(0.4, 0.1, 0.2, 1);
}

html[data-theme="dark"] .ts-knob { transform: translateX(22px); }

.theme-switch:hover .ts-track { border-color: var(--accent); }
.theme-switch:focus-visible .ts-track { outline: 2px solid var(--accent); outline-offset: 3px; }

/* moon while in light mode (what the switch will give you), sun in dark */
.theme-switch .icon-dark  { display: inline; }
.theme-switch .icon-light { display: none; }
html[data-theme="dark"] .theme-switch .icon-dark  { display: none; }
html[data-theme="dark"] .theme-switch .icon-light { display: inline; }


/* ==========================================================================
   5. FOOTER
   ========================================================================== */

.site-footer {
  border-top: 1px solid var(--rule);
  background: var(--bg-soft);
  padding: 36px 0;
  font-size: 0.9rem;
  color: var(--ink-faint);
}

.site-footer p { margin: 0 0 4px; }

.site-footer .footer-name {
  color: var(--ink);
  font-weight: 600;
}

.site-footer .copyright {
  margin-top: 14px;
  font-size: 0.82rem;
}


/* ==========================================================================
   6. HOMEPAGE — hero + project card grid
   ========================================================================== */

.hero {
  padding: 24px 0 var(--section);
}

.hero h1 {
  font-size: 2.6rem;
  margin-bottom: 12px;
}

.hero .subhead {
  font-size: 1.15rem;
  color: var(--ink-soft);
  max-width: 620px;
  margin: 0;
}

/* The grid itself.
   auto-fit + minmax means: fit as many 300px-or-wider columns as will
   comfortably fit, and reflow to fewer columns automatically on smaller
   screens. No media query needed for the grid. */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 32px;
}

/* Each card is a single <a> so the whole tile is clickable. */
.card {
  display: block;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--rule);
  border-radius: 10px;
  overflow: hidden;                 /* clips the image corner radius */
  background: var(--bg);
  transition: transform 0.18s ease, box-shadow 0.18s ease,
              border-color 0.18s ease;
}

.card:hover {
  transform: translateY(-3px);
  box-shadow: 0 8px 24px var(--card-hover);
  border-color: var(--card-edge);
}

/* Neutral grey block standing in for a photo.
   Swap for <img> later — the aspect-ratio keeps the layout stable. */
.card-image {
  background: var(--placeholder);
  aspect-ratio: 3 / 2;
  width: 100%;
}

/* When .card-image is a real <img> rather than an empty grey <div>,
   object-fit: cover crops it to the 3:2 box instead of squashing it. */
img.card-image {
  object-fit: cover;
  display: block;
}

.card-body { padding: 20px 22px 24px; }

.card-title {
  font-size: 1.08rem;
  font-weight: 650;
  margin: 0 0 6px;
  line-height: 1.35;
}

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

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


/* --- Compact card variant ------------------------------------------------
   Used on the JavaScript projects page. Same card component, just smaller,
   to signal these are lighter-weight than the main portfolio projects.
   Add .compact to BOTH the grid and each card:
     <div class="card-grid compact"> <a class="card compact"> ... */

.card-grid.compact {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 20px;
}

.card.compact .card-image  { aspect-ratio: 4 / 3; }   /* shorter than 3/2 */
.card.compact .card-body   { padding: 14px 16px 18px; }
.card.compact .card-title  { font-size: 0.97rem; margin-bottom: 4px; }
.card.compact .card-subtitle { font-size: 0.82rem; }


/* ==========================================================================
   7. PROJECT DETAIL PAGES
   ========================================================================== */

/* "← Back to Portfolio" link above the title */
.back-link {
  display: inline-block;
  margin-bottom: 24px;
  font-size: 0.92rem;
  text-decoration: none;
  color: var(--ink-soft);
}

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

.project-title { margin-bottom: 28px; }

/* A placeholder image slot + its caption.
   <figure class="image-slot">
     <div class="image-slot-box"></div>
     <figcaption>Caption text</figcaption>
   </figure>                                                              */
.image-slot {
  margin: 40px 0;
}

.image-slot-box {
  background: var(--placeholder);
  aspect-ratio: 16 / 9;
  width: 100%;
  border-radius: 8px;
}

/* A real photo in an image slot. Unlike the grey placeholder box above,
   this keeps the photo's own aspect ratio instead of forcing 16/9, so
   nothing gets cropped. Just swap the <div> for an <img>:
     <figure class="image-slot">
       <img src="images/whatever.jpg" alt="...">
       <figcaption>Caption</figcaption>
     </figure>                                                            */
.image-slot img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 8px;
}

/* Video behaves the same as an image inside a slot */
.image-slot video {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 8px;
  background: #000;
}

/* For small source images (e.g. a 500px-wide drawing). Without this they get
   stretched to full column width and go soft. Caps the width and centres. */
.image-slot.constrained img {
  max-width: 520px;
  margin: 0 auto;
}

.image-slot figcaption {
  margin-top: 10px;
  font-size: 0.88rem;
  font-style: italic;          /* italic captions, as requested */
  color: var(--ink-faint);
  text-align: center;
}

/* Full-width banner image sitting above a project title */
.page-banner {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 10px;
  margin: 0 0 30px;
}


/* --- Parts / spec tables -------------------------------------------------
   Two-column "Item | Part" tables. Deliberately plain: no vertical rules,
   just hairlines between rows.                                            */

.parts-table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 40px;
  font-size: 0.94rem;
}

.parts-table th,
.parts-table td {
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}

/* Header row */
.parts-table thead th {
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  font-weight: 600;
  border-bottom: 1px solid var(--ink-faint);
}

/* First column = the item label, second = the actual part */
.parts-table tbody th {
  font-weight: 500;
  color: var(--ink-soft);
  width: 45%;
}

.parts-table tbody td { font-weight: 550; }

.parts-table tbody tr:hover { background: var(--bg-soft); }

/* Heading that sits directly above a table */
.table-heading { margin: 0 0 14px; }


/* ==========================================================================
   8. ABOUT PAGE
   ========================================================================== */

/* Name / role / location block at the top */
.about-header {
  margin-bottom: 40px;
}

.about-header h1 {
  font-size: 2rem;
  margin-bottom: 8px;
}

.about-header .role {
  font-size: 1.05rem;
  color: var(--ink-soft);
  margin: 0 0 2px;
}

.about-header .location {
  font-size: 0.95rem;
  color: var(--ink-faint);
  margin: 0;
}

/* --- Vertical timeline ---------------------------------------------------
   The <ol class="timeline"> draws one continuous vertical line via a
   ::before pseudo-element; each <li> draws its own dot on top of it. */

.timeline {
  list-style: none;
  margin: 0;
  padding: 0 0 0 28px;
  position: relative;
}

/* The continuous line */
.timeline::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: var(--rule);
}

.timeline-item {
  position: relative;
  margin-bottom: 44px;
}

.timeline-item:last-child { margin-bottom: 0; }

/* The dot on the line */
.timeline-item::before {
  content: "";
  position: absolute;
  left: -28px;
  top: 9px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--accent);
}

.timeline-item h3 {
  margin: 0 0 2px;
  font-size: 1.08rem;
}

/* "Jun 2022 – Present · Richmond, BC · Hybrid" */
.timeline-meta {
  margin: 0 0 12px;
  font-size: 0.87rem;
  color: var(--ink-faint);
}

.timeline-item ul {
  margin: 0;
  padding-left: 20px;
}

.timeline-item li {
  margin-bottom: 8px;
  color: var(--ink-soft);
}

/* --- Honours --- */
.honour {
  margin-bottom: 28px;
}

.honour h3 {
  margin: 0 0 2px;
  font-size: 1.05rem;
}

.honour .honour-meta {
  margin: 0 0 8px;
  font-size: 0.87rem;
  color: var(--ink-faint);
}

.honour p { margin: 0; color: var(--ink-soft); }

/* --- Link row at the bottom of About --- */
.link-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

/* Button-ish outlined link */
.link-button {
  display: inline-block;
  padding: 10px 20px;
  border: 1px solid var(--accent);
  border-radius: 6px;
  text-decoration: none;
  font-size: 0.95rem;
  color: var(--accent);
  transition: background 0.15s ease, color 0.15s ease;
}

.link-button:hover {
  background: var(--accent);
  color: var(--on-accent);
}


/* ==========================================================================
   9. SKILLS PAGE
   ========================================================================== */

/* Two columns on wide screens, one on narrow — same auto-fit trick. */
.skills-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 32px;
}

.skill-group {
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 24px 26px;
  background: var(--bg-soft);
}

.skill-group h2 {
  font-size: 1.05rem;
  margin: 0 0 12px;
}

.skill-group ul {
  margin: 0;
  padding-left: 20px;
}

.skill-group li {
  margin-bottom: 8px;
  color: var(--ink-soft);
  font-size: 0.96rem;
}

.skill-group li:last-child { margin-bottom: 0; }


/* ==========================================================================
   10. INTERESTS PAGE
   ========================================================================== */

.interests-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.interests-list li {
  padding: 14px 0 14px 26px;
  border-bottom: 1px solid var(--rule);
  position: relative;
  color: var(--ink);
}

.interests-list li:last-child { border-bottom: 0; }

/* Custom bullet */
.interests-list li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: 24px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}


/* ==========================================================================
   10b. CONTACT PAGE
   A single centred panel. No form — this is a static site with no server to
   receive one, so everything routes through LinkedIn instead.
   ========================================================================== */

.contact-card {
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--bg-soft);
  padding: 40px 36px;
  text-align: center;
}

.contact-card h2 {
  margin: 0 0 10px;
  font-size: 1.3rem;
}

.contact-card .contact-lead {
  color: var(--ink-soft);
  max-width: 46ch;
  margin: 0 auto 28px;   /* auto side margins centre the narrow paragraph */
}

/* Make the LinkedIn button the obvious primary action: filled, not outlined */
.link-button.primary {
  background: var(--accent);
  color: var(--on-accent);
  padding: 13px 30px;
  font-weight: 600;
}

.link-button.primary:hover {
  background: var(--accent-dark);
}

/* Centre the button row on the contact card */
.contact-card .link-row { justify-content: center; }

/* Small print under the button */
.contact-note {
  margin: 28px auto 0;
  max-width: 52ch;
  font-size: 0.88rem;
  color: var(--ink-faint);
  line-height: 1.6;
}

.contact-note p { margin: 0 0 8px; }
.contact-note p:last-child { margin: 0; }


/* ==========================================================================
   11. RESPONSIVE — narrow screens
   The card/skills grids already reflow on their own; these rules handle
   the top bar and type scale.
   ========================================================================== */

@media (max-width: 640px) {

  :root {
    /* The bar gets taller when the nav wraps onto its own line, so the
       body padding token has to grow with it. */
    --bar-height: 104px;
    --section: 48px;
  }

  body { font-size: 16px; }

  /* Brand and toggle share the first row; the nav wraps onto its own line. */
  .topbar-inner {
    flex-wrap: wrap;
    justify-content: space-between;
    align-content: center;
    gap: 6px 8px;
    padding: 0 16px;
  }

  .brand { margin-right: 0; }

  .theme-switch { order: 2; margin-left: 0; }

  .nav {
    order: 3;
    flex-basis: 100%;
    gap: 18px;
    flex-wrap: wrap;
    justify-content: center;
  }

  .nav a { font-size: 0.88rem; }

  .container { padding: 0 18px; }

  main { padding: 36px 0 var(--section); }

  h1              { font-size: 1.8rem; }
  .hero h1        { font-size: 2rem; }
  .hero .subhead  { font-size: 1.02rem; }

  .card-grid { gap: 24px; }

  .link-row { flex-direction: column; }
  .link-button { text-align: center; }
}

/* Respect users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .card { transition: none; }
  .card:hover { transform: none; }
}
