@font-face {
  font-family: "Aubrey";
  src: url("aubrey.woff2") format("woff2");
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Just Me Again Down Here";
  src: url("JustMeAgainDownHere.woff2") format("woff2");
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Rethink Sans";
  src: url("RethinkSans.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: "Rethink Sans Italic";
  src: url("RethinkSans-Italic.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: block;
}


* {
  box-sizing: border-box;
}

/* Force-banish the default operating system cursor across absolutely everything */
html, 
body, 
* {
  cursor: none !important;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100vh;
  color: #000000;
  cursor: none;
  overflow-x: hidden;
  background-color: #f7f5f0; 
  background-image: 
    linear-gradient(rgba(214, 232, 227, 0.5) 1px, transparent 1px),
    linear-gradient(90deg, rgba(214, 232, 227, 0.5) 1px, transparent 1px);
  background-size: 28px 28px; /* Size of the grid squares */
}

.custom-cursor {
  position: fixed;
  width: 26px;
  height: 26px;
  border: 2px solid #899FBB;
  background-color: transparent;
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%);
  z-index: 9999;
  box-sizing: border-box;

  /* NEW: Hide it by default until the mouse moves on this page */
  opacity: 0; 

  /* Add opacity to your transition string so it fades in smoothly */
  transition:
    opacity 0.2s ease,
    width 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275),
    height 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275),
    background-color 0.15s ease,
    border 0.15s ease;
}

/* Slightly scaled up the inner target dot to match the bigger ring */
.custom-cursor::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px; /* Increased from 4px */
  height: 6px; /* Increased from 4px */
  background-color: #899FBB;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  transition: background-color 0.15s ease, opacity 0.15s ease;
}

.custom-cursor.hovered {
  width: 50px; 
  height: 50px;
  border: 2px dashed #d6e8e3; 
  background-color: rgba(214, 232, 227, 0.25); 
}

.custom-cursor.hovered::before {
  opacity: 0;
}

.custom-cursor.clicked {
  width: 18px; /* Increased from 14px */
  height: 18px;
  border: 2px solid #899FBB;
  background-color: #899FBB; 
  transform: translate(-50%, -50%) scale(0.85);
}

.custom-cursor.clicked::before {
  background-color: #ffffff; 
  opacity: 1;
}

a, 
a img, 
a *,
button {
  cursor: none !important;
}

/* Header */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 110px;
  padding: 34px 40px;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  z-index: 100;
}

.logo img {
  width: 60px;
  height: auto;
  display: block;
  transition: transform 0.25s ease;
  margin-top: -17px;
}

.logo img:hover {
  transform: rotate(-3deg) scale(1.2);
}

.main-nav {
  display: flex;
  gap: 40px;
  transition: opacity 0.3s ease;
}

.main-nav a {
  font-family: "Just Me Again Down Here";
  font-size: 2.2rem;
  line-height: 1;
  color: #000000;
  text-decoration: none;
  transition: transform 0.2s ease, color 0.2s ease;
}

.main-nav a:hover {
  color: #899FBB;
  transform: rotate(-2deg) scale(1.04);
  transition: transform 0.2s ease, color 0.2s ease;
}

/* Main layout */
.archive-home {
  position: relative;
  min-height: 100vh;
}

/* Sticker / Archive view toggle — lives in the page, not the header,
   roughly aligned with the nav row so it reads as part of the same top
   band. It's positioned absolute (relative to .archive-home) rather
   than fixed, so — unlike the header — it scrolls away with the page
   instead of sticking. A sliding highlight (.view-toggle-highlight)
   animates between the two buttons; which side it sits on is driven
   purely by the same html.grid-active class that shows/hides the two
   views below, so the pill and the content it controls can never fall
   out of sync. */
.view-toggle {
  position: absolute;
  top: 36px;
  left: 50%;
  transform: translateX(-50%);
  /* Above .site-header's z-index:100 — the header is transparent here
     (nothing painted between the logo and nav), but its fixed, full-
     width box still absorbs pointer events at any z-index below it,
     so real clicks landed on the header instead of the toggle unless
     this sits higher. */
  z-index: 150;
  display: grid;
  grid-template-columns: 1fr 1fr;
  padding: 5px;
  background: #ffffff;
  border: 1.4px solid #2f3437;
  border-radius: 999px;
  box-shadow: 4px 4px 0px #d6e8e3;
}

.view-toggle-highlight {
  position: absolute;
  top: 5px;
  bottom: 5px;
  left: 5px;
  right: 50%;
  background: #2f3437;
  border-radius: 999px;
  transition: transform 0.32s cubic-bezier(0.65, 0, 0.35, 1);
}

html.grid-active .view-toggle-highlight {
  transform: translateX(100%);
}

.view-toggle-btn {
  position: relative;
  z-index: 1;
  border: none;
  background: transparent;
  padding: 9px 22px 8px;
  border-radius: 999px;
  font-family: "Aubrey", serif;
  font-size: 1.05rem;
  line-height: 1;
  white-space: nowrap;
  cursor: none;
  color: #2f3437;
  transition: color 0.25s ease;
}

.view-toggle-btn[data-view="sticker"] {
  color: #ffffff;
}

html.grid-active .view-toggle-btn[data-view="sticker"] {
  color: #2f3437;
}

html.grid-active .view-toggle-btn[data-view="grid"] {
  color: #ffffff;
}

/* Scoped with the #view-toggle id so hover always wins over the
   html.grid-active state rules above, regardless of source order. */
#view-toggle .view-toggle-btn:hover {
  color: #899FBB;
}

/* Below ~1000px, .main-nav (right-aligned in the header) has drifted
   close enough to center to run into the toggle pill (measured: they
   start touching around 950px). Drop the toggle below the nav's own
   row instead of trying to keep them side by side — the header is
   still transparent here, so this just tucks the pill under it. The
   720px mobile query below overrides this again for the shorter,
   opaque mobile header. */
@media (max-width: 1000px) {
  .view-toggle {
    top: 84px;
  }
}

.hero {
  position: relative;
  width: 100%;
  min-height: 100vh;
  padding: 120px 60px 80px;
}

/* Center title */
.center-title {
  position: absolute;
  top: 52%;
  left: 50%;
  width: min(900px, 90vw);
  transform: translate(-50%, -50%);
  text-align: center;
  z-index: 10;
}

.center-title h1 {
  margin: 0;
  font-family: "Just Me Again Down Here";
  font-size: 8.8em;
  font-weight: 400;
  line-height: 0.9;
  letter-spacing: 0.02em;
}

.center-title p {
  margin: 18px 0 0;
  font-family: "Aubrey", serif;
  font-size: 2em;
  line-height: 1.1;
}
/* 
* {
  outline: 1px solid red !important;
} */

/* Floating objects */
.object {
  position: absolute;
  display: block;
  z-index: 2;
  opacity: 0.9;
  transition:
    transform 0.25s ease,
    opacity 0.25s ease;
}

.object img {
  width: 100%;
  display: block;
}

.object:hover {
  opacity: 1;
  transform: scale(1.06) rotate(2deg);
}

/* Scatter view "deal-in" entrance — see the comment on dealStickers()
   in script.js. .deal-pending is in the HTML from the start (so
   there's no flash of the final layout before JS measures each
   sticker's position), and script.js swaps it for .dealing with the
   --deal-* custom properties filled in per element, then removes
   .dealing once the animation finishes so the sticker goes back to
   being governed purely by the rules above/below. */
.object.deal-pending {
  opacity: 0;
}

.object.dealing {
  animation: sticker-deal 0.65s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  animation-delay: var(--deal-delay, 0s);
}

@keyframes sticker-deal {
  from {
    transform: translate(var(--deal-x), var(--deal-y)) scale(0.2) rotate(0deg);
    opacity: 0;
  }
  to {
    transform: translate(0, 0) scale(1) rotate(var(--deal-rot));
    opacity: 0.9;
  }
}

@media (prefers-reduced-motion: reduce) {
  .object.deal-pending {
    opacity: 0.9;
  }
}

/* Individual placements — widths are clamp(min, vw, max) rather than
   a flat px value: the vw term is each sticker's original px sized as
   a share of a 1440px canvas, so above ~1440px it holds at its
   original size (the max), and below that it shrinks in proportion
   to the viewport instead of staying fixed-size while the canvas
   around it (and the % positions below) keep shrinking — that
   mismatch was what piled stickers on top of each other/the title
   well before the phone-width layout kicked in. The min floor is a
   backstop for this fluid range only; the mobile media query below
   720px replaces these entirely with its own stacked sizing. */
.object-1 {
  width: clamp(160px, 20.14vw, 290px);
  top: 12%;
  left: 5%;
  transform: rotate(-10deg);
}

