/* Sidebar layout: on wide screens the header (name, nav, profile links) is a
   fixed column on the left and the content scrolls on the right. On narrow
   screens it becomes a sticky top bar, the same on every page. */

:root {
  color-scheme: dark;
  --bg: #0b1222;
  --raise: #121b2e;
  --text: #a8b3c7;
  --strong: #e8edf5;
  --muted: #8592a8;
  --accent: #5eead4;
  --accent-soft: rgba(94, 234, 212, 0.14);
  --line: #1f2b45;
  --mono: "JetBrains Mono", ui-monospace, Consolas, monospace;

  font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 1rem;
  line-height: 1.7;
}

@media (prefers-color-scheme: light) {
  :root {
    color-scheme: light;
    --bg: #f7f8fb;
    --raise: #ffffff;
    --text: #3d4a5c;
    --strong: #0e1726;
    --muted: #5b687d;
    --accent: #0b7066;
    --accent-soft: rgba(11, 112, 102, 0.1);
    --line: #dfe4ec;
  }
}

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

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

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

/* Links */

a {
  color: var(--strong);
  text-decoration: none;
}

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

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Links inside running text stay underlined so they read as links. */
main p a,
main dd a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

/* Icons from /icons.svg, sized to the text around them. */
.icon {
  flex: none;
  width: 1em;
  height: 1em;
  vertical-align: -0.125em;
}

/* Hidden until a keyboard user tabs to it. */
.skip-link {
  position: absolute;
  z-index: 10;
  left: 1rem;
  top: -4rem;
  padding: 0.5rem 1rem;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 6px;
}

.skip-link:focus {
  top: 1rem;
}

/* Layout */

header {
  padding: 2.5rem 1.5rem 0.5rem;
}

main {
  padding: 2rem 1.5rem 1rem;
}

footer {
  margin: 0 1.5rem;
  padding: 1.5rem 0 3rem;
  border-top: 1px solid var(--line);
  font-size: 0.8125rem;
  color: var(--muted);
}

footer p {
  margin: 0;
}

footer a {
  color: var(--text);
}

@media (min-width: 64rem) {
  body {
    display: grid;
    grid-template-columns: clamp(16rem, 24vw, 19rem) minmax(0, 44rem);
    grid-template-rows: 1fr auto;
    justify-content: center;
    column-gap: clamp(3rem, 6vw, 6rem);
    min-height: 100vh;
    padding: 0 clamp(2rem, 3vw, 3rem);
  }

  header {
    grid-row: 1 / span 2;
    position: sticky;
    top: 0;
    height: 100vh;
    display: flex;
    flex-direction: column;
    padding: 6rem 0 3rem;
  }

  main {
    padding: 6rem 0 2rem;
  }

  footer {
    grid-column: 2;
    margin: 0;
  }
}

/* Sidebar */

.name {
  margin: 0;
  font-size: clamp(2.25rem, 4vw, 2.75rem);
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.1;
  color: var(--strong);
}

.name a {
  color: inherit;
}

.tagline {
  margin: 0.75rem 0 0;
  font-size: 1.125rem;
  font-weight: 500;
  color: var(--strong);
}

.blurb {
  margin: 0.5rem 0 0;
  max-width: 20rem;
  color: var(--muted);
}

header nav ul,
.profiles {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The nav draws the site as a file tree: Projects, Games, and Experience are
   folders inside portfolio/, matching their addresses. The branch lines are text,
   hidden from screen readers, which get the nesting from the nested lists.
   Small screens show the same links in one row, without the lines. */
header nav {
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 2;
}

header nav > ul {
  margin-top: 4rem;
}

header nav a {
  white-space: nowrap;
  color: var(--muted);
}

header nav a:hover,
header nav .root {
  color: var(--strong);
}

header nav a[aria-current] {
  color: var(--accent);
}

.branch {
  white-space: pre;
  color: color-mix(in srgb, var(--muted) 55%, transparent);
}

/* The alternative text after the slash keeps screen readers from reading it;
   aria-current already tells them. */
header nav a[aria-current="page"]::after {
  content: "  \2190  you are here";
  content: "  \2190  you are here" / "";
  white-space: pre;
  color: var(--muted);
}

/* Icon-only profile links; each has an aria-label and a title tooltip. */
.profiles {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.25rem;
  margin-top: 1.25rem;
  font-size: 1.75rem;
}

.profiles a {
  display: flex;
  color: var(--muted);
  transition: color 0.2s;
}

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

/* Links to the Christmas Tracker, at the end of the profile row. */
.profiles .tree-button {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  font-size: 1.25rem;
  line-height: 1;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 10px;
  transition: border-color 0.2s, transform 0.2s;
}

.profiles .tree-button:hover {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--line));
  transform: rotate(-8deg);
}

