/* Portfolio Design System: component styles. Load after tokens.css.
   Type comes from the type-style classes in tokens.css (.h2, .h3, .lead, .body-sm, .label, .eyebrow, .button-label, .hover-label, .nav-link-text, .nav-link-number, .menu-close, .header-link, .work-title, .work-number, .body-lower, .bullet-heading).
   Every colour, space and size below is a token. Square corners, no shadows, and no borders except the line under work items. */

/* ---------- Base ---------- */

:where(body) { margin: 0; background: var(--surface); color: var(--ink); font-family: var(--font-body); }
:where(h1, h2, h3, p, ul) { margin: 0; }
:where(a) {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-thickness: var(--underline-width);
  text-underline-offset: var(--underline-offset);
}
:where(a:hover) { text-decoration-thickness: var(--underline-width-hover); }
:where(:focus-visible) { outline: var(--focus-width) solid var(--focus); outline-offset: var(--focus-offset); }

/* Section links glide to their section, unless reduced motion is on. */
@media (prefers-reduced-motion: no-preference) {
  :where(html) { scroll-behavior: smooth; }
}
/* Sections that take focus after a menu jump don't need an outline around the whole section. */
:where([tabindex="-1"]):focus { outline: none; }

.display, .h1, .h2, .h3, .bullet-heading { text-wrap: balance; }
.eyebrow, .button-label { text-transform: uppercase; }

@media (max-width: 767px) {
  .display { font-size: 56px; line-height: 1.05; }
  .h1 { font-size: 44px; line-height: 1.06; }
  .h2 { font-size: 36px; line-height: 1.17; }
}

/* The one inverted band per page (footer, featured project, contact strip). */
.inverse {
  background: var(--surface-inverse);
  color: var(--ink-inverse);
  --focus: var(--ink-inverse);
}

/* ---------- Layout ---------- */

.container {
  box-sizing: border-box;
  width: 100%;
  max-width: calc(var(--content-max) + 2 * var(--margin-desktop));
  margin-inline: auto;
  padding-inline: var(--margin-phone);
}
.grid {
  display: grid;
  grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
  column-gap: var(--gutter);
}
@media (min-width: 768px) {
  .container { padding-inline: var(--margin-tablet); }
}
@media (min-width: 1024px) {
  .container { padding-inline: var(--margin-desktop); }
}

/* ---------- Logo ---------- */

.logo { display: block; }
.logo--white { display: none; }
[data-theme="dark"] .logo--black,
.inverse .logo--black { display: none; }
[data-theme="dark"] .logo--white,
.inverse .logo--white { display: block; }
[data-theme="dark"] .inverse .logo--black { display: block; }
[data-theme="dark"] .inverse .logo--white { display: none; }

/* ---------- Button (from Figma: button, node 284:710) ---------- */

.button {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--button-gap);
  padding: var(--button-padding-block) var(--button-padding-inline);
  border: 0;
  border-radius: var(--radius-none);
  background: var(--button-bg);
  color: var(--button-ink);
  font-family: var(--font-body);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.button:hover { background: var(--button-bg-hover); color: var(--button-ink-hover); }
.button:active { background: var(--button-bg-pressed); color: var(--button-ink-pressed); }
.button:disabled,
.button[aria-disabled="true"] { background: var(--button-bg-disabled); color: var(--button-ink-disabled); cursor: not-allowed; }

/* On an inverse band the resting button takes the page colours, so it doesn't vanish. */
.inverse { --button-bg: var(--surface); --button-ink: var(--ink); }

/* The logo variant: both logo files sit in the button; show the one that reads on the current fill. */
.button__logo { display: block; width: auto; height: var(--button-logo-height); }
.button__logo--black { display: none; }
[data-theme="dark"] .button__logo--white,
.inverse .button__logo--white { display: none; }
[data-theme="dark"] .button__logo--black,
.inverse .button__logo--black { display: block; }
[data-theme="dark"] .inverse .button__logo--white { display: block; }
[data-theme="dark"] .inverse .button__logo--black { display: none; }
.button:hover .button__logo--white { display: none; }
.button:hover .button__logo--black { display: block; }
.button:active .button__logo--white { display: block; }
.button:active .button__logo--black { display: none; }

