/* ==========================================================================
   Components: header, nav, rail, cards, buttons, footer
   Each block is self-contained and styled only through tokens.
   ========================================================================== */

/* ---- Site header --------------------------------------------------------
   56px tall with a 1px bottom divider, sticky so navigation stays reachable
   through long pages. */

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background-color: var(--color-bg);
  border-bottom: var(--border-width) solid var(--color-line);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-m);
  /* The divider is part of the 56px total, so the inner row is one pixel
     shorter. This keeps page content starting at y=88 as in the reference. */
  height: calc(var(--header-height) - var(--border-width));
}

.site-header__logo {
  font-size: var(--step-2);
  font-weight: var(--weight-bold);
  line-height: 1;
  white-space: nowrap;
}

/* ---- Primary navigation -------------------------------------------------
   Mobile: off-canvas panel toggled by the hamburger.
   Desktop (>=48rem): inline row, button hidden. */

.site-nav-group {
  display: flex;
  align-items: center;
  gap: var(--space-m);
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;  /* 44px minimum touch target */
  height: 2.75rem;
  margin-right: calc(var(--space-2xs) * -1);
  border-radius: var(--radius-m);
}

.nav-toggle__bars {
  position: relative;
}

.nav-toggle__bars,
.nav-toggle__bars::before,
.nav-toggle__bars::after {
  display: block;
  width: 1.25rem;
  height: 2px;
  background-color: currentColor;
  transition: transform var(--duration-fast) var(--ease-out),
              opacity var(--duration-fast) var(--ease-out);
}

.nav-toggle__bars::before,
.nav-toggle__bars::after {
  content: "";
  position: absolute;
}

.nav-toggle__bars::before { transform: translateY(-6px); }
.nav-toggle__bars::after { transform: translateY(6px); }

/* Hamburger morphs into a close icon when the panel is open. */
.nav-toggle[aria-expanded="true"] .nav-toggle__bars {
  background-color: transparent;
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::before {
  transform: rotate(45deg);
}

.nav-toggle[aria-expanded="true"] .nav-toggle__bars::after {
  transform: rotate(-45deg);
}

.site-nav {
  position: fixed;
  inset: var(--header-height) 0 auto;
  z-index: var(--z-nav-panel);
  background-color: var(--color-bg);
  border-bottom: var(--border-width) solid var(--color-line);
  padding: var(--space-s) var(--gutter) var(--space-l);
  /* Hidden by default on mobile. Uses visibility rather than display so the
     transition can run. */
  visibility: hidden;
  opacity: 0;
  transform: translateY(-0.5rem);
  transition: opacity var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out),
              visibility var(--duration-base);
}

.site-nav.is-open {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
}

.site-nav__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav__link {
  display: block;
  padding-block: var(--space-2xs);
  font-size: var(--step-2);
  font-weight: var(--weight-semibold);
}

.site-nav__link[aria-current="page"] {
  text-decoration: underline;
}

/* Avatar sits outside the collapsible panel so it stays visible at all
   widths, matching the reference. */
.site-header__avatar {
  width: var(--avatar-size);
  height: var(--avatar-size);
  border-radius: var(--radius-full);
  overflow: hidden;
  background-color: var(--color-surface);
  flex: 0 0 auto;
  transition: box-shadow var(--duration-base) var(--ease-out);
}

.site-header__avatar:hover,
.site-header__avatar:focus-visible {
  box-shadow: var(--shadow-avatar);
}

.site-header__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (width >= 48rem) {
  .nav-toggle {
    display: none;
  }

  .site-nav {
    position: static;
    inset: auto;
    visibility: visible;
    opacity: 1;
    transform: none;
    background: none;
    border: 0;
    padding: 0;
  }

  .site-nav__list {
    flex-direction: row;
    gap: var(--space-m);
    align-items: center;
  }

  .site-nav__link {
    font-size: var(--step-1);
    padding-block: 0;
  }
}

/* Prevents background scrolling while the mobile nav panel is open. */
body.has-nav-open {
  overflow: hidden;
}

@media (width >= 48rem) {
  body.has-nav-open {
    overflow: visible;
  }
}

/* ---- Rail ---------------------------------------------------------------
   The "Recent" sidebar. Sticky on desktop so it stays in view while the main
   column scrolls; static below the shell breakpoint where it sits inline. */

@media (width >= 62rem) {
  .rail__inner {
    position: sticky;
    top: calc(var(--header-height) + var(--space-l));
  }
}

.rail__heading {
  /* Optical alignment: the serif cap height sits lower in its line box than
     the adjacent portrait's top edge, so nudge it up to match. */
  margin-top: -0.15em;
}

/* The heavier rule dividing recent work from recent writing. */
.rail__divider {
  border: 0;
  border-top: var(--border-width) solid var(--color-line-strong);
  margin-block: var(--space-s);
}

/* Right-aligned "See all …" links. */
.rail__more {
  display: block;
  text-align: right;
  font-size: var(--step-0);
  font-weight: var(--weight-medium);
}

