:root {
  --font-body: "Geom", var(--font-sans);
  --font-heading: "Geom", var(--font-sans);
  --content-max: 72ch;
  /* Rem, not ch: a ch-based measure computes a different pixel width per
     element depending on its own font-size, so an h1 and a p "sharing"
     72ch actually get different widths and misaligned left edges once
     each is centered independently. A fixed rem value stays identical
     across every font-size, so headings and body text line up. */
  --prose-max: 44rem;

  /* Brand palette: teal accent, black/grey/white to complement it. */
  --color-accent: #a0d9d2;
  --color-black: #111111;
  --color-white: #ffffff;
  --color-grey-100: #f4f4f5;
  --color-grey-300: #e2e2e4;
  --color-grey-600: #6b6b70;

  --color-bg: var(--color-white);
  --color-text: var(--color-black);
  --color-muted: var(--color-grey-600);
  --color-border: var(--color-grey-300);
  --color-surface: rgb(from var(--color-accent) r g b / 15%);

  /* Perfect Fifth type scale (ratio 1.5), 1rem = 16px body copy. */
  --step--1: 0.667rem;
  --step-0: 1rem;
  --step-1: 1.5rem;
  --step-2: 2.25rem;
  --step-3: 3.375rem;
  --step-4: 5.0625rem;
}

* {
  box-sizing: border-box;
}

html {
  scrollbar-color: var(--color-accent) var(--color-grey-100);
}

::selection {
  background-color: var(--color-accent);
  color: var(--color-black);
}

body {
  background-color: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 100;
  line-height: var(--font-lineheight-3);
}

a {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-color: var(--color-accent);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  text-wrap: balance;
  margin-block: 0 0.5lh;
  font-weight: 100;
}

h1 {
  font-size: var(--step-3);
  font-weight: 400;
}

h2 {
  font-size: var(--step-2);
}

h3 {
  font-size: var(--step-1);
}

/* Open Props' normalize.css caps p/h1-h6/li/blockquote/etc. at a
   readable measure (max-inline-size) but leaves the box flush left
   instead of centering it — and each element type gets a different
   ch-based cap, so they don't line up with each other. Give the whole
   set one shared, absolute measure and center that, so a heading and
   the paragraph below it share the same left/right edges. Harmless
   no-op wherever an element's own container is already narrower. */
h1,
h2,
h3,
h4,
h5,
h6,
p,
small {
  max-inline-size: var(--prose-max);
  margin-inline: auto;
}

/* <li> is reused structurally all over the site (nav, post list rows,
   credits, social links) — reset Open Props' own li max-inline-size
   default globally, then opt genuine markdown lists in the prose
   measure back in specifically, below. */
li {
  max-inline-size: none;
}

/* li/dd/figcaption/blockquote only ever appear as genuine prose inside
   markdown content — scoped here, not global, since <li> elsewhere
   (nav, cards, post list rows, credits, social links) is a structural
   row, not a reading column, and shouldn't get centered/capped. */
.post-content li,
.post-content dd,
.post-content figcaption,
.post-content blockquote {
  max-inline-size: var(--prose-max);
  margin-inline: auto;
}

.wrap {
  /* Not var(--size-content-3): Open Props defines that as 60ch (~480px),
     far narrower than this layout wants. min() guarantees the side
     gutter even on viewports narrower than the 68.75rem cap. */
  width: min(100% - 2 * var(--size-6), 68.75rem);
  margin-inline: auto;
  padding-inline: var(--size-4);
}

/* Header */

.site-header {
  padding-block: var(--size-4);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--size-4);
  flex-wrap: wrap;
}

.site-title {
  display: inline-flex;
  align-items: center;
}

.site-logo {
  display: block;
  height: 1.75rem;
  width: auto;
}