.object-2 {
  width: clamp(95px, 11.81vw, 170px);
  top: 14%;
  right: 45%;
  transform: rotate(10deg);
}

/* design */
.object-11 {
  width: clamp(75px, 9.38vw, 135px);
  top: 16%;
  right: 31%;
  transform: rotate(-6deg);
}

.object-3 {
  width: clamp(135px, 16.67vw, 240px);
  top: 12%;
  right: 10%;
  transform: rotate(17deg);
}

.object-4 {
  width: clamp(80px, 9.72vw, 140px);
  top: 14%;
  left: 29%;
  transform: rotate(-10deg);
}

.object-5 {
  width: clamp(185px, 22.92vw, 330px);
  top: 56%;
  right: 4%;
  transform: rotate(9deg);
}

/* dice */
.object-6 {
  width: clamp(40px, 4.86vw, 70px);
  top: 58%;
  right: 27%;
  transform: rotate(-12deg);
  z-index:100;
}

.object-7 {
  width: clamp(260px, 31.94vw, 460px);
  bottom: 6%;
  left: 5%;
  transform: rotate(9deg);
}

.object-9 {
  width: clamp(85px, 10.42vw, 150px);
  bottom: 10%;
  left: 38%;
  transform: rotate(-10deg);
}

.object-10 {
  width: clamp(95px, 11.81vw, 170px);
  bottom: 6%;
  right: 37%;
  transform: rotate(8deg);
}

/* gdm */
.object-12 {
  width: clamp(48px, 5.9vw, 85px);
  bottom: 4%;
  right: 29%;
  transform: rotate(-8deg);
}

/* Below ~1000px the toggle drops to top:84px (see the .view-toggle
   media query above), pushing its bottom edge to ~130px — past where
   these top-row stickers' percentage-based `top` would otherwise land
   on a shorter viewport. max() keeps each sticker's normal
   proportional position on tall viewports but puts a hard floor under
   it so it can never sit above the toggle. Needs to come after the
   base .object-N rules above to win the cascade. */
@media (max-width: 1000px) {
  .object-1,
  .object-3 {
    top: max(12%, 158px);
  }

  .object-2,
  .object-4 {
    top: max(14%, 172px);
  }

  .object-11 {
    top: max(16%, 184px);
  }
}

/* Hover/focus tag on the reroll stickers (dice, art palette, game
   controller) — a quick hint that the sticker links somewhere new each
   visit, since that isn't obvious from the icon alone. */
.reroll-tag {
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translate(-50%, 4px);
  margin-bottom: 10px;
  padding: 6px 14px;
  background: #f7f5f0;
  color: #2f3437;
  border: 1.3px solid #2f3437;
  border-radius: 999px;
  font-family: "Aubrey", serif;
  font-size: 0.95rem;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 5;
}

.reroll-trigger:hover .reroll-tag,
.reroll-trigger:focus-visible .reroll-tag {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Make sticker buttons look like your original sticker links */
.project-trigger {
  border: none;
  padding: 0;
  background: transparent;
  cursor: none;
}

/* Lock page when modal is open */
body.modal-open {
  overflow: hidden;
}

/* Project modal */
.project-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.project-modal.active {
  opacity: 1;
  pointer-events: auto;
}

.project-modal-overlay {
  position: absolute;
  inset: 0;
  background: rgba(245, 248, 250, 0.72);
  backdrop-filter: blur(3px);
}

/* Main project card */
.project-card {
  position: relative;
  width: min(820px, 92vw);
  min-height: 360px;
  background: #d6e8e3;
  border: 1.5px solid #2f3437;
  border-radius: 22px;
  box-shadow: 14px 16px 24px rgba(0, 0, 0, 0.22);
  transform: scale(0.96) rotate(-1deg);
  opacity: 0;
  transition:
    transform 0.24s ease,
    opacity 0.24s ease;
  z-index: 1001;
}

.project-modal.active .project-card {
  transform: scale(1) rotate(-1deg);
  opacity: 1;
}

/* Close button */
.project-close {
  position: absolute;
  top: 16px;
  right: 27px;
  width: 34px;
  height: 34px;
  border: none;
  background: transparent;
  font-family: "aubrey";
  font-size: 2.9rem;
  line-height: 0.8;
  color: #2f3437;
  cursor: none;
  transition: transform 0.18s ease, color 0.18s ease;
  z-index: 2;
}

.project-close:hover {
  color: #899FBB;
  transform: rotate(6deg) scale(1.12);
}

/* Inner layout */
.project-card-inner {
  display: grid;
  grid-template-columns: 38% 1fr;
  gap: 32px;
  padding: 48px 46px 38px;
  align-items: center;
}

/* Left image / gif area */
.project-preview {
  width: 100%;
  /*aspect-ratio: 1 / 1;*/
  height:300px;
  background: #ffffff;
  border-radius: 16px;
  overflow: hidden;
  border: 1.5px solid rgba(47, 52, 55, 0.75);
}

.project-preview img,
.project-preview video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  opacity: 0;
  transition: opacity 0.15s ease;
}

.project-preview img.loaded,
.project-preview video.loaded {
  opacity: 1;
}

/* Right info */
.project-info {
  padding-right: 12px;
}

.project-file-label {
  margin: 0 0 6px;
  font-family: "Aubrey", serif;
  font-size: 1rem;
  letter-spacing: 0.08em;
  color: #4f687f;
  text-transform: uppercase;
}

.project-info h2 {
  margin: 0;
  font-family: "Just Me Again Down Here";
  font-size: 4rem;
  font-weight: 400;
  line-height: 0.86;
}

.project-category {
  margin: 8px 0 18px;
  display: inline-block;
  padding: 8px 15px;
  background: #ffffff;
  border: 1.3px solid #2f3437;
  border-radius: 999px;
  font-family: "Aubrey", serif;
  font-size: 1.1rem;
  line-height: 1;
}

.project-summary {
  margin: 0 0 10px;
  font-family: "Rethink Sans", serif;
  font-size: 1.2rem;
  font-weight:100;
  line-height: 1.16;
}

.project-tools {
  margin: 0 0 16px 0;
  /* font-family: "Aubrey", serif; */
  font-family: "Just Me Again Down Here", cursive;
  font-size: 1.7rem;
  line-height: 1;
  color: #2f3437;
}

/* Buttons */
.project-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.project-action-btn {
  display: inline-block;
  padding: 8px 18px 7px;
  background: #ffffff;
  border: 1.4px solid #2f3437;
  border-radius: 999px;
  font-family: "Aubrey", cursive;
  font-size: 1.25rem;
  line-height: 1;
  color: #000000;
  text-decoration: none;
  cursor: none;
  transition:
    transform 0.18s ease,
    background-color 0.18s ease,
    color 0.18s ease;
}

.project-action-btn:hover {
  background: #899FBB;
  color: #ffffff;
  transform:scale(1.08);
}

.project-action-btn.play-btn {
  background: black;
  color: #ffffff;
}

.project-action-btn.play-btn:hover {
  background: #899FBB;
}

/* ============================================================
   PROJECTS SECTION (home page — hover-only cards, no click-open
   modal, distinct from the sticker interaction above it. A tile
   is either a real link to a built page, or an .is-placeholder
   div for a project that doesn't exist yet.)
   ============================================================ */

/* Sticker view / Archive (grid) view — the two interchangeable ways
   to browse the same home page content, switched by the view-toggle
   pill above, the header nav's Work link, or the logo/title. Only one
   is ever in the document flow at a time; html.grid-active is the
   single source of truth both here and on .view-toggle above.
   Switching is choreographed from script.js as a quick fade out/in
   (opacity only — the transition below is what that animates) rather
   than an instant display swap. */
#sticker-view,
.grid-view {
  opacity: 1;
  transition: opacity 0.28s ease;
}

html.grid-active #sticker-view {
  display: none;
}

.grid-view {
  display: none;
  position: relative;
  z-index: 1;
  width: min(86vw, 1320px);
  margin: 0 auto 130px;
  padding-top: 110px;
}

html.grid-active .grid-view {
  display: block;
}

/* Same reasoning as .hero's padding-top: the grid view's title was
   sized to clear a header-height toggle, not the lower position it
   drops to once the toggle/nav collision fix below ~1000px kicks in
   (see the .view-toggle media query above .object-1). */
@media (max-width: 1000px) {
  .grid-view {
    padding-top: 150px;
  }
}

.grid-view-header {
  text-align: center;
  margin-bottom: 54px;
}

.grid-view-title-link {
  display: inline-block;
  text-decoration: none;
  color: inherit;
  cursor: none;
}

.grid-view-title {
  margin: 0 0 15px;
  font-family: "Just Me Again Down Here", cursive;
  font-weight: 400;
  font-size: clamp(4.2em, 7vw, 7.8em);
  line-height: 0.95;
  text-align: center;
  transition: color 0.2s ease, transform 0.2s ease;
}