/* ---------- Hover button (from Figma: hover button, node 428:2584) ---------- */
/* Over any [data-hover-button] area the pointer becomes a dot, and a label trails it.
   Mouse and trackpad only: components/bundle.js switches it on; touch screens never see it. */

.hover-button {
  position: fixed;
  inset: 0 auto auto 0;
  z-index: 1000;
  pointer-events: none;
  opacity: 0;
  transition: opacity 150ms ease-out;
}
.hover-button.is-visible { opacity: 1; }
.hover-button__dot,
.hover-button__label {
  position: absolute;
  top: 0;
  left: 0;
  will-change: transform;
}
.hover-button__dot { display: block; line-height: 0; }
.hover-button__dot-img { display: block; }
.hover-button__label {
  box-sizing: border-box;
  min-width: calc(var(--hover-label-min-width) + 2 * var(--hover-label-padding-inline));
  padding: var(--hover-label-padding-block) var(--hover-label-padding-inline);
  border-radius: var(--radius-none);
  font-family: var(--font-display);
  text-align: center;
  white-space: nowrap;
}
.hover-button--dark .hover-button__label { background: var(--hover-dark); color: var(--on-hover-dark); }
.hover-button--light .hover-button__label { background: var(--hover-light); color: var(--on-hover-light); }
.hover-button--dark .hover-button__dot-img--light,
.hover-button--light .hover-button__dot-img--dark { display: none; }

/* Inside a hover area the pointer is replaced by the dot, but only once the script is running. */
.has-hover-button [data-hover-button],
.has-hover-button [data-hover-button] * { cursor: none; }

/* A laid-out copy that doesn't move, for documentation and design review. */
.hover-button--static { position: static; display: inline-flex; align-items: flex-start; gap: var(--space-8); opacity: 1; }
.hover-button--static .hover-button__dot,
.hover-button--static .hover-button__label { position: static; }
.hover-button--static .hover-button__label { margin-top: var(--space-8); }

