/* Values come from Figma "One page web site" › Frame 4 (node 48:413, 960px
   wide). Spacing is the Figma value at 960px and scales with the screen. */
/* Fonts, served from this site so no visitor data goes to a font CDN.
   Inter is one variable file covering both weights used (400, 600). */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/fonts/inter-latin.woff2") format("woff2");
}

@font-face {
  font-family: "Roboto Slab";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/roboto-slab-latin-400.woff2") format("woff2");
}

:root {
  --bg: #ededed;
  --card: #fff;
  --text: #2d1e19;
  --intro-text: #3c3c3c;
  --pill: #ddd;
  --pill-text: var(--text);
  --media-placeholder: #ececec;                /* light, so no dark fringe at the rounded edges */
  /* Line grid (grid-major-minor-10-30px): 1px lines on a 21.286px pitch,
     every third line lighter. */
  --grid-minor: rgba(136, 136, 136, 0.2);
  --grid-major: #f7f7f7;
  --grid-pitch: 21.286px;

  --font-serif: "Roboto Slab", Georgia, serif;
  --font-sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;

  /* One Figma pixel at the current screen size: 1px at 960px wide, scaling
     between 0.72px and 1.17px (titles 26px–42px). */
  --u: clamp(0.7222px, calc(100vw / 960), 1.1667px);

  --card-margin: clamp(12px, 3.65vw, 58px);    /* 35px at 960 */
  --card-side: max(8px, var(--card-margin) / 2); /* left/right of the card: half the margin, 17.5px at 960 */
  --pad-left: clamp(16px, 5vw, 80px);          /* card edge → text, 48px at 960 */
  --pad-right: clamp(16px, 5.625vw, 90px);     /* video → card edge, 54px at 960 */
  --row-gap: clamp(72px, 17vw, 190px);         /* 163px at 960 */
}

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

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

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* Mood colors (Figma "Mood board background", node 61:913). Picked on the
   login page or in the mood tab, they recolor the page behind the white card.
   Gray is the default. Blue and purple are deep enough for white text. */
html[data-mood="blue"]   { --bg: #2f58e0; }   /* white text 5.84:1 */
html[data-mood="aqua"]   { --bg: #87e5ff; }
html[data-mood="yellow"] { --bg: #f0e650; }
html[data-mood="orange"] { --bg: #f5b96e; }
html[data-mood="purple"] { --bg: #8763c3; }   /* Figma #8b68c5, a hair deeper so white text passes (4.56:1) */

/* The pill nav (and pill buttons) take on the mood color too: white text on
   the dark ones (blue, purple), the usual brown on the light ones. Gray
   keeps the original #ddd pill. */
html[data-mood]:not([data-mood="gray"]) { --pill: var(--bg); }
html[data-mood="blue"],
html[data-mood="purple"] { --pill-text: #fff; }

body {
  margin: 0;
  background: var(--bg);
  transition: background-color 0.4s ease;
  color: var(--text);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

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

:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 4px;
}

/* Header: logo sits above the card and overlaps its top edge by 12px. --- */

.site-header {
  position: relative;
  z-index: 1;
  height: clamp(80px, 13.44vw, 129px);         /* card starts at 129px */
}

.logo {
  position: absolute;
  top: clamp(16px, 4.27vw, 41px);
  left: calc(var(--card-side) + 9px);
  border-radius: 4px;
}

.logo img {
  width: clamp(116px, 20.2vw, 194px);
  height: auto;
}

/* Card ------------------------------------------------------------------ */

.card {
  display: block;
  margin: 0 var(--card-side) var(--card-margin);
  overflow: clip;                              /* clips the grid to the corners (clip, not hidden, so the nav can stick) */
  border-radius: 20px;
  background: var(--card);
}

/* Same space on the left and right (29px in from the project text at 960px),
   so the paragraph spans the card evenly at every width. */
.intro {
  --intro-inset: calc(var(--pad-left) + 29 * var(--u));
  margin: 0 var(--intro-inset);
  padding-top: clamp(40px, 10vw, 96px);
  color: var(--intro-text);
  font-size: clamp(20px, 2.917vw, 35.2px);      /* 20px phones, 28px at 960 (Figma), up to 35px (+10%) on wide screens */
  line-height: 1.74;
  letter-spacing: -0.02em;
  text-wrap: pretty;                           /* no single word on the last line */
}

/* Work: pill nav + projects, on the line grid. -------------------------- */

.work {
  position: relative;
  margin-top: clamp(40px, 8.2vw, 79px);
  padding: 0 var(--pad-right) var(--row-gap) var(--pad-left);
}

/* The grid starts at the top of the pill nav and fades in: about half
   strength by the first project, full strength just below it. */
.work::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--grid-major) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-major) 1px, transparent 1px),
    linear-gradient(to right, var(--grid-minor) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-minor) 1px, transparent 1px);
  background-size:
    calc(var(--grid-pitch) * 3) 100%,
    100% calc(var(--grid-pitch) * 3),
    var(--grid-pitch) 100%,
    100% var(--grid-pitch);
  background-position: 0.5px 0.5px;
  -webkit-mask-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.5) 174px, #000 260px);
  mask-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.5) 174px, #000 260px);
  pointer-events: none;
}