.site-nav ul {
  display: flex;
  gap: var(--size-5);
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-nav a {
  text-decoration: none;
  font-size: var(--step-0);
  text-transform: uppercase;
  color: var(--color-accent);
}

.site-nav a:hover {
  text-decoration: underline;
  text-decoration-color: var(--color-accent);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Mobile nav: a native <details>/<summary> disclosure, no JS. A closed
   <details>'s content is suppressed by the browser in a way plain CSS
   can't reliably force back open (its own box stays sized as if
   closed even when a child is display:flex !important'd visible), so
   rather than fight that, the nav list is rendered twice: a plain
   always-visible <ul> for desktop, and a second copy inside the
   <details> for the mobile toggle. Only one of the two is ever shown
   at a given viewport width. */
.nav-toggle {
  display: none;
  background: none;
}

.nav-toggle-btn {
  list-style: none;
  cursor: pointer;
  color: var(--color-accent);
  padding: var(--size-1);
  background: none;
}

.nav-toggle-btn::-webkit-details-marker {
  display: none;
}

.nav-toggle-icon {
  display: block;
  position: relative;
  width: 1.5rem;
  height: 1.125rem;
}

.nav-toggle-icon .line {
  position: absolute;
  inset-inline-start: 0;
  width: 100%;
  height: var(--border-size-2, 2px);
  background: currentColor;
  transition: transform 0.2s ease, opacity 0.2s ease;
}

.nav-toggle-icon .line:nth-child(1) {
  top: 0;
}

.nav-toggle-icon .line:nth-child(2) {
  top: 50%;
  translate: 0 -50%;
}

.nav-toggle-icon .line:nth-child(3) {
  bottom: 0;
}

.nav-toggle[open] .nav-toggle-icon .line:nth-child(1) {
  transform: translateY(0.5rem) rotate(45deg);
}

.nav-toggle[open] .nav-toggle-icon .line:nth-child(2) {
  opacity: 0;
}

.nav-toggle[open] .nav-toggle-icon .line:nth-child(3) {
  transform: translateY(-0.5rem) rotate(-45deg);
}

@media (max-width: 40em) {
  .site-nav > .site-nav-list {
    display: none;
  }

  .nav-toggle {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
  }

  .nav-toggle-btn {
    display: flex;
    align-items: center;
  }

  /* .site-nav ul { display: flex } is an author rule with no
     !important, which beats the browser's own (UA-origin) rule that
     normally hides a <details>'s content when it's closed. Restate it
     explicitly here so the menu actually stays hidden until opened. */
  .nav-toggle:not([open]) ul {
    display: none;
  }

  .nav-toggle ul {
    flex-direction: column;
    align-items: flex-end;
    gap: var(--size-3);
    margin-block-start: var(--size-4);
  }
}

/* Main */

main.wrap {
  background: var(--color-white);
  border-radius: var(--radius-4) var(--radius-4) 0 0;
  padding-block: var(--size-7);
  min-height: 60vh;
}

/* Consistent breathing room between a page's top-level sections. */
main.wrap > * + * {
  margin-block-start: var(--size-8);
}

/* Footer */

.site-footer {
  color: var(--color-muted);
  font-size: var(--step--1);
}

.site-footer .wrap {
  background: var(--color-white);
  padding-block: var(--size-5);
}

.site-footer small {
  font-size: inherit;
}

/* Hero */

.hero {
  padding-block: var(--size-8) var(--size-7);
  text-align: center;
}

.hero h1 {
  font-size: var(--step-4);
  max-inline-size: none;
}

.hero p {
  color: var(--color-muted);
  font-size: var(--step-1);
  max-width: var(--content-max);
  margin-inline: auto;
}

/* Cards / grid */

.grid {
  display: grid;
  gap: var(--size-6);
  grid-template-columns: repeat(auto-fill, minmax(17.5rem, 1fr));
}

.card {
  border: var(--border-size-1) solid var(--color-border);
  border-radius: var(--radius-3);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: box-shadow var(--transition, 0.2s ease);
}

.card:hover {
  box-shadow: var(--shadow-3);
}

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

.card > a {
  text-decoration: none;
}

.card-body h3 a {
  text-decoration: none;
}

.card img {
  width: 100%;
  aspect-ratio: var(--ratio-landscape);
  object-fit: cover;
  display: block;
}

.card-body {
  padding: var(--size-4);
  display: flex;
  flex-direction: column;
  gap: var(--size-2);
}

.card-body h3 {
  margin: 0;
  font-size: var(--step-1);
  max-inline-size: none;
}

.card-body p {
  margin: 0;
  color: var(--color-muted);
  font-size: var(--step--1);
  max-inline-size: none;
}

/* Portfolio grid: edge-to-edge tiles, no border/radius/gap, title and
   category overlaid in white at the bottom-left. A 4-track base grid
   with a repeating span pattern (1/1/2, then 2/1/1) gives every pair
   of rows a different rhythm instead of a flat uniform grid, no
   matter how many tiles are actually being shown. */
.portfolio-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 20rem;
  gap: 0;
}

.portfolio-tile:nth-child(4n + 1),
.portfolio-tile:nth-child(4n + 4) {
  grid-column: span 1;
}

.portfolio-tile:nth-child(4n + 2),
.portfolio-tile:nth-child(4n + 3) {
  grid-column: span 2;
}

/* About page's designer cards share the same edge-to-edge tile/overlay
   look, just with an even 1/1 grid instead of the portfolio mosaic. */
.about-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 20rem;
  gap: 0;
}

@media (max-width: 40em) {
  .portfolio-grid,
  .about-grid {
    grid-template-columns: 1fr;
  }

  .portfolio-grid > .portfolio-tile {
    grid-column: span 1;
  }
}

.portfolio-tile,
.about-tile {
  position: relative;
  overflow: hidden;
}

.portfolio-tile > a,
.about-tile > a {
  display: block;
  width: 100%;
  height: 100%;
  text-decoration: none;
}

.portfolio-tile img,
.about-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.portfolio-tile .tile-overlay,
.about-tile .tile-overlay {
  position: absolute;
  inset-inline-start: 0;
  inset-block-end: 0;
  inset-inline-end: 0;
  padding: var(--size-4);
  background: linear-gradient(to top, rgb(0 0 0 / 55%), transparent 65%);
  color: var(--color-white);
}

.portfolio-tile .tile-overlay h3,
.about-tile .tile-overlay h3 {
  margin: 0;
  font-size: var(--step-1);
  max-inline-size: none;
  color: var(--color-white);
}