/* Touch screens get no hover button, so a section shows its own button inside this wrapper. */
.hover-button-touch { display: none; }
@media (hover: none), (pointer: coarse) {
  .hover-button-touch { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  .hover-button { transition: none; }
}

/* ---------- Cursor (the site-wide pointer) ---------- */
/* An 11px dot in ink replaces the pointer, swelling to 24px while it moves and settling back when it's still.
   components/bundle.js makes the element and switches it on: mouse and trackpad only, so touch screens never see it.
   It steps aside for the hover button's own dot and for text fields' text cursor (bundle.js), and while a modal dialog is open (below). */

.cursor-dot {
  position: fixed;
  inset: 0 auto auto 0;
  z-index: 1001;
  pointer-events: none;
  opacity: 0;
  transition: opacity 150ms ease-out;
}
.cursor-dot.is-visible { opacity: 1; }
.cursor-dot__circle {
  display: block;
  width: var(--cursor-dot-size);
  height: var(--cursor-dot-size);
  margin: calc(var(--cursor-dot-size) / -2) 0 0 calc(var(--cursor-dot-size) / -2);
  border-radius: 50%;
  background: var(--ink);
  transition: width 300ms ease-in-out, height 300ms ease-in-out, margin 300ms ease-in-out;
}
.cursor-dot.is-moving .cursor-dot__circle {
  width: var(--cursor-dot-size-moving);
  height: var(--cursor-dot-size-moving);
  margin: calc(var(--cursor-dot-size-moving) / -2) 0 0 calc(var(--cursor-dot-size-moving) / -2);
  transition-duration: 200ms;
  transition-timing-function: ease-out;
}
/* On the open menu, which is always black, the dot turns white. */
.menu-is-open .cursor-dot__circle { background: var(--menu-ink); }

/* The normal pointer is hidden while the dot runs, except in text fields, which keep their text cursor.
   A modal dialog sits above the whole page, the dot included, so while one is open the normal pointer comes back. */
.has-cursor-dot:not(:has(dialog[open])),
.has-cursor-dot:not(:has(dialog[open])) * { cursor: none; }
.has-cursor-dot:not(:has(dialog[open])) :is(input, textarea, select, [contenteditable="true"]) { cursor: auto; }
.has-cursor-dot:has(dialog[open]) .cursor-dot { display: none; }

@media (prefers-reduced-motion: reduce) {
  .cursor-dot,
  .cursor-dot__circle { transition: none; }
}

/* ---------- Navigation link (from Figma: navigation link, node 408:1734) ---------- */
/* For the open menu. On hover or keyboard focus the link slides right: the left padding grows,
   as in Figma, so the link stays under the pointer while it moves. */

.nav-link {
  display: flex;
  width: fit-content;
  align-items: center;
  gap: var(--nav-link-gap);
  padding-left: 0;
  color: var(--ink);
  text-decoration-line: none;
  white-space: nowrap;
  transition: padding-left 250ms ease-out;
}
.nav-link:hover,
.nav-link:focus-visible { padding-left: var(--nav-link-shift); }

@media (max-width: 1023px) {
  .nav-link-text { font-size: 72px; line-height: 1.18; }
  .nav-link-number { font-size: 20px; line-height: 1.18; }
}
@media (max-width: 767px) {
  .nav-link { gap: var(--nav-link-gap-phone); --nav-link-shift: var(--nav-link-gap-phone); }
  .nav-link-text { font-size: 48px; }
  .nav-link-number { font-size: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-link { transition: none; }
}

/* ---------- Menu (from Figma: menu open, node 284:761) ---------- */
/* The full-screen menu the logo button opens. components/bundle.js opens and closes it.
   Opening: the background fades in, the links rise one after another, then the logo and X fade in.
   Closing plays it backwards: logo and X go, the links leave from the last one up, then the background. */

.menu {
  position: fixed;
  inset: 0;
  z-index: 900;
  box-sizing: border-box;
  display: flex;
  padding: calc(var(--space-24) * 2 + 83px) var(--menu-inset);
  overflow-y: auto;
  background: var(--menu-bg);
  color: var(--menu-ink);
  --ink: var(--menu-ink);
  --focus: var(--menu-ink);
  opacity: 0;
  transition: opacity 250ms ease-in 300ms;
}
.menu[hidden] { display: none; }
.menu.is-open { opacity: 1; transition: opacity 300ms ease-out; }

.menu__nav { margin: auto; }
.menu__list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-32);
  padding: 0;
  list-style: none;
}
.menu__list > li {
  opacity: 0;
  transform: translateY(var(--space-24));
  transition: opacity 250ms ease-in, transform 250ms ease-in;
  transition-delay: calc(var(--menu-item-reverse, 0) * 60ms);
}
.menu.is-open .menu__list > li {
  opacity: 1;
  transform: none;
  transition: opacity 450ms ease-out, transform 450ms cubic-bezier(0.2, 0.7, 0.2, 1);
  transition-delay: calc(150ms + var(--menu-item, 0) * 80ms);
}

.menu__home,
.menu__close { opacity: 0; transition: opacity 200ms ease-in; }
.menu.is-open .menu__home,
.menu.is-open .menu__close { opacity: 1; transition: opacity 350ms ease-out 450ms; }

.menu__home {
  position: absolute;
  top: var(--space-24);
  left: var(--menu-inset);
  line-height: 0;
}
.menu__home img { display: block; }
.menu__close {
  position: absolute;
  top: var(--menu-close-top);
  right: calc(var(--menu-inset) - var(--menu-close-size) / 2);
  display: grid;
  place-items: center;
  width: var(--menu-close-size);
  height: var(--menu-close-size);
  padding: 0;
  border: 0;
  border-radius: var(--radius-none);
  background: none;
  color: inherit;
  cursor: pointer;
}

/* The page behind doesn't scroll while the menu is open. */
html.menu-is-open { overflow: hidden; }

@media (max-width: 767px) {
  .menu { --menu-inset: var(--menu-inset-phone); }
}
@media (prefers-reduced-motion: reduce) {
  .menu,
  .menu__list > li,
  .menu__home,
  .menu__close { transition: none !important; }
  .menu__list > li { transform: none; }
}

/* ---------- Header (from Figma: nav, node 284:701) ---------- */
/* The logo on the left. From 1024px: section links and a Contact me button on the right.
   Below 1024px: a Menu button instead, which opens the Menu with the same links. Scrolls away with the page.
   It shares menu-inset with the Menu, so the logo stays put when the menu opens over it. */