.grid-view-title-link:hover .grid-view-title {
  color: #899FBB;
  transform: rotate(-1deg) scale(1.02);
}

.grid-view-subtitle {
  margin: 0;
  font-family: "Aubrey", serif;
  font-size: 1.8em;
  line-height: 1.1;
  text-align: center;
}

.project-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 52px 44px;
}

@media (max-width: 1100px) {
  .project-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.project-tile {
  position: relative;
  display: block;
  aspect-ratio: 16 / 10;
  background: #ffffff;
  border: 1.5px solid #2f3437;
  border-radius: 4px 14px 3px 12px / 12px 3px 14px 4px;
  box-shadow: 6px 6px 0px #d6e8e3;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  cursor: none;
  transform: rotate(-0.6deg);
  transition: transform 0.22s ease, box-shadow 0.22s ease;
}

.project-grid > *:nth-child(3n+2) {
  transform: rotate(0.7deg);
}

.project-grid > *:nth-child(3n) {
  transform: rotate(-1.1deg);
}

.project-tile:hover,
.project-tile:focus-visible {
  transform: rotate(0deg) scale(1.035) translateY(-3px);
  box-shadow: 9px 9px 0px #d6e8e3;
  z-index: 2;
}

.project-tile-media {
  position: absolute;
  inset: 0;
  background: #eef3f6;
}

.project-tile-media img,
.project-tile-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.project-tile-caption {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 4px;
  padding: 16px 18px;
  background: linear-gradient(
    to top,
    rgba(20, 22, 24, 0.82) 0%,
    rgba(20, 22, 24, 0.35) 55%,
    rgba(20, 22, 24, 0) 100%
  );
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 0.22s ease, transform 0.22s ease;
}

.project-tile:hover .project-tile-caption,
.project-tile:focus-visible .project-tile-caption {
  opacity: 1;
  transform: translateY(0);
}

.tile-name {
  font-family: "Just Me Again Down Here", cursive;
  font-size: 1.7rem;
  line-height: 1.05;
  color: #f7f5f0;
}

.tile-meta {
  font-family: "Courier New", Courier, monospace;
  font-size: 0.76rem;
  font-weight: bold;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #d6e8e3;
}

/* Placeholder tiles — not-yet-built projects. Same hover mechanics,
   just visually inert (muted diagonal fill, no image) so it reads
   as "reserved" rather than broken. */
.project-tile.is-placeholder {
  cursor: default;
  background: repeating-linear-gradient(
    135deg,
    #e6e6e2,
    #e6e6e2 10px,
    #dcdcd7 10px,
    #dcdcd7 20px
  );
  border-style: dashed;
  box-shadow: 6px 6px 0px rgba(137, 159, 187, 0.35);
}

.project-tile.is-placeholder:hover,
.project-tile.is-placeholder:focus-visible {
  transform: rotate(0deg) scale(1.015);
  box-shadow: 6px 6px 0px rgba(137, 159, 187, 0.35);
}

.project-tile.is-placeholder .project-tile-caption {
  background: linear-gradient(
    to top,
    rgba(47, 52, 55, 0.7) 0%,
    rgba(47, 52, 55, 0.18) 60%,
    rgba(47, 52, 55, 0) 100%
  );
}

.project-tile.is-placeholder .tile-name {
  color: #2f3437;
}

.project-tile.is-placeholder .tile-meta {
  color: #4f687f;
}

/* WIP tile — a real preview image for a project that's underway but
   isn't linked anywhere yet (a plain <div>, not <a>). It hovers like
   any other tile (lift, shadow, caption + badge reveal all inherited
   from the shared .project-tile rules) so it still reads as "look, a
   preview" — it just isn't a link underneath, so nothing happens on
   click and the custom cursor's hover state (script.js only watches
   <a>/<button>) never kicks in either. */
.tile-action-wip {
  background: #899FBB;
  color: #ffffff;
  border: 1.3px solid #2f3437;
}

/* ============================================================
   SKETCHBOOK PAGE (flat, uniform grid — every tile the same
   width and height, no ranking, no category headers. Same
   hover-caption mechanics as the Projects grid above, just
   without the rotation/wobble — this page is meant to read as
   a plainer, more contact-sheet-like index.)
   ============================================================ */

.sketchbook-page {
  /* Tuned so .sketchbook-hero-title lines up with .about-title — this
     page has an extra kicker line above the title that about.html
     doesn't, so it needs less top padding to land at the same spot,
     plus a little extra clearance so the kicker doesn't crowd the
     fixed header/nav above it. */
  padding-top: 56px;
}

.sketchbook-hero {
  width: min(760px, 88vw);
  margin: 0 auto;
  padding: 10px 0 6px;
  text-align: center;
  /* Already on screen at load, so it plays once immediately rather
     than waiting on a scroll trigger — same treatment as .project-hero
     on individual project pages. */
  animation: hero-rise 0.8s ease both;
}

.section-kicker-hand {
  margin: 0 0 4px;
  font-family: "Just Me Again Down Here", cursive;
  font-size: 1.1rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #899FBB;
}

.sketchbook-hero-title {
  margin: 0;
  font-family: "Just Me Again Down Here", cursive;
  font-weight: 400;
  /* Standardized with .grid-view-title / .about-title — see the note
     on .about-title. */
  font-size: clamp(4.2em, 7vw, 7.8em);
  line-height: 0.95;
}

.sketchbook-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  width: min(84vw, 1360px);
  margin: 46px auto 110px;
}

