/* =============================================================================
   andrewelans.com — the whole stylesheet.

   Naming is BEM: .block, .block__element, .block--modifier, plus `is-` state
   classes (.is-active) for state applied by the build. Blocks are listed in the
   order they appear on a page.

   The blog has its own copy of this file, in the blog.andrewelans.com repo:
   same tokens, same reset, same nav and footer, without the blocks only the CV
   and home page use. They are deliberate duplicates rather than a shared
   package, so a change that applies to both has to be made in both.
   ============================================================================= */

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

:root {
  --color-page: #edf1f2;
  --color-surface: #f7f9fa;
  --color-raised: #dee3e7;
  --color-ink: #081e32;
  --color-ink-muted: #4d5a66;
  --color-accent: #ff9900;
  --color-border: #c3ccd2;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2rem;
  --space-6: 3rem;

  --radius: 0.375rem;
  --shadow: 0 3px 5px rgb(57 63 72 / 30%);

  --font-body:
    system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --measure: 68ch;

  /* The site's one breakpoint. Its value is set by the widest thing that has to
     fit on a row: the expanded nav, where brand plus five links needs ~650px,
     so below 44rem the menu collapses. Media queries cannot read custom
     properties, so 44rem is repeated as a literal in each @media rule below —
     change them together. Adding a sixth nav item means measuring again. */
  --bp: 44rem;
}

/* -----------------------------------------------------------------------------
   Reset
   -------------------------------------------------------------------------- */

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

body,
h1,
h2,
h3,
p,
ul,
figure {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

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

html {
  /* Anchors land below the sticky-ish header rather than under it. */
  scroll-padding-top: var(--space-5);
}

body {
  /* Column layout so .main absorbs the slack and the footer sits at the
     bottom of the viewport on short pages, after the content on long ones. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* A floor, not a layout: below this the page scrolls sideways instead of
     reflowing. 375px is the narrowest viewport that actually ships (iPhone
     SE), so it is the honest place to stop. It used to be 400px, which put
     that phone into horizontal scrolling — the thing the floor was guarding
     against (the nav toggle wrapping under the brand) is now prevented in the
     nav block itself, by letting the brand shrink and pinning the button. */
  min-width: 375px;
  background-color: var(--color-page);
  color: var(--color-ink);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-y: scroll;
}

a {
  color: inherit;
}

/* Links that leave the site are marked. Keyed off `target` rather than a class,
   so the icon and the new-tab behaviour cannot drift apart. The second `content`
   gives the glyph empty alt text so screen readers skip it; browsers without
   that syntax discard the line and keep the plain glyph above. */
a[target="_blank"]::after {
  content: "↗";
  content: "↗" / "";
  padding-left: 0.15em;
  font-size: 0.85em;
}

:focus-visible {
  outline: 2px solid var(--color-ink);
  outline-offset: 2px;
}

/* -----------------------------------------------------------------------------
   Layout helpers — the only non-BEM classes, kept deliberately few
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: 72rem;
  margin-inline: auto;
  padding-inline: var(--space-4);
}

.prose {
  max-width: var(--measure);
}

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

/* -----------------------------------------------------------------------------
   Block: skip-link
   -------------------------------------------------------------------------- */

.skip-link {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 10;
  padding: var(--space-2) var(--space-3);
  background-color: var(--color-surface);
  border-radius: var(--radius);
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: translateY(0);
}

/* -----------------------------------------------------------------------------
   Block: nav
   -------------------------------------------------------------------------- */

.nav {
  background-color: var(--color-surface);
  box-shadow: var(--shadow);
}

.nav__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding-block: var(--space-3);
}

/* Below --bp the brand shares a row with the toggle button. At the 375px floor
   that row has ~327px in it, and the button plus the row gap take 60 — so the
   whole brand has to fit in ~267px. `min-width: 0` is what makes that possible:
   a flex item refuses to shrink below its content width without it, and the
   overflow is what used to push the button onto a line of its own. The smaller
   logo and type buy the wordmark enough room to stay on one line; full size
   returns at --bp. */