/* ---- Rail card ----------------------------------------------------------
   Title, 238x108 thumbnail, description. The title link is stretched across
   the whole card so the title, thumbnail, and summary share a single hit
   area and a single focusable element. */

.rail-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.rail-card__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

.rail-card__title {
  font-size: var(--step-2);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-snug);
}

/* .frame is itself positioned, so the thumbnail would otherwise paint over
   this overlay and swallow clicks on the image. */
.rail-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.rail-card__media img {
  transition: transform var(--duration-slow) var(--ease-out);
}

.rail-card:hover .rail-card__media img {
  transform: scale(1.04);
}

/* ---- Lens row -----------------------------------------------------------
   One of the four "How I see design" blocks. */

.lens__title {
  margin-bottom: var(--space-2xs);
}

.lens__body {
  display: flex;
  flex-direction: column;
  /* One blank line between paragraphs, matching the reference. */
  gap: var(--space-s);
}

/* ---- Buttons ------------------------------------------------------------ */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2xs);
  min-height: 2.75rem;
  padding: var(--space-2xs) var(--space-m);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-full);
  font-weight: var(--weight-semibold);
  line-height: 1.2;
  text-align: center;
  transition: background-color var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}

.button:hover {
  text-decoration: none;
}

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

.button--primary:hover {
  opacity: 0.85;
}

.button--secondary {
  border-color: var(--color-line);
}

.button--secondary:hover {
  border-color: var(--color-ink);
}

/* Text action with a trailing arrow that nudges on hover. */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3xs);
  font-weight: var(--weight-semibold);
}

.link-arrow::after {
  content: "\2192"; /* → */
  transition: transform var(--duration-fast) var(--ease-out);
}

.link-arrow:hover::after {
  transform: translateX(0.25rem);
}

/* ---- Tags --------------------------------------------------------------- */

.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3xs) var(--space-2xs);
  list-style: none;
  margin: 0;
  padding: 0;
}

.tag {
  display: inline-block;
  padding: 0.15em var(--space-2xs);
  border: var(--border-width) solid var(--color-line);
  border-radius: var(--radius-full);
  color: var(--color-ink-muted);
  font-size: var(--step--1);
  white-space: nowrap;
}

/* ---- Project card -------------------------------------------------------
   Used on the work index. */

.project-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
}

.project-card__media {
  overflow: hidden;
  border-radius: var(--radius-l);
  background-color: var(--color-surface);
}

.project-card__media img {
  transition: transform var(--duration-slow) var(--ease-out);
}

.project-card:hover .project-card__media img {
  transform: scale(1.03);
}

.project-card__title {
  font-size: var(--step-2);
}

.project-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.project-card__summary,
.project-card__meta {
  color: var(--color-ink-muted);
}

.project-card__meta {
  font-size: var(--step--1);
}

@media (width >= 56rem) {
  .project-card--featured {
    display: grid;
    grid-template-columns: 1.25fr 1fr;
    gap: var(--space-xl);
    align-items: center;
  }
}

/* ---- Work password gate -------------------------------------------------
   Courtesy lock on /work/ pages. Header stays above so navigation still
   works. html.work-unlocked is set from sessionStorage before paint. */

.work-gate {
  position: fixed;
  inset: 0;
  z-index: var(--z-work-gate);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
  background-color: color-mix(in srgb, var(--color-bg) 55%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

html.work-unlocked .work-gate {
  display: none;
}

html:not(.work-unlocked):has(.work-gate) {
  overflow: hidden;
}

.work-gate__card {
  width: 100%;
  max-width: 22rem;
  display: flex;
  flex-direction: column;
  gap: var(--space-s);
  padding: var(--space-l);
  background-color: var(--color-white);
  border: var(--border-width) solid var(--color-stroke);
  border-radius: var(--radius-l);
}

.work-gate__title {
  margin: 0;
}

.work-gate__input {
  width: 100%;
  min-height: 2.75rem;
  padding: var(--space-2xs) var(--space-s);
  border: var(--border-width) solid var(--color-line-strong);
  border-radius: var(--radius-m);
  background-color: var(--color-bg);
}

.work-gate__error {
  margin: 0;
  font-size: var(--step-0);
}

@media (prefers-reduced-motion: reduce) {
  .work-gate {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background-color: color-mix(in srgb, var(--color-bg) 92%, transparent);
  }
}

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

.site-footer {
  border-top: var(--border-width) solid var(--color-line);
  padding-block: var(--space-l);
  margin-top: var(--space-2xl);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m);
  justify-content: space-between;
  align-items: baseline;
}

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-m);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-footer__meta {
  color: var(--color-ink-muted);
}

/* ---- Scroll reveal ------------------------------------------------------
   js/main.js adds .is-visible. Each page carries a <noscript> fallback that
   reveals everything when JavaScript is unavailable. */

[data-reveal] {
  opacity: 0;
  transform: translateY(0.75rem);
  transition: opacity var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-out);
}

[data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    opacity: 1;
    transform: none;
  }
}