.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--header-gap);
  padding: var(--space-24) var(--menu-inset);
  color: var(--ink);
}
.site-header__home { display: block; line-height: 0; }
.site-header__actions { display: flex; align-items: center; gap: var(--header-gap); }
.site-header__links {
  display: flex;
  align-items: center;
  gap: var(--header-gap);
  padding: 0;
  list-style: none;
}
.site-header__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--space-48);
  color: inherit;
  text-decoration-line: none;
}
.site-header__link:hover {
  text-decoration-line: underline;
  text-decoration-thickness: var(--underline-width);
}
.site-header__menu-button { display: none; }

@media (max-width: 1023px) {
  .site-header__actions { display: none; }
  .site-header__menu-button { display: inline-flex; }
}
@media (max-width: 767px) {
  .site-header { padding-inline: var(--menu-inset-phone); }
}

/* Always the Menu-button layout, whatever the screen width. */
.site-header--compact .site-header__actions { display: none; }
.site-header--compact .site-header__menu-button { display: inline-flex; }

/* ---------- Work item (from Figma: work item, node 296:935) ---------- */
/* One project in the work list: a number, a title and a line underneath, linking to the case study.
   On hover or keyboard focus the row moves right and the project's image fades in at Figma's spot, top right of the item.
   Phones and touch screens never show the image. The line under each item is the system's one divider. */

.work-item {
  --work-indent-now: var(--work-item-indent);
  --work-shift-now: var(--work-item-shift);
  position: relative;
  display: block;
  padding: var(--space-24) 0 var(--space-32);
  border-bottom: 1px solid var(--work-divider);
  color: var(--ink);
  text-decoration-line: none;
}
.work-item__row {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: flex-start;
  gap: var(--space-32);
  padding-left: var(--work-indent-now);
  transition: padding-left 250ms ease-out;
}
.work-item:hover .work-item__row,
.work-item:focus-visible .work-item__row { padding-left: calc(var(--work-indent-now) + var(--work-shift-now)); }
.work-item__number { padding-top: var(--work-number-offset); color: var(--ink-muted); white-space: nowrap; }
.work-item__title { max-width: var(--work-title-width); }

/* Sits above the line and below the title, in Figma's spot: 57px above the top of the item, 62% across (871px of Figma's 1405). */
.work-item__image {
  position: absolute;
  z-index: 1;
  top: -57px;
  left: 62%;
  width: var(--work-image-width);
  height: var(--work-image-height);
  overflow: hidden;
  background: var(--surface-muted);
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease-out;
}
.work-item__image img { display: block; width: 100%; height: 100%; object-fit: cover; }
.work-item:hover .work-item__image,
.work-item:focus-visible .work-item__image { opacity: 1; }

@media (max-width: 1023px) {
  .work-item { --work-indent-now: 0px; --work-shift-now: var(--space-32); }
  .work-title { font-size: 48px; line-height: 1.18; }
  .work-number { font-size: 24px; line-height: 1.18; }
}
@media (max-width: 767px) {
  .work-item { --work-shift-now: 0px; }
  .work-item__row { gap: var(--space-16); }
  .work-title { font-size: 36px; }
  .work-number { font-size: 20px; }
}
/* No image on phones or touch screens. */
@media (max-width: 767px), (hover: none), (pointer: coarse) {
  .work-item__image { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .work-item__row,
  .work-item__image { transition: none; }
}

/* ---------- Bullet list (from Figma: bullet point items, node 508:6502) ---------- */
/* A 7px dot centred in a 21px slot, level with the middle of the first line, then the text.
   The dot takes the text's colour, so it follows the theme. Wrapped lines line up with the text, not the dot. */

.bullet-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding: 0;
  list-style: none;
}
.bullet-list > li {
  position: relative;
  padding-left: calc(var(--bullet-slot) + var(--space-8));
}
.bullet-list > li::before {
  content: "";
  position: absolute;
  top: var(--bullet-top);
  left: calc((var(--bullet-slot) - var(--bullet-size)) / 2);
  width: var(--bullet-size);
  height: var(--bullet-size);
  border-radius: 50%;
  background: currentColor;
}