.nav__brand {
  display: flex;
  align-items: center;
  min-width: 0;
  gap: var(--space-2);
  margin-right: auto;
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
}

.nav__logo {
  width: 101px;
  height: 24px;
}

.nav__brand.is-active {
  pointer-events: none;
}

/* The checkbox holds the open/closed state; the label is the visible button.
   No JavaScript is involved: `:checked ~ .nav__list` does the work. */
.nav__toggle-button {
  display: flex;
  flex: none; /* holds its row: the brand shrinks, this never does */
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  cursor: pointer;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

.nav__toggle:focus-visible ~ .nav__toggle-button {
  outline: 2px solid var(--color-ink);
  outline-offset: 2px;
}

.nav__toggle-icon,
.nav__toggle-icon::before,
.nav__toggle-icon::after {
  display: block;
  width: 1.25rem;
  height: 2px;
  background-color: currentcolor;
}

.nav__toggle-icon {
  position: relative;
}

.nav__toggle-icon::before,
.nav__toggle-icon::after {
  position: absolute;
  left: 0;
  content: '';
}

.nav__toggle-icon::before {
  top: -6px;
}

.nav__toggle-icon::after {
  top: 6px;
}

.nav__list {
  display: none;
  flex-basis: 100%;
  flex-direction: column;
}

.nav__toggle:checked ~ .nav__list {
  display: flex;
}

.nav__link {
  display: block;
  padding: var(--space-2) 0;
  text-decoration: none;
}

.nav__link:hover {
  text-decoration: underline;
  text-underline-offset: 5px;
}

/* The page you are on is marked, and not clickable. */
.nav__link.is-active {
  pointer-events: none;
  text-decoration: underline;
  text-underline-offset: 5px;
}

/* --bp */
@media (min-width: 44rem) {
  .nav__toggle-button {
    display: none;
  }

  /* The row is no longer contested, so the brand goes back to full size. */
  .nav__brand {
    gap: var(--space-3);
    font-size: 1.125rem;
  }

  .nav__logo {
    width: 135px;
    height: 32px;
  }

  .nav__list {
    display: flex;
    flex-basis: auto;
    flex-direction: row;
    gap: var(--space-4);
  }
}

/* -----------------------------------------------------------------------------
   Block: main
   -------------------------------------------------------------------------- */

.main {
  flex: 1;
  padding-block: var(--space-6);
}

.main__title {
  font-size: clamp(1.875rem, 1.4rem + 2vw, 2.5rem);
  line-height: 1.2;
}

.main__lead {
  margin-top: var(--space-3);
  color: var(--color-ink-muted);
  font-size: 1.125rem;
}

.main__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

/* Lists carry a top margin but no bottom one, so prose resuming after a list
   needs the same gap a following paragraph would get. */
.main p + p,
.main ul + p {
  margin-top: var(--space-3);
}

/* -----------------------------------------------------------------------------
   Block: hero — the home page introduction, photo beside the text
   -------------------------------------------------------------------------- */

/* Stacked below --bp: `center` is the cross axis here, so it centres the photo
   over the text. */
.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-5);
}

/* Name and tagline centre under the photo to finish that column; the paragraph
   below them does not — centred prose loses the left edge a reader scans. */
.hero__body .main__title,
.hero__body .main__lead {
  text-align: center;
}

/* Square source (800x800), so aspect-ratio just holds the box while it loads. */
.hero__photo {
  flex: none;
  width: 100%;
  max-width: 12rem;
  aspect-ratio: 1;
  object-fit: cover;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  box-shadow: var(--shadow);
}

/* --bp */
@media (min-width: 44rem) {
  /* `align-items: center` carries over from the base rule and does the other
     job here: on a row it centres photo and text against each other. */
  .hero {
    flex-direction: row;
    gap: var(--space-6);
  }

  /* Beside the photo they line up with the paragraph again. */
  .hero__body .main__title,
  .hero__body .main__lead {
    text-align: start;
  }

  .hero__photo {
    max-width: 14rem;
  }
}

/* -----------------------------------------------------------------------------
   Block: button
   -------------------------------------------------------------------------- */