@media (min-width: 64rem) {
  .profiles {
    margin-top: auto;
    padding-top: 2rem;
  }
}

/* Short laptop screens: tighten the sidebar so it still fits, and let it
   scroll if it doesn't. */
@media (min-width: 64rem) and (max-height: 40rem) {
  header {
    padding: 3rem 0 1.5rem;
    overflow-y: auto;
    scrollbar-width: none;
  }

  main {
    padding-top: 3rem;
  }

  header nav > ul {
    margin-top: 2rem;
  }
}

/* On small screens every page gets the same sticky top bar: the name and
   profile links, then the nav links in a row that scrolls sideways if it
   doesn't fit. The intro is left out; About me covers it. */
@media (max-width: 63.99rem) {
  header {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    padding: 0.875rem 1.5rem 0;
    background: color-mix(in srgb, var(--bg) 94%, transparent);
    backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--line);
  }

  .name {
    font-size: 1.25rem;
  }

  :is(.tagline, .blurb) {
    display: none;
  }

  /* The profile links sit beside the name, ahead of the nav row. */
  .profiles {
    order: 1;
    gap: 0.875rem;
    margin: 0 0 0 auto;
    font-size: 1.25rem;
  }

  .profiles .tree-button {
    width: 2rem;
    height: 2rem;
    font-size: 1rem;
    border-radius: 8px;
  }

  header nav {
    order: 2;
    flex: 1 1 100%;
    margin: 0.25rem -1.5rem 0;
    padding: 0 1.5rem;
    overflow-x: auto;
    scrollbar-width: none;
  }

  /* The tree flattens into one row. The root shows as just "~". */
  header nav > ul {
    display: flex;
    gap: 1.25rem;
    margin-top: 0;
  }

  header nav li,
  header nav ul ul {
    display: contents;
  }

  :is(.branch, .host),
  header nav a[aria-current="page"]::after {
    display: none;
  }

  header nav a {
    padding: 0.375rem 0;
    border-bottom: 2px solid transparent;
  }

  header nav a[aria-current] {
    border-color: var(--accent);
  }

  /* The bar is sticky, so the section labels aren't. */
  section > h2 {
    position: static;
  }
}

/* Text */

h1,
h2,
h3 {
  color: var(--strong);
  text-wrap: balance;
}

main h1 {
  margin: 0 0 1.5rem;
  font-size: 2.5rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.15;
}

p,
ul,
dl {
  margin: 0 0 1rem;
}

p {
  text-wrap: pretty;
}

/* Section labels (About me, Selected work, ...) stick to the top while their
   section scrolls past. */
section {
  margin-bottom: 4rem;
}

section > h2,
main > h2 {
  margin: 0 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

section > h2 {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 1rem 0;
  background: var(--bg);
}

/* The /portfolio/ page names itself and its folders the way the nav's tree does. */
main h1.folder {
  font-family: var(--mono);
  font-weight: 500;
  letter-spacing: -0.02em;
}

section > h2.folder {
  font-family: var(--mono);
  font-size: 0.875rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
}

/* "More in Projects and Games." */
section > p:last-child {
  margin-top: 1.5rem;
}

/* Work rows, on the home page and the Projects/Games pages */

article {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "date" "title" "summary" "view";
  column-gap: 1rem;
  margin: 0 -1rem 0.5rem;
  padding: 1rem;
  border-radius: 10px;
  transition: background-color 0.2s, box-shadow 0.2s;
}

article:has(.thumb) {
  grid-template-columns: 5.5rem minmax(0, 1fr);
  grid-template-areas: "thumb date" "thumb title" "summary summary" "view view";
}

article:hover,
article:has(:focus-visible) {
  background: var(--raise);
  box-shadow: 0 0 0 1px var(--line), 0 12px 32px -18px rgba(0, 0, 0, 0.5);
}

/* The title link covers the whole row, so the focus ring goes on the row. */
article:has(:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 0;
}

article a:focus-visible {
  outline: none;
}

article .thumb {
  grid-area: thumb;
  align-self: start;
  width: 100%;
  aspect-ratio: 16 / 10;
  margin-top: 0.3rem;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: 6px;
  transition: border-color 0.2s;
}

article:hover .thumb {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
}

/* Stand-in for projects and games that have no image yet. */
article .thumb.placeholder {
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--line) 40%, transparent);
  color: var(--muted);
}