.portfolio-tile .tile-overlay p,
.about-tile .tile-overlay p {
  margin: 0;
  font-size: var(--step--1);
  max-inline-size: none;
  color: var(--color-white);
}

/* Pills / tags */

.tag-pill {
  display: inline-block;
  padding: var(--size-1) var(--size-3);
  border-radius: var(--radius-round);
  background: var(--color-accent);
  color: var(--color-black);
  font-size: var(--step--1);
  text-decoration: none;
  margin-inline-end: var(--size-2);
  margin-block-end: var(--size-2);
}

.tag-pill-outline {
  background: transparent;
  border: var(--border-size-1) solid var(--color-border);
  color: var(--color-text);
}

/* Portfolio filters */

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-3);
  margin-block-end: var(--size-6);
}

.filter-group {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-2);
  align-items: center;
}

.filter-group span {
  color: var(--color-muted);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: var(--font-letterspacing-2);
}

.filter-btn {
  border: var(--border-size-1) solid var(--color-border);
  background: transparent;
  color: var(--color-text);
  padding: var(--size-1) var(--size-3);
  border-radius: var(--radius-round);
  font-size: var(--step--1);
  cursor: pointer;
}

.filter-btn:hover {
  border-color: var(--color-accent);
}

.filter-btn[aria-pressed="true"] {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-black);
}

.portfolio-item[hidden] {
  display: none;
}

/* Post list */

.post-list {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--size-6);
}

.post-list-item {
  display: flex;
  gap: var(--size-5);
  align-items: flex-start;
}

.post-list-item-body {
  flex: 1;
}

.post-thumb {
  width: 12.5rem;
  aspect-ratio: var(--ratio-landscape);
  object-fit: cover;
  border-radius: var(--radius-3);
  flex-shrink: 0;
}

@media (max-width: 40em) {
  .post-list-item {
    flex-direction: column;
  }

  .post-thumb {
    width: 100%;
  }
}

.post-list-item h2 {
  font-size: var(--step-1);
  margin-block-end: 0.25lh;
  max-inline-size: none;
}

.post-list-item h2 a {
  text-decoration: none;
}

.post-list-item h2 a:hover {
  color: var(--color-accent);
}

.post-list-item .post-meta {
  color: var(--color-muted);
  font-size: var(--step--1);
  margin-block-end: var(--size-2);
  max-inline-size: none;
}

.post-meta {
  color: var(--color-muted);
  font-size: var(--step--1);
}

.post-hero {
  width: 100%;
  aspect-ratio: var(--ratio-widescreen);
  object-fit: cover;
  border-radius: var(--radius-3);
  margin-block-start: var(--size-5);
}

.post-content {
  margin-block-start: var(--size-6);
}

.post-content :first-child {
  margin-block-start: 0;
}

.post-content p {
  margin-block: 1lh;
}

/* Figures read wider than the text measure, out to the container's
   padding edge, instead of matching the narrow prose column. */
.post-content img,
.post-content table,
.post-content pre {
  max-inline-size: none;
  width: 100%;
}

.post-content td,
.post-content th {
  max-inline-size: none;
}

@media (max-width: 40em) {
  /* Wide comparison tables would otherwise force every cell to wrap
     onto several lines, making rows absurdly tall — let them scroll
     horizontally instead. */
  .post-content table {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
  }
}

/* Credits */

.credits {
  margin-block-start: var(--size-7);
  padding-block-start: var(--size-5);
  border-top: var(--border-size-1) solid var(--color-border);
}

.credits h2 {
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: var(--font-letterspacing-2);
  color: var(--color-muted);
  margin-block-end: 0.75lh;
}

.credits-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--size-2);
}

.credit-role {
  color: var(--color-muted);
  margin-inline-end: var(--size-2);
}

.credit-role::after {
  content: ":";
}

/* Social links */

.social-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-4);
  list-style: none;
  margin: var(--size-3) 0 0;
  padding: 0;
}

.social-link {
  display: inline-flex;
  align-items: center;
  gap: var(--size-2);
  color: var(--color-accent);
  text-decoration: none;
}

.social-link:hover {
  text-decoration: underline;
  text-decoration-color: var(--color-accent);
}

.social-icon {
  width: 1.25rem;
  height: 1.25rem;
  flex-shrink: 0;
}

/* Page-to-page view transitions (native cross-document View Transitions API).
   Unsupported browsers simply ignore this and navigate normally. */

@view-transition {
  navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.4s;
  animation-timing-function: ease;
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* Scroll-driven reveal animations (native CSS scroll-linked animations).
   Gated behind @supports so browsers without it just show content normally,
   and behind prefers-reduced-motion so motion-sensitive users get no motion. */

@keyframes reveal-fade-in-up {
  from {
    opacity: 0;
    transform: translateY(1.25rem);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .reveal {
      animation: reveal-fade-in-up 0.7s ease both;
      animation-timeline: view();
      animation-range: entry 0% cover 40%;
    }

    .reveal-item {
      animation-delay: calc(var(--reveal-index, 0) * 90ms);
    }
  }
}
