* {
  box-sizing: border-box;
}

body {
  margin: 0 auto;
  max-width: 1000px;
  padding: 48px 20px 32px;
  background: #fbf8f1;
  color: #222;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  line-height: 1.4;
}

a {
  color: inherit;
}

a,
button {
  cursor: pointer;
}

/* Tiles and the logo animate their insides on hover. If the mouse hit-tests
   against those moving pieces, Chrome's cursor flickers between the hand and
   the arrow. Making the children invisible to the mouse means the link box
   itself (which never moves) is always what's under the cursor. */
.tile,
.wordmark {
  user-select: none;
  -webkit-user-drag: none;
}

.tile *,
.wordmark * {
  pointer-events: none;
}

/* header */

header {
  transition: transform 0.28s cubic-bezier(0.2, 0.8, 0.3, 1);
}

/* a card in the top row is floating up over the title, so the title moves
   up to stay readable - see main.js, which works out how far */
body.title-up header {
  transform: translateY(calc(-1 * var(--dodge, 0px)));
}

/* Scrolled down, the name lifts out of the page and becomes a bar across the
   top that everything else runs underneath.
   The header itself stays exactly where it was and keeps its height (main.js
   measures it into --header-height). That matters: if the page got shorter
   when the bar appeared, the scroll position would shift, which would put
   the header back, which would make the page taller again - and it would sit
   there flickering. */
.sentinel {
  height: 1px;
  margin-bottom: -1px;
}

/* flow-root so the tagline's top margin stays inside the header. Without it,
   the moment the name lifts out that margin escapes, pushes the header down
   six pixels, and the page changes length - which you see as the scrollbar
   twitching. */
header {
  display: flow-root;
}

body.stuck header {
  height: var(--header-height);
  transform: none;
}

body.stuck header h1 {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  padding: 9px max(20px, calc((100vw - 1000px) / 2 + 20px));
  border-bottom: 1px solid #e6e0d2;
  background: rgba(251, 248, 241, 0.92);
  backdrop-filter: blur(8px);
  font-size: 1.5rem;
}

body.stuck .intro {
  visibility: hidden;
}

@media (prefers-reduced-motion: reduce) {
  header,
  header h1 {
    transition: none;
  }
}

header h1 {
  /* deliberately no transition on the size: while the name is animating back
     into the page it would be the wrong height for a moment, and the page
     would change length under you as you scroll - a visible twitch of the
     scrollbar. It pops instead. */
  margin: 0;
  font-size: 3.2rem;
  font-weight: 800;
  letter-spacing: -0.03em;
}

header h1 a {
  text-decoration: none;
}

/* inline-block so the whole word is one solid hover area, with no gaps
   between letters where the cursor drops back to an arrow */
.wordmark {
  display: inline-block;
}

/* "Liamd8t" - the 8 ball stands in for the o */

.wordmark .ball {
  display: inline-block;
  width: 0.56em;
  height: 0.56em;
  margin: 0 0.035em 0 0.02em;
  vertical-align: -0.01em;
}

.wordmark .t {
  display: inline-block;
  transform-origin: 70% 90%;
}

/* on hover the ball rolls into the t, the t wobbles, the ball bounces back */

.wordmark.bump .ball {
  animation: ball-bump 0.7s;
}

.wordmark.bump .t {
  animation: t-wobble 0.7s;
}

@keyframes ball-bump {
  0% {
    transform: translateX(0) rotate(0);
    animation-timing-function: ease-in;
  }
  30% {
    transform: translateX(0.07em) rotate(28deg);
    animation-timing-function: ease-out;
  }
  55% {
    transform: translateX(-0.04em) rotate(-16deg);
    animation-timing-function: ease-in-out;
  }
  78% {
    transform: translateX(0.012em) rotate(5deg);
  }
  100% {
    transform: translateX(0) rotate(0);
  }
}

@keyframes t-wobble {
  0%,
  28% {
    transform: rotate(0);
  }
  42% {
    transform: rotate(9deg);
  }
  62% {
    transform: rotate(-4deg);
  }
  82% {
    transform: rotate(1.5deg);
  }
  100% {
    transform: rotate(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .wordmark.bump .ball,
  .wordmark.bump .t {
    animation: none;
  }
}

.intro {
  margin: 6px 0 0;
  color: #666;
  font-size: 1.1rem;
}

/* project grid */

.grid {
  display: grid;
  margin-top: 68px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 28px 22px;
}

.empty {
  grid-column: 1 / -1;
  margin: 24px 0;
  color: #999;
}

/* project tiles */

.tile {
  position: relative;
  display: block;
  text-decoration: none;
}

.tile-card {
  position: relative;
  isolation: isolate; /* keeps the caption's layering inside this card */
}

.thumb {
  position: relative;
  z-index: 2; /* sits over the caption printed on the page underneath */
  aspect-ratio: 1;
  border-radius: 12px;
  overflow: hidden;
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* The thumbnail floats up off the page, uncovering the text lying under it.
   --caption-height and --float are measured in main.js from the tallest
   description, so nothing ever gets cut off. */
.tile:hover .thumb,
.tile:focus-visible .thumb {
  transform: translateY(calc(-1 * var(--float, 80px))) scale(1.03) rotate(-1.5deg);
}

/* every other card tips the other way, so a row doesn't look uniform */
.tile:nth-child(even):hover .thumb,
.tile:nth-child(even):focus-visible .thumb {
  transform: translateY(calc(-1 * var(--float, 80px))) scale(1.03) rotate(1.5deg);
}

.thumb img {
  display: block;
  width: 100%;
  height: 100%;
}

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

.tile p {
  margin: 0;
  color: #777;
  font-size: 0.9rem;
}

/* On computers with a mouse, the name and description are always there,
   lying on the page in the bottom of the card's own space. The thumbnail
   covers them until it floats up. (Phones and tablets can't hover, so they
   just show them under each card as usual.) */
@media (hover: hover) {
  .tile:hover,
  .tile:focus-visible {
    z-index: 3; /* the floating card goes over its neighbours */
  }

  .caption {
    position: absolute;
    /* inside the card's own space, so it never covers the tile below */
    top: calc(100% - var(--caption-height, 68px));
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1; /* under the thumbnail */
  }

  .caption h3 {
    margin-top: 0;
  }

  .caption p {
    font-size: 0.85rem;
  }
}

/* footer */

footer {
  margin-top: 72px;
  color: #999;
  font-size: 0.9rem;
  text-align: center;
}

@media (max-width: 480px) {
  body {
    padding-top: 32px;
  }

  header h1 {
    font-size: 2.4rem;
  }

  .grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 20px 14px;
  }
}