.thumb.placeholder .icon {
  width: 1.75rem;
  height: 1.75rem;
  opacity: 0.7;
}

article :is(h2, h3) {
  grid-area: title;
  margin: 0 0 0.25rem;
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.4;
}

article :is(h2, h3) a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 10px;
}

/* Titles look like links, and every row ends in "View", so it's clear the
   whole row opens a page, even without hover on a phone. */
article :is(h2, h3) a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

article p.view {
  grid-area: view;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--accent);
}

article .view .icon {
  transition: transform 0.2s;
}

article:hover .view .icon {
  transform: translateX(3px);
}

article p {
  grid-area: summary;
  margin: 0;
  font-size: 0.9375rem;
}

article p:has(> time) {
  grid-area: date;
  margin-bottom: 0.125rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--muted);
}

@media (min-width: 36rem) {
  article {
    margin-left: -1.25rem;
    margin-right: -1.25rem;
    padding: 1.25rem;
  }

  article:has(.thumb) {
    grid-template-columns: 9rem minmax(0, 1fr);
    grid-template-areas: "thumb date" "thumb title" "thumb summary" "thumb view";
    column-gap: 1.5rem;
  }
}

/* Detail pages */

.back {
  margin-bottom: 1rem;
  font-family: var(--mono);
  font-size: 0.75rem;
}

main .back a {
  color: var(--muted);
  text-decoration: none;
}

main .back a::before {
  content: "\2190\00a0";
}

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

figure {
  margin: 0;
}

figcaption {
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  color: var(--muted);
}

/* Images vary from square logos to wide screenshots, so they're letterboxed
   inside a panel rather than stretched. */
.hero {
  margin-bottom: 1.5rem;
}

.hero img,
.gallery img {
  width: 100%;
  object-fit: contain;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 10px;
}

.hero img {
  max-height: 28rem;
}

.lead {
  font-size: 1.125rem;
  color: var(--strong);
}

/* More images, below the facts. Each opens full size. */
.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr));
  gap: 1rem;
  margin-bottom: 1rem;
}

.gallery img {
  aspect-ratio: 16 / 10;
  transition: border-color 0.2s;
}

.gallery a:hover img {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--line));
}

.gallery a:focus-visible {
  outline: none;
}

.gallery a:focus-visible img {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.detail main > dl {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.75rem 2rem;
  margin: 2rem 0;
  padding: 1.5rem;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 10px;
}

.detail main > dl dt {
  padding-top: 0.2rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.detail main > dl dd {
  margin: 0;
  color: var(--strong);
}

@media (max-width: 30rem) {
  .detail main > dl {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }

  .detail main > dl dd {
    margin-bottom: 0.75rem;
  }

  .detail main > dl dd:last-child {
    margin-bottom: 0;
  }
}

main > h2 {
  margin-top: 2.5rem;
}

.links {
  padding: 0;
  list-style: none;
}

.links li {
  margin-bottom: 0.5rem;
}

.links a {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
  color: var(--accent);
}

.links a:hover {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* Previous / next entry, at the bottom of detail pages */

.pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin-top: 3rem;
  padding-top: 2rem;
  border-top: 1px solid var(--line);
}

.pager a {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 1rem 1.25rem;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 10px;
  transition: border-color 0.2s;
}

.pager a:hover {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--line));
}

.pager .next {
  align-items: flex-end;
  text-align: right;
}

.pager .dir {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--muted);
}

.pager .title {
  font-weight: 600;
  line-height: 1.4;
  color: var(--strong);
}

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

@media (max-width: 30rem) {
  .pager {
    grid-template-columns: minmax(0, 1fr);
  }

  .pager span:empty {
    display: none;
  }
}

/* Blog posts */

.post-date {
  margin: -0.75rem 0 2rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--muted);
}

.prose h2,
.prose h3 {
  margin: 2.5rem 0 0.75rem;
  line-height: 1.3;
}

.prose h2 {
  font-size: 1.375rem;
}

.prose h3,
.prose h4,
.prose h5,
.prose h6 {
  font-size: 1.125rem;
}

.prose ul,
.prose ol {
  padding-left: 1.5rem;
}