@media (max-width: 1100px) {
  .sketchbook-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.sketchbook-tile {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: #ffffff;
  border: none;
  padding: 0;
  font: inherit;
  text-align: left;
  text-decoration: none;
  color: inherit;
  cursor: none;
  transition: opacity 0.7s ease, transform 0.7s ease;
}

/* script.js adds/removes this the same way it does for .wall-tile on
   gallery pages — each tile rises into place the first time it
   scrolls into view, rather than the whole grid dumping on screen
   at once. */
.sketchbook-tile.reveal-pending {
  opacity: 0;
  transform: translateY(28px);
}

.sketchbook-tile-media {
  position: absolute;
  inset: 0;
}

.sketchbook-tile-media img,
.sketchbook-tile-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.sketchbook-tile-caption {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 3px;
  padding: 14px 16px;
  background: linear-gradient(
    to top,
    rgba(20, 22, 24, 0.82) 0%,
    rgba(20, 22, 24, 0.3) 55%,
    rgba(20, 22, 24, 0) 100%
  );
  opacity: 0;
  transition: opacity 0.2s ease;
}

.sketchbook-tile:hover .sketchbook-tile-caption,
.sketchbook-tile:focus-visible .sketchbook-tile-caption {
  opacity: 1;
}

.sketchbook-tile .tile-name {
  font-size: 1.35rem;
}

.tile-desc {
  font-family: "Rethink Sans", serif;
  font-size: 0.82rem;
  line-height: 1.35;
  color: #f7f5f0;
  opacity: 0.9;
}

.sketchbook-tile.is-placeholder {
  cursor: default;
  background: repeating-linear-gradient(
    135deg,
    #e6e6e2,
    #e6e6e2 10px,
    #dcdcd7 10px,
    #dcdcd7 20px
  );
}

.sketchbook-tile.is-placeholder .sketchbook-tile-caption {
  background: linear-gradient(
    to top,
    rgba(47, 52, 55, 0.68) 0%,
    rgba(47, 52, 55, 0.15) 60%,
    rgba(47, 52, 55, 0) 100%
  );
}

.sketchbook-tile.is-placeholder .tile-name {
  color: #2f3437;
}

.sketchbook-tile.is-placeholder .tile-meta {
  color: #4f687f;
}

/* Action badge — tells you what clicking through actually does before
   you click: straight into a playable game, or through to a full
   written case-file page. Sits opposite corner from nothing in
   particular, fades in with the caption rather than living inside it,
   since it's a different kind of information (destination, not
   metadata). */
.tile-action {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: 5px 12px;
  font-family: "Courier New", Courier, monospace;
  font-size: 0.7rem;
  font-weight: bold;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border-radius: 999px;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.2s ease, transform 0.2s ease;
  pointer-events: none;
}

.sketchbook-tile:hover .tile-action,
.sketchbook-tile:focus-visible .tile-action,
.project-tile:hover .tile-action,
.project-tile:focus-visible .tile-action {
  opacity: 1;
  transform: translateY(0);
}

.tile-action-play {
  background: #899FBB;
  color: #ffffff;
  border: 1.3px solid #2f3437;
}

.tile-action-case,
.tile-action-gallery,
.tile-action-read {
  background: #f7f5f0;
  color: #2f3437;
  border: 1.3px solid #2f3437;
}

/* ABOUT PAGE */

.logo {
  display: flex;
  align-items: center;
  gap: 4px;
  text-decoration: none;
  color: #000;
}

.logo img {
  width: 60px;
  height: auto;
}

.logo span {
  font-family: "Just Me Again Down Here", cursive;
  font-size: 2.2rem;
  line-height: 1;
  margin-top:-10px;
  transition: transform 0.2s ease, color 0.2s ease, opacity 0.3s ease;
}

.logo span:hover{
  color: #899FBB;
  transform: rotate(-2deg) scale(1.04);
  transition: transform 0.2s ease, color 0.2s ease;
}

.about-page {
  width: min(84vw, 1180px);
  margin: 0 auto;
  padding: 80px 0 70px;
}

.about-title {
  font-family: "Just Me Again Down Here", cursive;
  /* Standardized with .grid-view-title / .sketchbook-hero-title so all
     three page titles share one size scale — this one used to be a
     fixed 7.8em with no responsive clamp at all, which is why it broke
     so much worse than the other two on narrow screens. */
  font-size: clamp(4.2em, 7vw, 7.8em);
  font-weight: 400;
  line-height: 0.95;
  text-align: center;
  margin: 0 0 15px;
  /* Already on screen at load, so it plays once immediately rather
     than waiting on a scroll trigger — same treatment as .project-hero
     and .sketchbook-hero. */
  animation: hero-rise 0.8s ease both;
}

.about-intro {
  display: grid;
  grid-template-columns: 1.25fr 0.75fr;
  gap: 30px;
  align-items: start;
}

/* Rise-up entrance for the About page's content blocks — the same
   fade-and-rise treatment .project-section uses on individual project
   pages, scoped here via the .about-reveal marker (added in the HTML)
   so it doesn't touch anything outside this page. Read/toggled by the
   same reveal observer in script.js. */
.about-reveal {
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.about-reveal.reveal-pending {
  opacity: 0;
  transform: translateY(28px);
}

.about-blurb {
  font-family: "Rethink Sans", serif;
  font-size: 1.2rem;
  line-height: 1.45;
  max-width: 620px;
}

.about-blurb p {
  margin: 0 0 25px;
}

.about-image {
  width: 100%;
  height: auto;
  background-color: transparent;
  transform: rotate(1.5deg);
}

.about-image img {
  width: 100%;
  height: 100%;
  min-height: 430px;
  object-fit: cover;
  display: block;
  /* This mathematical wizardry makes the box edges slightly wobbly/hand-drawn */
  border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px;
  
  /* Thick sketched outline using your main blue */
  border: 4px solid #899FBB;
  
  /* A secondary "offset" sketch line in mint to look like a double-drawn outline */
  box-shadow: 6px 6px 0px #d6e8e3; 
}

.contact-box {
  margin-top: 35px;
  max-width: 520px; 
}

.contact-box h2 {
  font-family: "Rethink Sans", serif;
  font-size: 1.7em;
  font-weight: 700;
  margin: 0 0 18px;
  color: #000;
}

.contact-links {
  display: flex;
  flex-direction: column;
  align-items: flex-start; 
  gap: 14px;
}

.social-sub-row {
  display: flex;
  flex-direction: row;
  gap: 14px; 
}

.contact-links a {
  /* Using a typewriter font stack for that stamped archive/metadata system look */
  font-family: "Courier New", Courier, monospace;
  font-size: 1.1rem; 
  font-weight: bold;
  text-decoration: none;
  
  /* Palette inversion: Mint background with Blue text/borders */
  color: rgb(53, 46, 46);
  background-color: #d6e8e3;
  
  padding: 8px 16px;
  display: inline-block;
  
  /* Matches the subtle ink-pen wobble from your photo frame */
  border: 2px solid #899FBB;
  border-radius: 2px 4px 1px 3px / 3px 1px 4px 2px;
  
  /* A sharp, flat 3D paper shadow using your main blue */
  box-shadow: 4px 4px 0px #899FBB;
  
  /* Smooth transition for the "button press" effect */
  transition: transform 0.1s ease, box-shadow 0.1s ease;
}

/* The interactive "stamped down" hover effect */
.contact-links a:hover {
  background-color: #c9ded8; /* Slightly darker mint on hover */
  color: #7289a6;            /* Deepens the blue slightly */
  
  /* Moves the button down and right, exactly matching the shadow size */
  transform: translate(4px, 4px); 
  box-shadow: 0px 0px 0px #899FBB; /* Absorbs the shadow to feel like a real click */
}
/* The Full-Width Blue Band Container */
.outside-work-strip {
  /* background-color: #899FBB; */
  width: 100%;
  overflow: hidden;
}

/* Header styling inside the band */
.strip-header {
  padding: 0 40px;
  margin-bottom: 30px;
}

.strip-header h2 {
  font-family: "Rethink Sans", serif;
  font-size: 2rem;
  font-weight: 700;
  color: black;
  text-align:center;
}

/* The Horizontal Scroll Track */
.horizontal-scroll-track {
  display: flex;
  align-items: center;
  overflow-x: auto;
  padding: 20px 40px 40px 40px;
  gap: 30px;
  -webkit-overflow-scrolling: touch; 
  
  scrollbar-width: none; 
  -ms-overflow-style: none;  
}

/* HIDES SCROLLBARS: Chrome, Safari, and Opera */
.horizontal-scroll-track::-webkit-scrollbar {
  display: none;
}

/* Base rules for elements inside the track */
.scroll-card {
  flex-shrink: 0;
}

/* TYPE 1: The Index Card Text Blocks */
.scroll-card.text-card {
  background-color: #899FBB;
  color: white;
  width: 290px;
  height: 320px;
  padding: 25px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  border-radius: 4px 15px 3px 10px / 10px 4px 12px 3px;
  box-shadow: 8px 8px 0px #d6e8e3;
  transform: rotate(-1deg);
  transition: transform 0.2s ease;
}

.scroll-card.text-card h3 {
  font-family: "Just Me Again Down Here", serif;
  /* font-size: 1.5rem; */
  font-size:2.7em;
  font-weight:200;
  /* font-weight: 700; */
  margin: 0 0 10px 0;
}

.scroll-card.text-card p {
  font-family: "Rethink Sans", serif;
  font-size: 0.95rem;
  line-height: 1.5;
  margin: 0;
}

.card-tag {
  font-family: "Courier New", Courier, monospace;
  font-size: 0.8rem;
  font-weight: bold;
  color: #d6e8e3;
  margin-bottom: auto;
}
/* TYPE 2: Static Polaroid Snapshots (Hover effect completely removed) */
.scroll-card.photo-card {
  background-color: #ffffff;
  width: 260px;
  height: 320px; 
  object-fit: cover;
  display: block;
  
  /* Thick white border like a physical photo print */
  border: 12px solid #ffffff; 
  /* A very soft, realistic paper shadow */
  box-shadow: 4px 6px 15px rgba(0, 0, 0, 0.1);
}

.scroll-card.photo-card:nth-of-type(even) {
  transform: rotate(-2deg) translateY(5px);
}

.scroll-card.photo-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 1px solid rgba(0,0,0,0.05);
}

.category-divider {
  margin-left: 50px;
}
/* Container Spacing */
.skills-section {
  margin-top: 80px;
  width: 100%;
  box-sizing: border-box;
}

.skills-section h2 {
  font-family: "Rethink Sans", serif;
  font-size: 2rem;
  font-weight: 700;
  margin-bottom: 30px;
  color: #000;
}

/* Responsive Layout Grid */
.skills-manifest-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 30px;
}

/* Individual Inventory Boxes (Styled exactly like your contact links!) */
.manifest-card {
  /* Filled with green/mint, using the dark text color from your contact code */
  background-color: #d6e8e3; 
  color: rgb(53, 46, 46);
  
  padding: 30px 25px;
  position: relative;
  box-sizing: border-box;
  
  /* Matches the exact ink-pen wobble from your contact buttons */
  border: 2px solid #899FBB;
  border-radius: 2px 4px 1px 3px / 3px 1px 4px 2px;
  
  /* A sharp, flat 3D paper shadow using your main blue */
  box-shadow: 6px 6px 0px #899FBB;
  
  /* Loose paper organic rotations */
  transform: rotate(-0.5deg);
}

/* Retaining the subtle dynamic layout tilts */
.manifest-card:nth-child(2) {
  transform: rotate(1deg);
}
.manifest-card:nth-child(3) {
  transform: rotate(-1deg);
}

.manifest-card h3 {
  font-family: "Rethink Sans", serif;
  font-size: 1.5rem;
  font-weight: 700;
  margin: 0 0 20px 0;
  color: rgb(53, 46, 46);
  /* Perforated notebook divider line */
  border-bottom: 1px dashed rgba(137, 159, 187, 0.6); 
  padding-bottom: 10px;
}