.work > * { position: relative; }

.pill-nav {
  --nav-h: 63px;
  --nav-track: 0.31em;                         /* letter spacing of the labels */
  position: relative;                          /* the contact menu hangs from it */
  display: inline-flex;
  align-items: center;
  gap: 24px;                                   /* the same space on both sides of every divider */
  height: var(--nav-h);
  padding: 0 40px;                             /* and the same at both ends */
  border-radius: 50px;
  background: var(--pill);
  box-shadow: 4px 4px 20px 4px rgba(0, 0, 0, 0.08);
  transition: background-color 0.4s ease;
}

.pill-nav > a,
.pill-nav__contact {
  color: var(--pill-text);
  font-size: 16px;
  line-height: 1.32;
  letter-spacing: var(--nav-track);
  text-decoration: none;
  text-transform: uppercase;
  border-radius: 2px;
  transition: opacity 0.2s ease;
}

/* Letter spacing also adds a gap after the last letter; cancel it so each
   label's space is measured from its last letter, not from that gap. */
.pill-nav > a { margin-right: calc(-1 * var(--nav-track)); }

.pill-nav > a:hover,
.pill-nav__contact:hover { opacity: 0.6; }

/* The nav stays on screen while scrolling through the projects. */
.work > .pill-nav {
  position: sticky;
  top: 16px;
  z-index: 5;
}

/* Contact is a button that opens a menu: same look as the links, with a
   small chevron that flips when the menu is open. */
button.pill-nav__contact {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0;                                      /* the letter spacing after the T already spaces the chevron */
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  cursor: pointer;
}

.pill-nav__caret {
  width: 0.4em;
  height: 0.4em;
  margin-top: -0.25em;
  margin-right: 0.08em;                        /* its rotated corner pokes out this much; keeps the end spacing even */
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.25s ease, margin 0.25s ease;
}

.pill-nav__contact[aria-expanded="true"] .pill-nav__caret { margin-top: 0.2em; transform: rotate(-135deg); }

/* The menu sits 4px under the bar, its left edge lined up with the "C" of
   Contact (main.js sets left), and only as wide as its text: the same
   padding on both sides. Same grey and shadow as the bar. */
.contact-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  width: max-content;
  margin: 0;
  padding: 6px;
  list-style: none;
  border-radius: 18px;
  background: var(--pill);
  box-shadow: 4px 8px 20px 4px rgba(0, 0, 0, 0.08);
  animation: menu-in 0.2s ease;
}

.contact-menu[hidden] { display: none; }