.prose li {
  margin-bottom: 0.25rem;
}

.prose li a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.prose img {
  margin: 1.5rem 0;
  border: 1px solid var(--line);
  border-radius: 10px;
}

.prose blockquote {
  margin: 0 0 1rem;
  padding-left: 1.25rem;
  border-left: 3px solid var(--line);
  color: var(--muted);
}

.prose hr {
  margin: 2.5rem 0;
  border: 0;
  border-top: 1px solid var(--line);
}

code {
  font-family: var(--mono);
  font-size: 0.875em;
}

.prose :not(pre) > code {
  padding: 0.1em 0.35em;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 4px;
}

.prose pre {
  margin: 0 0 1rem;
  padding: 1rem 1.25rem;
  overflow-x: auto;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 10px;
  line-height: 1.5;
}

/* Photos page: whole photos in columns, uncropped. The home page shows a
   cropped strip of the first few instead. */

.photos {
  columns: 2 16rem;
  column-gap: 1rem;
}

.photos figure {
  margin-bottom: 1rem;
  break-inside: avoid;
}

.photos img,
.photo-strip img {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 10px;
  transition: border-color 0.2s;
}

.photo-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.75rem;
}

.photo-strip img {
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 8px;
}

:is(.photos, .photo-strip) a:hover img {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--line));
}

:is(.photos, .photo-strip) a:focus-visible {
  outline: none;
}

:is(.photos, .photo-strip) a:focus-visible img {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Contact page */

.contact-list {
  display: grid;
  gap: 0.75rem;
  margin-top: 2rem;
  padding: 0;
  list-style: none;
}

/* Discord has no public profile URL, so its card is a div with no arrow. */
.contact-list a,
.contact-list div {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "icon label arrow" "icon handle arrow";
  align-items: center;
  column-gap: 1rem;
  padding: 1rem 1.25rem;
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 10px;
  transition: border-color 0.2s;
}

.contact-list a::after {
  content: "\2192";
  grid-area: arrow;
  color: var(--muted);
  transition: transform 0.2s, color 0.2s;
}

.contact-list a:hover,
.contact-list :target {
  border-color: color-mix(in srgb, var(--accent) 50%, var(--line));
}

.contact-list a:hover::after {
  color: var(--accent);
  transform: translateX(3px);
}

.contact-list .icon {
  grid-area: icon;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--accent);
}

.contact-list .label {
  grid-area: label;
  font-weight: 600;
  color: var(--strong);
}

.contact-list .handle {
  grid-area: handle;
  overflow-wrap: anywhere;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--muted);
}

/* 404 page: a cat asleep where the page should be, under the failed cd. */

.lost {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1rem 2rem;
  margin: 0 0 1.5rem;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--raise);
  font-family: var(--mono);
  font-size: 0.875rem;
}

.napping-cat {
  position: relative;
  margin: 0;
  padding-top: 1.5rem;
  font: inherit;
  font-size: 1.25rem;
  line-height: 1.25;
  color: var(--accent);
}

.napping-cat .z {
  position: absolute;
  left: 7.5ch;
  top: 1.5rem;
  color: var(--muted);
  opacity: 0;
  animation: doze 3s ease-out infinite;
}

.napping-cat .z2 {
  animation-delay: 1s;
}

.napping-cat .z3 {
  animation-delay: 2s;
}

@keyframes doze {
  0% {
    opacity: 0;
    transform: translate(0, 0) scale(0.8);
  }

  30% {
    opacity: 1;
  }

  100% {
    opacity: 0;
    transform: translate(2.5ch, -1.75rem) scale(1.2);
  }
}

.shell {
  margin: 0;
  overflow-wrap: anywhere;
  color: var(--muted);
}

.shell .prompt {
  color: var(--accent);
}

.shell .lost-path {
  color: var(--strong);
}

.shell .cursor {
  display: inline-block;
  width: 0.6em;
  height: 1.1em;
  margin-left: 0.25em;
  vertical-align: text-bottom;
  background: var(--accent);
  animation: blink 1.1s steps(1) infinite;
}

@keyframes blink {
  50% {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }

  .napping-cat .z,
  .shell .cursor {
    animation: none;
  }

  .napping-cat .z {
    opacity: 1;
  }

  .napping-cat .z2 {
    transform: translate(1.25ch, -0.875rem);
  }

  .napping-cat .z3 {
    transform: translate(2.5ch, -1.75rem);
  }
}