/* Tag Flex Container */
.tag-container {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* The Individual Skill Tags (Crisp white labels) */
.skill-tag {
  font-family: "Courier New", Courier, monospace;
  font-size: 0.9rem;
  font-weight: bold;
  color: rgb(53, 46, 46);
  
  /* Whiter color tag fill to stand out beautifully on the mint canvas */
  background-color: #ffffff; 
  border: 1px solid #899FBB;
  
  padding: 5px 12px;
  display: inline-block;
  
  /* Mini micro-wobble edges */
  border-radius: 3px 6px 2px 5px / 4px 2px 5px 3px;
  
  /* A subtle micro-shadow to make the white labels look layered on top of the card */
  box-shadow: 2px 2px 0px rgba(137, 159, 187, 0.4);
}

/* ============================================================
   INDIVIDUAL PROJECT PAGE ("case file" template)
   Reusable across every project page (mediaWorks, orbitErrands, etc).
   Colors are driven by CSS custom properties set on .project-page,
   so each project can retheme itself without touching these rules —
   see the [data-theme] block in each project's own stylesheet.
   ============================================================ */

.project-page {
  --proj-accent: #899FBB;      /* primary line/tab color */
  --proj-accent-2: #d6e8e3;    /* secondary / fill color */
  --proj-accent-soft: #eef3f6; /* pale tint for backgrounds */
  --proj-ink: #2f3437;

  padding-top: 110px;
}

/* ---- Hero ---- */
.project-hero {
  width: min(92vw, 1480px);
  margin: 0 auto;
  padding: 46px 0 20px;
}

/* Hero: text (kicker/title/tagline/meta — the "title card") on the
   left, a supporting image on the right, matched to the title card's
   own height. Flexbox (not grid) on purpose: align-items:stretch
   here is simple and predictable — the row's height comes from the
   title card's real content, and the image stretches to meet it,
   the same proven pattern as the .figure-pair component. Stacks —
   image below text — on small screens. */
.project-hero-grid {
  display: flex;
  gap: 15px;
  align-items: stretch;
}

.project-hero-text {
  flex: 1.3 1 0%;
  min-width: 0;
}

/* A placeholder-friendly image box — sized for a good-looking crop
   rather than needing to match any particular photo's own ratio. */
.project-hero-figure {
  display: flex;
  flex: 1 1 240px;
  min-width: 0;
  margin: 0;
  border: 1.5px solid var(--proj-ink);
  border-radius: 18px;
  overflow: hidden;
  background: var(--proj-accent-soft);
  box-shadow: 8px 8px 0px var(--proj-accent-2);
}

.project-hero-figure img {
  display: block;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  object-fit: cover;
}

.project-kicker {
  margin: 0 0 8px;
  font-family: "Aubrey", serif;
  font-size: 1.05rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--proj-accent);
}

.project-hero-title {
  margin: 0;
  font-family: "Just Me Again Down Here", cursive;
  font-weight: 400;
  /* Scales with the column instead of a fixed size, so the title
     never outgrows the (now narrower, shared) text column and forces
     an overflow or an ugly mid-word wrap as the layout shrinks. The
     vw figure is calibrated against the text column's actual share
     of the hero width (~55%), not the full viewport. */
  font-size: clamp(2.2em, 5.6vw, 6.4em);
  line-height: 0.9;
  overflow-wrap: anywhere;
}

.project-hero-title span {
  color: var(--proj-accent);
}

.project-hero-tagline {
  max-width: 640px;
  margin: 18px 0 0;
  font-family: "Rethink Sans", serif;
  font-size: 1.35rem;
  line-height: 1.4;
}

.project-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 34px;
  margin: 30px 0 0;
}

.project-meta div {
  min-width: 140px;
}

.project-meta dt {
  font-family: "Courier New", Courier, monospace;
  font-size: 0.75rem;
  font-weight: bold;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--proj-accent);
  margin-bottom: 3px;
}

.project-meta dd {
  margin: 0;
  font-family: "Rethink Sans", serif;
  font-size: 1rem;
  line-height: 1.3;
}

.project-hero-media {
  margin-top: 34px;
  width: 100%;
  border: 1.5px solid var(--proj-ink);
  border-radius: 18px;
  overflow: hidden;
  background: var(--proj-accent-soft);
  box-shadow: 8px 8px 0px var(--proj-accent-2);
}

.project-hero-media video,
.project-hero-media img {
  display: block;
  width: 100%;
  height: auto;
}

/* ---- Layout: sticky sidebar + content column ---- */
.project-layout {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  align-items: start;
  gap: 50px;
  width: min(92vw, 1480px);
  margin: 40px auto 0;
  padding-bottom: 100px;
}

.project-sidebar {
  position: sticky;
  top: 130px;
}

.project-sidebar-label {
  margin: 0 0 10px 20px;
  font-family: "Courier New", Courier, monospace;
  font-size: 0.72rem;
  font-weight: bold;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--proj-ink);
  opacity: 0.5;
}

.project-nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 13px;
}

.project-nav-tab {
  position: relative;
  display: inline-block;
  padding-left: 20px;
  font-family: "Aubrey", serif;
  font-size: 1.1rem;
  line-height: 1.3;
  color:black;
  text-decoration: none;
  opacity: 0.6;
  transform-origin: left center;
  transition: opacity 0.35s ease, color 0.25s ease, transform 0.2s ease;
}

.project-nav-tab::before {
  content: "";
  position: absolute;
  left: 1px;
  top: 0.55em;
  width: 6px;
  height: 6px;
  background: var(--proj-accent);
  transform: translateY(-50%) rotate(45deg);
  opacity: 0.7;
  transition: opacity 0.35s ease;
}

.project-nav-tab:hover {
  opacity: 0.85;
  transform: translateX(5px) scale(1.05);
}

.project-nav-tab.active {
  opacity: 1;
  color: var(--proj-accent);
  transform: translateX(5px) scale(1.05);
}

.project-nav-tab.active::before {
  opacity: 1;
}

/* ---- Content column ---- */
.project-main {
  min-width: 0;
}