.contact-menu a,
.contact-menu button {
  display: block;
  width: 100%;
  padding: 12px 14px;
  border: 0;
  border-radius: 12px;
  background: none;
  color: var(--pill-text);
  font: inherit;
  font-size: 15px;
  line-height: 1.3;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.contact-menu a:hover,
.contact-menu button:hover { background: color-mix(in srgb, currentColor 10%, transparent); }

@keyframes menu-in { from { opacity: 0; transform: translateY(-6px); } }

@media (prefers-reduced-motion: reduce) {
  .contact-menu { animation: none; }
  .pill-nav__caret { transition: none; }
}

/* The divider asset is a 23px horizontal line; turned upright it takes up
   1px of the row. */
.pill-nav__divider {
  flex: none;
  margin: 0 -11px;
  transform: rotate(90deg);
}

/* Divider lines turn white with the white text. */
html[data-mood="blue"] .pill-nav__divider,
html[data-mood="purple"] .pill-nav__divider { filter: brightness(0) invert(1); }

/* Projects -------------------------------------------------------------- */

.projects {
  display: flex;
  flex-direction: column;
  gap: var(--row-gap);
  margin-top: clamp(48px, 11.15vw, 107px);
  scroll-margin-top: 24px;
}

.project {
  display: grid;
  grid-template-columns: minmax(0, 364fr) minmax(0, 394fr);
  column-gap: calc(33 * var(--u));
  align-items: start;
}

.project:focus-visible { outline-offset: 12px; }

.project__text {
  display: flex;
  flex-direction: column;
}

/* "Most recent" tag above the newest projects: square, the same size as
   "Request case study" in the title's typeface (Inter, regular weight), on
   the page's own background color, so it changes with the mood (white text
   on blue and purple, like the nav). */
.project__badge {
  align-self: flex-start;
  margin: 0 0 calc(10 * var(--u));
  padding: 0.3em 0.6em;
  border-radius: 0;
  background: var(--bg);
  color: var(--pill-text);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: clamp(14px, calc(16 * var(--u)), 19px);
  line-height: 1.32;
  letter-spacing: -0.02em;
  transition: background-color 0.4s ease;
}

.project__title {
  margin: 0;
  font-weight: 600;
  font-size: calc(36 * var(--u));              /* 26px – 42px */
  line-height: 1.32;
  letter-spacing: -0.02em;
  text-wrap: balance;                          /* even lines, never one word alone */
}

/* "Request case study" opens a ready-made email from the recruiter (main.js). */
.project__cta {
  display: block;
  width: fit-content;
  margin: calc(10 * var(--u)) 0 0;
  color: inherit;
  text-decoration: none;
  font-family: var(--font-serif);
  font-size: clamp(14px, calc(16 * var(--u)), 19px);
  line-height: 1.32;
  letter-spacing: -0.02em;
}

.project__media {
  position: relative;
  margin-top: calc(5 * var(--u));              /* video top sits 5px below the title top */
  aspect-ratio: 394 / 226;
  overflow: hidden;
  border-radius: 12px;
  background: var(--media-placeholder);
  /* Very soft shadow so the video edge reads on the white card. */
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 8px 24px rgba(0, 0, 0, 0.07);
  transform: translateZ(0);                    /* own layer: clean clipping of the rounded corners */
  transition: transform 0.4s ease;
}

/* Drawn 1px past the box on every side so no half-pixel gap can show the
   background as a fuzzy line at the edge. */
.project__media video {
  position: absolute;
  inset: -1px;
  width: calc(100% + 2px);
  height: calc(100% + 2px);
  object-fit: cover;
  display: block;
}

.project__media video[hidden] { display: none; }

/* Videos that aren't as wide as the box (Hotwire, 3:2): shown whole, with the
   same top and bottom space as the recording. The sides are filled with the
   video's own near-white backdrop so the extra width blends in. */
.project__media--fit { background: #fafcfe; }
.project__media--fit video { object-fit: contain; }

@media (hover: hover) and (pointer: fine) {
  .project:hover .project__cta,
  .project__cta:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
  .project:hover .project__media { transform: translateZ(0) scale(1.015); }
}

@media (prefers-reduced-motion: reduce) {
  .project__media { transition: none; }
  .project:hover .project__media { transform: translateZ(0); }
}

/* Typing effect for the welcome lines (welcome.js): letters not yet typed
   stay invisible but keep their space, so nothing shifts while it types. */
.type-rest { visibility: hidden; }

/* Text cursor: an empty inline span (no width), so it never changes how the
   line wraps. */
.type-caret { position: relative; }

.type-caret::after {
  content: "";
  position: absolute;
  top: 0.1em;
  left: 0.04em;
  width: 0.08em;
  height: 1.05em;
  background: currentColor;
  animation: caret-blink 1s steps(1) infinite;
}

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

/* Mood swatches (login page and the mood tab) ------------------------------ */

/* 24px circles with the soft Figma shadow. */
.mood {
  position: relative;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--swatch);
  box-shadow: 2px 2px 25px 5px rgba(0, 0, 0, 0.08);
  cursor: pointer;
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Hover: the swatch bubbles up a little. */
.mood:hover { transform: scale(1.1); }

.mood input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

/* Selected: just a little bigger than the others, no ring. */
.mood:has(input:checked) { transform: scale(1.22); }

.mood:has(input:focus-visible) {
  outline: 2px solid var(--text);
  outline-offset: 5px;
}

/* Mood tab: the Figma tab (node 54:846) tucked into the right edge of the
   screen, bleeding off it, so only the left corners are rounded. It shows the
   current color; hover (tap or swipe on touch) slides it out to all six. */
.mood-tab {
  --tab-bleed: 9px;                           /* how far it runs off the screen */
  position: fixed;
  top: 87.5%;                                  /* halfway between the lower third and the bottom */
  right: calc(-1 * var(--tab-bleed));
  z-index: 20;
  width: calc(39px + var(--tab-bleed));        /* Figma tab at 75%: 39 × 42 */
  height: 42px;
  border-radius: 6px 0 0 6px;
  background: rgba(255, 255, 255, 0.9);
  box-shadow: -2px 2px 20px 2px rgba(0, 0, 0, 0.08); /* keeps it visible where it overlaps the white card */
  overflow: hidden;
  transform: translateY(-50%);
  transition: width 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.mood-tab.is-open,
.mood-tab:has(:focus-visible) { width: calc(177px + var(--tab-bleed)); }

@media (hover: hover) {
  .mood-tab:hover { width: calc(177px + var(--tab-bleed)); }
}

/* Closed: just the current color, centered in the tab. */
.mood-tab__handle {
  position: absolute;
  inset: 0 auto 0 0;
  display: grid;
  place-items: center;
  width: 39px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  transition: opacity 0.2s ease;
}

.mood-tab__current {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--bg);
  box-shadow: 2px 2px 25px 5px rgba(0, 0, 0, 0.08);
  transition: background-color 0.4s ease;
}

/* Open: the row of swatches, revealed from the screen edge as the tab grows. */
.mood-tab__swatches {
  position: absolute;
  top: 0;
  right: var(--tab-bleed);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  width: 177px;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}

/* Swatches in the tab are 75% of the login ones. */
.mood-tab .mood { width: 18px; height: 18px; }

.mood-tab.is-open .mood-tab__handle,
.mood-tab:has(:focus-visible) .mood-tab__handle { opacity: 0; pointer-events: none; }

.mood-tab.is-open .mood-tab__swatches,
.mood-tab:has(:focus-visible) .mood-tab__swatches { opacity: 1; pointer-events: auto; transition-delay: 0.1s; }

@media (hover: hover) {
  .mood-tab:hover .mood-tab__handle { opacity: 0; pointer-events: none; }
  .mood-tab:hover .mood-tab__swatches { opacity: 1; pointer-events: auto; transition-delay: 0.1s; }
}

@media (prefers-reduced-motion: reduce) {
  .mood-tab, .mood-tab__handle, .mood-tab__swatches { transition: none; }
}

/* Pac-Man in front of "Request case study": a 45° mouth opening toward the
   words. Hovering the link turns him a little, like he's about to take a bite. */
.cta-shape {
  display: inline-block;
  width: 0.78em;
  height: 0.78em;
  margin-right: 0.5em;
  vertical-align: -0.08em;
  border-radius: 50%;
  background: conic-gradient(from 112.5deg, currentColor 0 315deg, transparent 0);
  transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@media (prefers-reduced-motion: no-preference) {
  .project__cta:hover .cta-shape,
  .project__cta:focus-visible .cta-shape { transform: rotate(-25deg); }
}

/* Welcome flash (login, and once more on arrival after logging in) ------- */
/* Centered in the grey area, 12px above the card. welcome.js types it out
   with a cursor, holds it, then fades it away (once). */
.welcome-flash {
  position: absolute;
  left: 50%;
  bottom: 12px;
  margin: 0;
  color: var(--intro-text);
  font-size: 20px;
  font-weight: 400;
  line-height: 1.35;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transform: translateX(-50%);
  transition: opacity 0.5s ease, transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

.welcome-flash[hidden] { display: none; }
.welcome-flash.is-on { opacity: 1; }
.welcome-flash.is-leaving { opacity: 0; transform: translate(-50%, -8px); }

/* Portfolio: vertically centered in the grey space above the card. */
.welcome-flash--mid {
  top: 50%;
  bottom: auto;
  transform: translate(-50%, -50%);
}
.welcome-flash--mid.is-leaving { transform: translate(-50%, calc(-50% - 8px)); }

/* Phones: centered would run into the logo, so it sits on the right. */
@media (max-width: 720px) {
  .welcome-flash {
    left: auto;
    right: calc(var(--card-side) + 12px);
    max-width: calc(100% - var(--card-side) * 2 - clamp(116px, 20.2vw, 194px) - 24px);
    font-size: 16px;
    white-space: normal;
    text-align: right;
    transform: none;
  }
  .welcome-flash.is-leaving { transform: translateY(-8px); }
  .welcome-flash--mid { transform: translateY(-50%); }
  .welcome-flash--mid.is-leaving { transform: translateY(calc(-50% - 8px)); }
}

/* Waving hand in front of the text. */
.welcome-flash__wave {
  display: inline-block;
  margin-right: 0.35em;
  transform-origin: 70% 80%;
}

.welcome-flash.is-on .welcome-flash__wave { animation: wave 1.6s ease-in-out 0.2s 2; }

@keyframes wave {
  0%, 60%, 100% { transform: rotate(0deg); }
  10%, 30% { transform: rotate(16deg); }
  20%, 40% { transform: rotate(-8deg); }
  50% { transform: rotate(10deg); }
}

@media (prefers-reduced-motion: reduce) {
  .welcome-flash { transition: opacity 0.5s ease; }
  .welcome-flash.is-leaving { transform: translateX(-50%); }
  .welcome-flash.is-on .welcome-flash__wave { animation: none; }
}

/* Footer (all pages) ---------------------------------------------------- */

.site-footer {
  padding: 0 var(--card-margin) clamp(20px, 3vw, 32px);
  text-align: center;
}

.site-footer p {
  margin: 0;
  color: var(--text);
  font-size: 13px;
  letter-spacing: 0.01em;
  opacity: 0.72;                               /* lightest that still reads (4.5:1) on every light mood */
}

/* Dark moods: the copyright and the 👋 welcome turn white, like the nav. */
html[data-mood="blue"] .site-footer p,
html[data-mood="purple"] .site-footer p { color: #fff; opacity: 1; }
html[data-mood="blue"] .welcome-flash,
html[data-mood="purple"] .welcome-flash { color: #fff; }

/* Resume page ----------------------------------------------------------- */

.resume {
  padding: clamp(24px, 5vw, 56px) var(--pad-left) clamp(40px, 8vw, 96px);
}

.resume__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  max-width: 850px;
  margin: 0 auto clamp(20px, 3vw, 32px);
}

.resume__back {
  color: var(--text);
  font-size: 16px;
  text-decoration: none;
  border-radius: 2px;
}

.resume__back:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Same look as the pill nav. */
.pill-button {
  display: inline-flex;
  align-items: center;
  height: 48px;
  padding: 0 28px;
  border-radius: 50px;
  background: var(--pill);
  box-shadow: 4px 4px 20px 4px rgba(0, 0, 0, 0.08);
  color: var(--pill-text);
  font-size: 14px;
  letter-spacing: 0.31em;
  text-decoration: none;
  text-transform: uppercase;
  white-space: nowrap;
  transition: opacity 0.2s ease, background-color 0.4s ease;
}

.pill-button:hover { opacity: 0.75; }

.resume__pages {
  display: grid;
  gap: clamp(16px, 3vw, 32px);
  max-width: 850px;
  margin: 0 auto;
}

.resume__pages img {
  width: 100%;
  height: auto;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06), 0 8px 24px rgba(0, 0, 0, 0.07);
}


/* Mobile: single column, video below the text; the pill spans the width. */
@media (max-width: 720px) {
  .resume__back { font-size: 14px; }
  .pill-button { height: 42px; padding: 0 18px; font-size: 12px; letter-spacing: 0.2em; }

  .intro { --intro-inset: var(--pad-left); }

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

  .project__media { margin-top: 0; }

  /* Same side padding on both sides so the pill sits dead center. */
  :root { --pad-right: var(--pad-left); }

  /* Full width; labels and dividers evenly spaced across the pill. */
  .pill-nav {
    --nav-h: 52px;
    --nav-track: 0.2em;
    display: flex;
    justify-content: space-evenly;             /* equal space between labels, dividers and both ends */
    gap: 0;
    padding: 0;
  }

  .pill-nav > a,
  .pill-nav__contact {
    font-size: 13px;
  }
}