.button {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-ink);
  border-radius: var(--radius);
  text-decoration: none;
  line-height: 1.5;
}

.button:hover {
  background-color: var(--color-raised);
}

.button--primary {
  background-color: var(--color-ink);
  color: var(--color-surface);
}

.button--primary:hover {
  background-color: #0d2b47;
}

/* -----------------------------------------------------------------------------
   Block: section
   -------------------------------------------------------------------------- */

.section {
  margin-top: var(--space-6);
}

.section__title {
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  font-size: 1.5rem;
  margin-bottom: var(--space-1);
}

/* -----------------------------------------------------------------------------
   Block: entry — one job, one degree, one project
   -------------------------------------------------------------------------- */

.entry {
  margin-top: var(--space-4);
  padding: var(--space-4);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}

.entry__title {
  font-size: 1.125rem;
}

.entry__meta {
  color: var(--color-ink-muted);
  font-size: 0.875rem;
}

.entry__list {
  margin-top: var(--space-3);
  padding-left: var(--space-4);
  list-style: disc;
}

.entry__list > li + li {
  margin-top: var(--space-1);
}

/* -----------------------------------------------------------------------------
   Block: list — plain bulleted content lists
   -------------------------------------------------------------------------- */

.list {
  margin-top: var(--space-3);
  padding-left: var(--space-4);
  list-style: disc;
}

.list > li + li {
  margin-top: var(--space-1);
}

/* For lists whose items carry their own affordance, e.g. a <details> marker. */
.list--plain {
  padding-left: 0;
  list-style: none;
}

.list--plain > li + li {
  margin-top: 0;
}

/* -----------------------------------------------------------------------------
   Block: project — one EPC project, expanded on demand
   -------------------------------------------------------------------------- */

.project {
  border-bottom: 1px solid var(--color-border);
}

.project__name {
  padding-block: var(--space-2);
  cursor: pointer;
  font-weight: 600;
}

.project__name:hover {
  color: var(--color-ink-muted);
}

.project__body {
  padding-bottom: var(--space-3);
}

.project__meta {
  color: var(--color-ink-muted);
  font-size: 0.875rem;
}

.project__body p + p {
  margin-top: var(--space-3);
}


/* -----------------------------------------------------------------------------
   Block: contact — the labelled <dl> of ways to reach me
   -------------------------------------------------------------------------- */

.contact {
  margin: var(--space-5) 0 0;
}

.contact__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  padding-block: var(--space-2);
  border-top: 1px solid var(--color-border);
}

.contact__label {
  flex: none;
  min-width: 6rem;
  color: var(--color-ink-muted);
}

.contact__value {
  margin: 0;
}

/* -----------------------------------------------------------------------------
   Block: site-footer
   -------------------------------------------------------------------------- */

.site-footer {
  margin-top: var(--space-6);
  padding-block: var(--space-4);
  border-top: 1px solid var(--color-border);
  color: var(--color-ink-muted);
  font-size: 0.875rem;
}

/* Mobile-first, like the nav above it: the three groups stack, and go back on
   one row at --bp. They used to be a single wrapping row with
   `justify-content: space-between`, which threw the wrapped groups to opposite
   edges on a narrow screen instead of stacking them. */
.site-footer__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* The links inside a group have to be able to break too, or a four-link group
   stays on one line and overflows however the parent is laid out. */
.site-footer__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* --bp */
@media (min-width: 44rem) {
  .site-footer__inner {
    flex-direction: row;
    justify-content: space-between;
  }
}

/* -----------------------------------------------------------------------------
   Print — a CV gets printed and saved to PDF, so nothing may stay collapsed
   -------------------------------------------------------------------------- */

@media print {
  /* Browsers hide a closed <details> in two different ways depending on
     version, so both are overridden. */
  .project > .project__body {
    display: block !important;
    content-visibility: visible !important;
  }

  .project::details-content {
    content-visibility: visible !important;
  }

  .project__name {
    list-style: none;
  }

  .site-header,
  .site-footer,
  .main__actions {
    display: none;
  }
}