.project-section {
  padding: 20px 0 15px;
  border-bottom: 1px dashed rgba(47, 52, 55, 0.18);
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.project-section:last-child {
  border-bottom: none;
}

/* script.js adds this class right before observing each section, then
   removes it once the section scrolls into view — so if JS never
   runs (blocked, errors, disabled), sections simply never get hidden
   in the first place and the page reads normally. */
.project-section.reveal-pending {
  opacity: 0;
  transform: translateY(28px);
}

/* A softer, quieter version of the same entrance for the hero —
   it's already on screen at load, so it plays once immediately
   rather than waiting on a scroll trigger. */
.project-hero {
  animation: hero-rise 0.8s ease both;
}

@keyframes hero-rise {
  from {
    opacity: 0;
    transform: translateY(18px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .project-section,
  .project-hero,
  .wall-hero,
  .wall-section,
  .wall-tile,
  .about-title,
  .sketchbook-hero {
    animation: none !important;
    transition: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

.section-kicker {
  display: inline-block;
  margin: 0 0 10px;
  padding: 5px 13px;
  font-family: "Aubrey", serif;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--proj-ink);
  background: var(--proj-accent-soft);
  border: 1px solid gray;
  border-radius: 999px;
}

.project-section h2 {
  margin: 5px 0 18px;
  font-family: "Just Me Again Down Here", serif;
  font-weight: 400;
  font-size: 2.7em;
  line-height: 1.05;
  letter-spacing: 0.01em;
}

.project-section p {
  max-width: min(100%, 860px);
  margin: 0 0 16px;
  font-family: "Rethink Sans", serif;
  font-size: 1.08rem;
  line-height: 1.55;
}

.project-section p:last-of-type {
  margin-bottom: 0;
}

.project-section ul,
.project-section ol {
  max-width: min(100%, 860px);
  margin: 0 0 16px;
  padding-left: 1.3em;
  font-family: "Rethink Sans", serif;
  font-size: 1.08rem;
  line-height: 1.55;
}

.project-section li {
  margin-bottom: 6px;
}

.project-section li:last-child {
  margin-bottom: 0;
}

.project-section ul:last-of-type,
.project-section ol:last-of-type {
  margin-bottom: 0;
}

/* ---- Text beside a smaller supporting figure ---- */
.text-with-figure {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(200px, 280px);
  gap: 34px;
  align-items: start;
}

.text-with-figure .project-figure {
  margin: 4px 0 0;
}

/* ---- Pull quote ---- */
.project-pullquote {
  max-width: min(100%, 680px);
  margin: 28px 0;
  padding-left: 22px;
  border-left: 3px solid var(--proj-accent);
  font-family: "Just Me Again Down Here", cursive;
  font-weight: 400;
  font-size: 2em;
  line-height: 1.15;
  color: var(--proj-ink);
}

/* ---- Color palette swatches ---- */
.palette-row {
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
  margin: 22px 0 8px;
}

.palette-swatch {
  width: 108px;
  text-align: center;
}

.palette-swatch .swatch-block {
  display: block;
  width: 100%;
  height: 88px;
  border: 1.4px solid var(--proj-ink);
  border-radius: 4px 12px 3px 10px / 10px 3px 12px 4px;
  box-shadow: 4px 4px 0px rgba(47, 52, 55, 0.15);
}

.palette-swatch .swatch-label {
  display: block;
  margin-top: 8px;
  font-family: "Courier New", Courier, monospace;
  font-size: 0.78rem;
  font-weight: bold;
  color: var(--proj-ink);
}

/* ---- Horizontal photo scroller (reusable across all project pages) ----
   Every image in a row shares one height and keeps its own natural
   aspect ratio (width: auto) — portrait, landscape, and square photos
   can sit side by side without ever being cropped. */
.photo-scroll {
  display: flex;
  align-items: flex-end;
  gap: 28px;
  margin: 10px 0 2px;
  /* overflow-x: auto below implicitly computes overflow-y to auto
     too, so the top padding needs enough headroom that the tilted
     photo corners (see .photo-scroll-item img below) don't clip
     against the padding box. */
  padding: 18px 6px 10px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

.photo-scroll::-webkit-scrollbar {
  height: 8px;
}

.photo-scroll::-webkit-scrollbar-thumb {
  background: var(--proj-accent-2);
  border-radius: 8px;
}

.photo-scroll-item {
  flex-shrink: 0;
  scroll-snap-align: start;
  margin: 0; /* reset the browser's default figure margin (1em 40px) — it was swamping the flex `gap` above */
}

/* Thick "photo print" border + a playful alternating tilt, borrowed
   from the polaroid look on about.html's horizontal scroll-track
   (minus that page's wheel-hijacking JS — this stays a plain native
   overflow-x scroller). Rotating just the img rather than the whole
   figure keeps the caption underneath sitting flat and legible. */
.photo-scroll-item img {
  display: block;
  width: auto;
  height: 280px;
  max-width: 70vw;
  background: #ffffff;
  border: 12px solid #ffffff;
  border-radius: 0;
  box-shadow: 4px 6px 16px rgba(0, 0, 0, 0.14);
}

.photo-scroll-item:nth-child(3n+1) img {
  transform: rotate(-2.2deg);
}

.photo-scroll-item:nth-child(3n+2) img {
  transform: rotate(1.6deg) translateY(4px);
}

.photo-scroll-item:nth-child(3n) img {
  transform: rotate(-1deg);
}

.photo-scroll-item figcaption {
  max-width: 320px;
  margin: 10px 2px 0;
  font-family: "Courier New", Courier, monospace;
  font-size: 0.78rem;
  color: var(--proj-ink);
  line-height: 1.3;
}

.scroll-hint {
  margin: 0 0 0 2px;
  font-family: "Courier New", Courier, monospace;
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: 0.55;
}

/* ---- Simple single/two-up figure grids ---- */
.project-figure {
  margin: 26px 0;
}

.project-figure img {
  display: block;
  width: 100%;
  border: 1.4px solid var(--proj-ink);
  border-radius: 10px;
}

.project-figure figcaption {
  margin-top: 10px;
  font-family: "Courier New", Courier, monospace;
  font-size: 0.8rem;
  color: var(--proj-ink);
  opacity: 0.8;
}

/* Two figures side by side, always filling the full row width between
   them — each image keeps its own aspect ratio (no distortion), so a
   portrait and a landscape photo share the row in proportion to their
   own shape rather than splitting it evenly. script.js sets each
   figure's flex-grow/shrink to its image's width/height ratio once
   the image has loaded; the values below are just the pre-JS fallback. */
.figure-pair {
  display: flex;
  align-items: stretch;
  gap: 22px;
  margin: 26px 0;
}

.figure-pair .project-figure {
  flex: 1 1 0%;
  min-width: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
}

.figure-pair .project-figure img {
  display: block;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
  object-fit: cover;
}

/* The caption's own text would otherwise be the widest thing in the
   figure, forcing the flex item wider than its image. Cap its
   contribution to auto-sizing so the figure always matches the image. */
.figure-pair .project-figure figcaption {
  max-width: 0;
  min-width: 100%;
}

/* ============================================================
   GALLERY WALL TEMPLATE (flat collage gallery)
   For multi-piece galleries like MediaLab — and later Artworks,
   Generasian, Baedeker, ABT, GDM, NYUCSSA — where the story is "here's
   everything I made" with no single arc. Deliberately built as its
   own visual language, separate from the case-file template above:
   no cards, no accent-color fills, no rounded corners — just images
   of their own natural proportions, packed edge to edge like a
   contact sheet, with context living in a hover caption rather than
   a permanent label. Only .project-page's theme variables and
   .project-kicker/.project-hero-tagline/.project-meta text styles are
   shared with the case-file template; everything else here is new. ----
   ============================================================ */

.wall-hero {
  width: min(760px, 88vw);
  margin: 0 auto;
  padding: 10px 0 6px;
  text-align: center;
  /* Same quiet entrance as .project-hero on the case-file template —
     it's already on screen at load, so it plays once immediately
     rather than waiting on a scroll trigger. */
  animation: hero-rise 0.8s ease both;
}

.wall-hero .project-kicker {
  display: block;
}

.wall-hero-title {
  margin: 0;
  font-family: "Just Me Again Down Here", cursive;
  font-weight: 400;
  font-size: clamp(2.4em, 7vw, 6.4em);
  line-height: 0.9;
}

.wall-hero-title span {
  color: var(--proj-accent);
}

.wall-hero .project-hero-tagline {
  max-width: 100%;
  margin: 14px auto 0;
  font-size: 1.05rem;
}

/* Role/Org form one left-aligned column, Timeline/Tools form another —
   both columns read normally (left-aligned), centered as a pair. A
   thin dashed divider sits between them and stretches to match
   whichever column runs taller (Org's value wraps to two lines, Tools
   usually doesn't) — it's what makes the block read as one aligned
   unit even though the two columns aren't the same height. */
.wall-hero-meta {
  display: flex;
  justify-content: center;
  align-items: stretch;
  gap: 32px;
  margin: 28px 0 0;
  text-align: left;
}

.wall-hero-meta-col {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0;
}

.wall-hero-meta-col div {
  min-width: 0;
}

.wall-lede {
  width: min(760px, 88vw);
  margin: 26px auto 0;
  text-align: center;
  font-family: "Rethink Sans", serif;
  font-size: 1.02rem;
  line-height: 1.5;
  opacity: 0.85;
}

/* ---- The wall itself: a true bin-packed masonry, positioned by the
   script in script.js rather than CSS Grid. CSS Grid's "dense" auto-
   placement (an earlier version of this) scans for the *first* cell an
   item fits, not the *shortest* column, so mixed 1- and 2-wide tiles
   could still leave a visible gap. The JS instead tracks each
   column's running height directly and always drops the next tile
   into whichever column(s) are currently shortest — true masonry,
   the same technique Pinterest-style grids use — so gaps only ever
   happen where two neighboring columns' heights genuinely can't be
   reconciled by a 2-wide tile, not from a placement heuristic giving
   up early. Every image still keeps its own natural ratio (width set
   in px by JS, height:auto — never cropped, only ever scaled down
   proportionally). .wall-tile-wide (banners, the multi-card tile) is
   just a marker class the script reads to request a 2-column tile. */
/* ---- Section title: splits one gallery page into multiple stacked
   walls (e.g. Merch above Graphics) — a script-font label plus a
   one-line note, styled as a smaller echo of .wall-hero-title/.wall-lede
   rather than a new visual language. Only meaningful when a page has
   more than one .wall; single-wall pages never use this. ---- */
/* script.js adds/removes reveal-pending the same way it does for
   .project-section — lets a second wall (e.g. Merch below Graphics)
   rise into place as you scroll to it, rather than everything
   appearing at once on load. */
.wall-section {
  width: min(760px, 88vw);
  margin: 64px auto 0;
  text-align: center;
  transition: opacity 0.7s ease, transform 0.7s ease;
}

.wall-section:first-of-type {
  margin-top: 8px;
}

.wall-section.reveal-pending {
  opacity: 0;
  transform: translateY(28px);
}

.wall-section-title {
  margin: 0;
  font-family: "Just Me Again Down Here", cursive;
  font-weight: 400;
  font-size: clamp(1.8em, 4.4vw, 3em);
  line-height: 1;
  color: var(--proj-accent);
}

.wall-section-note {
  margin: 10px auto 0;
  font-family: "Rethink Sans", serif;
  font-size: 0.98rem;
  line-height: 1.5;
  opacity: 0.85;
}

.wall {
  position: relative;
  width: min(94vw, 1600px);
  margin: 34px auto 100px;
}

/* A wall that's followed by another .wall section shouldn't carry the
   full 100px closing margin — the next section's heading provides its
   own top spacing instead. */
.wall-section + .wall {
  margin-top: 24px;
}

.wall-tile {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  margin: 0;
  padding: 0;
  border: none;
  background: none;
  text-align: left;
  text-decoration: none;
  color: inherit;
  cursor: none;
  /* Reveal transition only — top/left (the masonry position itself,
     set inline by layoutWall() in script.js) are never transitioned,
     so a resize-triggered relayout still snaps instantly instead of
     sliding tiles around the page. */
  transition: opacity 0.6s ease, transform 0.6s ease;
}

.wall-tile.reveal-pending {
  opacity: 0;
  transform: translateY(24px);
}

.wall-tile img {
  display: block;
  width: 100%;
  height: auto;
}

/* ---- A pair tile: two images shown side by side as one masonry item
   (e.g. two versions of the same card), instead of two separate tiles
   that the grid could place apart. Each image keeps its own ratio —
   flex-basis:0 lets them split the row width evenly, and height:auto
   keeps them proportional rather than stretched. ---- */
.wall-pair {
  display: flex;
  align-items: center; /* not the flex default (stretch) — panes rarely
    share an aspect ratio exactly, and stretch would distort whichever
    one doesn't match the row's tallest sibling */
  gap: 5px;
}

.wall-pair img {
  flex: 1 1 0%;
  min-width: 0;
  width: auto;
}

/* ---- A trio tile: three images side by side as one masonry item —
   used for a full sketch/color/final progression in one row. Same
   idea as .wall-pair, just three panes; always paired with
   .wall-tile-triple so the row gets a 3-column span to breathe in. ---- */
.wall-trio {
  display: flex;
  align-items: center; /* see .wall-pair — same fix for the same reason,
    more visible here since the final vector's canvas proportions rarely
    match the sketch/color panes it sits beside */
  gap: 5px;
}

.wall-trio img {
  flex: 1 1 0%;
  min-width: 0;
  width: auto;
}

/* ---- A quad tile: four images in a 2x2 grid as one masonry item —
   used for the new-member card's four color palettes. Always paired
   with .wall-tile-wide so the 2x2 has room to read clearly. ---- */
.wall-quad {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 5px;
}

.wall-quad img {
  width: 100%;
  height: auto;
  min-width: 0;
  display: block;
}

/* ---- Hover caption: a full-cover scrim so it reads over any image,
   present only on tiles that actually have something to say — a tile
   with no .wall-caption in it does nothing on hover at all. ---- */
.wall-caption {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  padding: 18px;
  background: rgba(20, 22, 24, 0.7);
  color: #f7f5f0;
  opacity: 0;
  transition: opacity 0.22s ease;
  font-family: "Rethink Sans", serif;
  font-size: 0.92rem;
  line-height: 1.42;
}

.wall-tile:hover .wall-caption,
.wall-tile:focus-visible .wall-caption {
  opacity: 1;
}

.wall-caption em {
  display: block;
  margin-top: 8px;
  font-family: "Courier New", Courier, monospace;
  font-style: normal;
  font-size: 0.74rem;
  letter-spacing: 0.03em;
  opacity: 0.82;
}

/* ---- Lightbox: full view of a piece plus prev/next through its
   group. Flat by design — no rounded corners, no filled backgrounds
   behind the controls, just the image and plain glyph buttons. ---- */
.gallery-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 60px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.gallery-lightbox.active {
  opacity: 1;
  pointer-events: auto;
}

.gallery-lightbox-overlay {
  position: absolute;
  inset: 0;
  background: rgba(16, 17, 19, 0.92);
}

.gallery-lightbox-card {
  position: relative;
  width: min(880px, 92vw);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  transform: scale(0.97);
  opacity: 0;
  transition: transform 0.24s ease, opacity 0.24s ease;
  z-index: 1;
}

.gallery-lightbox.active .gallery-lightbox-card {
  transform: scale(1);
  opacity: 1;
}

.gallery-lightbox-media {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.gallery-lightbox-media img {
  display: block;
  max-width: 100%;
  max-height: 78vh;
  object-fit: contain;
}

.gallery-lightbox-close,
.gallery-lightbox-prev,
.gallery-lightbox-next {
  position: absolute;
  border: none;
  background: none;
  color: #f7f5f0;
  font-family: "Aubrey", serif;
  line-height: 1;
  cursor: none;
  z-index: 2;
  transition: color 0.18s ease, transform 0.18s ease;
}

.gallery-lightbox-close {
  top: -38px;
  right: 0;
  font-size: 2rem;
}

.gallery-lightbox-prev,
.gallery-lightbox-next {
  top: 50%;
  transform: translateY(-50%);
  font-size: 2.6rem;
  padding: 10px;
}

.gallery-lightbox-prev {
  left: -56px;
}

.gallery-lightbox-next {
  right: -56px;
}

.gallery-lightbox-close:hover {
  color: #899FBB;
  transform: rotate(8deg) scale(1.1);
}

.gallery-lightbox-prev:hover,
.gallery-lightbox-next:hover {
  color: #899FBB;
  transform: translateY(-50%) scale(1.15);
}

.gallery-lightbox-footer {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
  padding: 14px 4px 0;
  color: #f7f5f0;
}

.gallery-lightbox-caption {
  margin: 0;
  font-family: "Rethink Sans", serif;
  font-size: 1rem;
  line-height: 1.4;
}

.gallery-lightbox-counter {
  margin: 0;
  flex-shrink: 0;
  font-family: "Courier New", Courier, monospace;
  font-size: 0.8rem;
  opacity: 0.75;
}

/* Mobile */
@media (max-width: 720px) {
  /* Site header — logo wordmark + nav links need to fit next to each
     other on narrow screens without wrapping into the fixed header
     and clipping off-screen. It's the one nav that's always present
     on mobile (the section sidebar is dropped entirely below), so it
     gets a solid background plus a bottom rule so it always reads
     clearly as a sticky nav bar while content scrolls beneath it. */
  .site-header {
    height: auto;
    padding: 10px 20px;
    flex-wrap: wrap;
    row-gap: 6px;
    align-items: center;
    background: #f7f5f0;
    border-bottom: 2px solid #899FBB;
  }

  .logo img {
    width: 32px;
    margin-top: 0;
  }

  .logo span {
    font-size: 1.1rem;
    margin-top: 0;
  }

  .main-nav {
    gap: 14px;
  }

  .main-nav a {
    font-size: 1.1rem;
  }

  .logo span,
  .main-nav {
    opacity: 1 !important;
  }

  .project-modal {
    padding: 28px 18px;
  }

  .project-card {
    width: min(430px, 94vw);
    max-height: 86vh;
    overflow-y: auto;
    transform: scale(0.96) rotate(0deg);
  }

  .project-modal.active .project-card {
    transform: scale(1) rotate(0deg);
  }

  .project-card-tab {
    left: 34px;
    min-width: 150px;
    font-size: 1rem;
  }

  .project-card-inner {
    grid-template-columns: 1fr;
    gap: 22px;
    padding: 42px 28px 30px;
  }

  .project-preview {
     max-width: 280px;
     height: 260px;
    aspect-ratio: 1 / 1;
    margin: 0 auto;
  }

  .project-info {
    padding-right: 0;
  }

  .project-info h2 {
    font-size: 3.2rem;
  }

  .project-summary {
    font-size: 1.18rem;
  }

  .project-tools {
    font-size: 1rem;
  }

  /* Project page ("case file" template) */
  .project-hero-grid {
    flex-direction: column;
    gap: 26px;
  }

  .project-hero-text {
    flex: 0 0 auto;
  }

  .project-hero-figure {
    flex: 0 0 auto;
    aspect-ratio: 16 / 10;
  }

  .project-hero-title {
    font-size: 3.6em;
  }

  .project-hero-tagline {
    font-size: 1.1rem;
  }

  .project-layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 6px;
  }

  /* Section index only makes sense with room for a real sidebar —
     dropped entirely on mobile rather than squeezed into a horizontal
     strip. The frosted-glass treatment it used to have now lives on
     the site header instead, which stays put as the one persistent
     nav on small screens. */
  .project-sidebar {
    display: none;
  }

  .project-section h2 {
    font-size: 2.2em;
  }

  .text-with-figure {
    grid-template-columns: 1fr;
  }

  /* Stacked on mobile: undo the ratio-based flex-grow/shrink script.js
     sets inline (!important wins over inline styles) so each image
     just shows full-width at its own natural height, one per row. */
  .figure-pair {
    flex-direction: column;
  }

  .figure-pair .project-figure {
    flex: 0 0 auto !important;
  }

  .figure-pair .project-figure img {
    flex: 0 0 auto !important;
    height: auto !important;
    max-height: 280px;
    object-fit: unset;
  }

  /* Portrait pairs (e.g. phone-interface screenshots) — the generic
     figure-pair mobile behavior above stacks each image full-width
     with a 280px height cap, which squishes tall portrait screenshots
     out of their aspect ratio. Keep these side by side instead, sized
     off their own natural ratio so nothing distorts. */
  .figure-pair-portrait {
    flex-direction: row;
    gap: 12px;
  }

  .figure-pair-portrait .project-figure {
    flex: 1 1 0% !important;
  }

  .figure-pair-portrait .project-figure img {
    max-height: none;
    object-fit: contain;
  }

  .photo-scroll-item img {
    height: 210px;
    max-width: 78vw;
  }

  /* Gallery wall (flat collage gallery — MediaLab, and later galleries).
     Column count and gap for the wall itself are computed by the JS
     masonry layout in script.js (keyed off the same breakpoints as
     the rest of this stylesheet), not CSS — a position:absolute
     masonry can't be laid out with grid-template-columns. */
  .wall-caption {
    padding: 12px;
    font-size: 0.82rem;
  }

  .wall-hero-meta {
    flex-direction: column;
    align-items: flex-start;
    gap: 20px;
  }

  .wall-hero-meta-divider {
    display: none;
  }

  .gallery-lightbox {
    padding: 46px 20px 20px;
  }

  .gallery-lightbox-close {
    top: -34px;
    right: 0;
    font-size: 1.6rem;
  }

  .gallery-lightbox-prev,
  .gallery-lightbox-next {
    top: auto;
    bottom: -46px;
    transform: none;
    font-size: 2rem;
  }

  .gallery-lightbox-prev {
    left: 0;
  }

  .gallery-lightbox-next {
    right: 0;
  }

  .gallery-lightbox-prev:hover,
  .gallery-lightbox-next:hover {
    transform: scale(1.15);
  }

  .gallery-lightbox-footer {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }

  /* About page */
  /* Header-to-title clearance — the desktop padding-top (80px) leaves
     only ~22px under the mobile header before the title starts, which
     reads as cramped right under the header's border-bottom rule. */
  .about-page {
    padding-top: 98px;
  }

  /* Standardized title-to-content gap across About/Sketchbook/Grid view
     on mobile — this was 15px here, ~52px on Sketchbook, and 54px on
     the home grid view, so the About page felt cramped right under the
     title next to the other two. 32px everywhere now. */
  .about-title {
    margin-bottom: 32px;
  }

  .about-intro {
    grid-template-columns: 1fr;
    gap: 30px;
  }

  .about-image {
    order: 2;
    transform: rotate(0deg);
  }

  .about-image img {
    min-height: 0;
  }

  .about-text {
    order: 1;
  }

  .strip-header h2 {
    font-size: 1.3rem;
  }

  /* Sketchbook page header-to-title clearance — same reasoning as
     .about-page above; the kicker line was landing just ~8px under
     the mobile header. */
  .sketchbook-page {
    padding-top: 88px;
  }

  /* Projects section (home) + Sketchbook grid */
  /* Same 32px standard as .about-title above. */
  .grid-view-header {
    margin-bottom: 32px;
  }

  .project-grid {
    grid-template-columns: 1fr;
    gap: 26px 20px;
  }

  .project-tile,
  .project-grid > *:nth-child(3n+2),
  .project-grid > *:nth-child(3n) {
    transform: rotate(0deg);
  }

  .project-tile-caption {
    opacity: 1;
    transform: translateY(0);
    background: linear-gradient(
      to top,
      rgba(20, 22, 24, 0.78) 0%,
      rgba(20, 22, 24, 0) 100%
    );
  }

  .project-tile.is-placeholder .project-tile-caption {
    background: linear-gradient(
      to top,
      rgba(47, 52, 55, 0.68) 0%,
      rgba(47, 52, 55, 0) 100%
    );
  }

  .tile-action {
    opacity: 1;
    transform: translateY(0);
  }

  .sketchbook-grid {
    grid-template-columns: 1fr;
    width: min(90vw, 1600px);
    /* .sketchbook-hero's own 6px bottom padding plus this margin adds
       up to the same 32px standard as .about-title/.grid-view-header. */
    margin-top: 26px;
  }

  .sketchbook-tile-caption {
    opacity: 1;
    background: linear-gradient(
      to top,
      rgba(20, 22, 24, 0.78) 0%,
      rgba(20, 22, 24, 0) 100%
    );
  }

  .sketchbook-tile.is-placeholder .sketchbook-tile-caption {
    background: linear-gradient(
      to top,
      rgba(47, 52, 55, 0.68) 0%,
      rgba(47, 52, 55, 0) 100%
    );
  }

  /* Scatter view (home hero) — on desktop the stickers are scattered
     via absolute percent/pixel placement, which is fine because
     there's room for them to spread out. On phone widths that same
     placement just piles every sticker on top of the title in the
     middle of the screen, so here we drop the absolute positioning
     entirely: the title becomes a normal block up top, and the
     stickers flow into a centered, wrapping row below it. */
  .view-toggle {
    /* Desktop deliberately overlaps the transparent header (see the
       comment on .view-toggle above) — but the mobile header has a
       solid background, so the same top offset visibly covers it.
       Push the toggle down to sit under the header instead. */
    top: 96px;
    /* left:50%/translateX(-50%) is exactly centered in principle, but
       the sub-pixel result of that math can land the pill a hair off
       true center depending on viewport width/zoom. left:0/right:0
       with auto margins centers a fit-content box unambiguously. */
    left: 0;
    right: 0;
    transform: none;
    width: fit-content;
    margin: 0 auto;
  }

  .hero {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-content: flex-start;
    gap: 18px 14px;
    min-height: auto;
    padding: 172px 24px 60px;
  }

  .center-title {
    position: static;
    order: -1;
    flex: 1 1 100%;
    width: 100%;
    max-width: none;
    transform: none;
    margin: 0 0 12px;
  }

  .center-title h1 {
    font-size: clamp(2.6rem, 15vw, 4.4rem);
  }

  .center-title p {
    font-size: 1rem;
  }

  .object {
    position: static;
  }

  .object:hover {
    transform: scale(1.08);
  }

  /* Sized and staggered individually rather than as a uniform grid —
     the reroll shortcuts (design/art/dice/games) read as small
     incidental icons, the real project stickers take the range of
     sizes in between, and Newtown Creek (the widest/longest artwork)
     anchors the bottom of the stack at full width instead of
     fighting for a column. Alternating margin-top plus small
     rotations keep the wrap from reading as two neat columns. */
  .object-1 {
    width: 132px;
    margin-top: 6px;
    transform: rotate(-4deg);
  }

  .object-2 {
    width: 112px;
    margin-top: 30px;
    transform: rotate(3deg);
  }

  /* design (reroll) */
  .object-11 {
    width: 76px;
    margin-top: 2px;
    transform: rotate(-6deg);
  }

  .object-3 {
    width: 140px;
    margin-top: 16px;
    transform: rotate(5deg);
  }

  /* art (reroll) */
  .object-4 {
    width: 80px;
    margin-top: 34px;
    transform: rotate(-3deg);
  }

  .object-5 {
    width: 150px;
    margin-top: 0;
    transform: rotate(4deg);
  }

  /* dice (reroll) */
  .object-6 {
    width: 64px;
    margin-top: 24px;
    transform: rotate(-8deg);
  }

  /* games (reroll) */
  .object-9 {
    width: 82px;
    margin-top: 8px;
    transform: rotate(6deg);
  }

  .object-10 {
    width: 124px;
    margin-top: 10px;
    transform: rotate(-3deg);
  }

  .object-12 {
    width: 96px;
    margin-top: 0;
    transform: rotate(5deg);
  }

  /* Newtown Creek — widest sticker, so it gets its own full-width row
     at the very end of the stack instead of a column. */
  .object-7 {
    order: 5;
    flex: 1 1 100%;
    width: min(86vw, 320px);
    margin: -6px auto 0;
    transform: rotate(-2deg);
  }

  /* Grid view title — same idea as the scatter view's toggle-position
     comment above: the toggle sits lower on mobile (top:96px, opaque
     header) than the fixed padding-top here was written for. */
  .grid-view {
    padding-top: 165px;
  }
}


