/* ==========================================================================
   Portfolio — home page
   Figma: Portfolio / Home page (frame 157:2583, 1400 × 750)
   ========================================================================== */

/* Fonts (self-hosted, SIL Open Font License) -------------------------------- */

@font-face {
  font-family: "DM Mono";
  src: url("assets/fonts/dm-mono-latin-400-normal.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Tokens -------------------------------------------------------------------- */

:root {
  --ink: #000;
  --paper: #fff;
  --sand: #feead4;
  --tag-on-dark: #4d4d4d;

  --font-mono: "DM Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  --text-lg: 24px;
  --text-md: 18px;
  --text-sm: 15px;

  --gap: 40px;
  --radius-card: 12px;
  --radius-tag: 4px;
  --rule: 1.2px;
}

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

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

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  background: var(--paper);
  color: var(--ink);
  font: 400 var(--text-sm) / 1 var(--font-mono);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1,
h2,
p,
ul {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

a {
  color: inherit;
}

img {
  display: block;
}

/* Cap-height trimming, as set on the Figma text layers ("leading trim: cap
   height"): the box hugs the capitals instead of the full line-height. */
.trim {
  text-box: trim-both cap alphabetic;
}

/* Fallback for browsers without text-box-trim, from DM Mono's metrics
   (ascent 0.992, descent 0.310, cap height 0.700, at line-height 1). */
@supports not (text-box: trim-both cap alphabetic) {
  .trim::before,
  .trim::after {
    content: "";
    display: table;
  }
  .trim::before {
    margin-bottom: -0.141em;
  }
  .trim::after {
    margin-top: -0.159em;
  }
}

/* Page layout --------------------------------------------------------------- */

.home {
  display: flex;
  gap: var(--gap);
  padding: 48px 24px;
}

/* Intro (left column) ------------------------------------------------------- */

.intro {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding-block: 40px;
}

.intro__about {
  display: flex;
  flex-direction: column;
  gap: 36px;
}

.intro__text {
  display: flex;
  flex-direction: column;
  gap: 24px;
  width: 248.64px;
}

.intro__lead {
  font-size: var(--text-lg);
  font-weight: 400;
  line-height: 1;
}

.intro__open {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.intro__status {
  width: 222px;
  /* Trimmed like the other text, then padded back to Figma's 15px line box. */
  padding-block: 2.5px 2px;
}

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

.link {
  display: flex;
  align-items: center;
  gap: 5px;
  min-height: 15px;
  text-decoration: none;
  white-space: nowrap;
  transition: opacity 0.15s ease;
}

.link__icon {
  flex: none;
  width: 14px;
  height: 14px;
}

.link__label {
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-position: from-font;
}

.link:hover {
  opacity: 0.6;
}

.link:focus-visible,
.card:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

.link:focus-visible {
  border-radius: 2px;
}

/* Work (right column) ------------------------------------------------------- */

.work {
  display: grid;
  grid-template-columns: 316fr 474fr;
  gap: var(--gap);
  width: 830px;
}

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

.card {
  /* Per-card geometry of the title row, measured from the Figma layers. */
  --rule-inset-start: 7.88px; /* card edge → short leading rule */
  --rule-lead: 10.12px;       /* length of the leading rule */
  --name-gap-start: 4px;      /* leading rule → name */
  --name-gap-end: 0px;        /* name → trailing rule */
  --rule-inset-end: 7.84px;   /* trailing rule → card edge */
  --tags-inset: 14px;
  --meta-gap: 13.82px;        /* name baseline → tags */
  --meta-bottom: 19px;        /* tags → card edge */
  --title-size: var(--text-lg);

  position: relative;
  display: block;
  overflow: hidden;
  border-radius: var(--radius-card);
  text-decoration: none;
}

.card__art {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.card__art img {
  position: absolute;
  max-width: none;
}

.card:hover .card__art {
  transform: scale(1.025);
}

.card__meta {
  position: absolute;
  inset: auto 0 0;
  display: flex;
  flex-direction: column;
  gap: var(--meta-gap);
  padding-bottom: var(--meta-bottom);
}

.card__title {
  display: flex;
  align-items: center;
  padding-inline: var(--rule-inset-start) var(--rule-inset-end);
  font-size: var(--title-size);
  font-weight: 400;
  line-height: 1;
}

/* The rules either side of the name. Like the round-capped strokes in Figma,
   each one reaches half its thickness past both of its end points. */
.card__title::before,
.card__title::after {
  --cap: calc(var(--rule) / 2);
  content: "";
  height: var(--rule);
  border-radius: var(--cap);
  background: currentColor;
}

.card__title::before {
  flex: 0 0 calc(var(--rule-lead) + var(--rule));
  margin-left: calc(-1 * var(--cap));
  margin-right: calc(var(--name-gap-start) - var(--cap));
}

.card__title::after {
  flex: 1 1 0;
  min-width: 0;
  margin-left: calc(var(--name-gap-end) - var(--cap));
  margin-right: calc(-1 * var(--cap));
}

.card__name {
  display: block;
  white-space: nowrap;
}

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  padding-inline-start: var(--tags-inset);
}

.tag {
  /* 27px tall: Figma rounds the 10.5px cap height up to 11px and the extra
     half pixel sits above the text. */
  padding: 8.5px 8px 8px;
  border-radius: var(--radius-tag);
  background: var(--ink);
  color: var(--paper);
  white-space: nowrap;
}

/* Card: Communi ------------------------------------------------------------- */

.card--communi {
  aspect-ratio: 316 / 414;
  background: var(--paper);
  color: var(--ink);
}

/* The art is its own blending group: the noise (soft-light) and the knocked-out
   tagline (difference) blend with the layers below them and nothing else. */
.card--communi .card__art {
  isolation: isolate;
  background: var(--paper);
}

.communi__gradient {
  inset: 0;
  width: 100%;
  height: 100%;
}

.communi__noise {
  top: 0;
  left: 0;
  width: 100%;
  height: calc(395 / 414 * 100%);
  mix-blend-mode: soft-light;
}

.communi__mark {
  top: 0;
  left: 0;
  width: 100%;
  height: calc(400 / 414 * 100%);
}

.communi__tagline {
  left: calc(53.638 / 316 * 100%);
  top: calc(147.592 / 414 * 100%);
  width: calc(209 / 316 * 100%);
  height: auto;
}

/* Drawn in the card's own 316 × 414 coordinate space (see the viewBox). */
.communi__knockout {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  mix-blend-mode: difference;
}

/* Card: Virtual Event Summit ------------------------------------------------ */

.card--ves {
  --rule-inset-start: 8.03px;
  --rule-lead: 17.4px;
  --name-gap-start: 4px;
  --name-gap-end: 6.38px;
  --rule-inset-end: 9.25px;
  --tags-inset: 22px;

  aspect-ratio: 474 / 414;
  background: var(--ink);
  color: var(--paper);
}

.card--ves .tag {
  background: var(--tag-on-dark);
}

.ves__particles {
  top: calc(19.75 / 414 * 100%);
  left: 0;
  width: 100%;
  height: calc(304 / 414 * 100%);
}

.ves__wordmark {
  left: calc(217.142 / 474 * 100%);
  top: calc(162.652 / 414 * 100%);
  width: calc(208 / 474 * 100%);
  height: auto;
}

/* Card: Crowd Conversion ---------------------------------------------------- */

.card--crowd {
  --name-gap-start: 6.87px;
  --name-gap-end: 5.38px;
  --rule-inset-end: 12px;
  --meta-gap: 15.82px;
  --meta-bottom: 17.18px;
  --title-size: var(--text-md);

  grid-column: 1 / -1;
  height: 200px;
  background: var(--sand);
  color: var(--ink);
}

.crowd__logo {
  left: 22px;
  top: 23.46px;
}

/* On hover the artwork grows out from the centre of the logo (its 182 × 88
   box at 22, 23.46) rather than from the middle of the card, so the logo
   stays put and the dots drift away from it. */
.card--crowd .card__art {
  transform-origin: 113px 67.46px;
}

.crowd__dot--1 { left: 320.56px; top: 68.04px; }
.crowd__dot--2 { left: 411.25px; top: 26.67px; }
.crowd__dot--3 { left: 782.56px; top: 56.5px; }
.crowd__dot--4 { left: 551.97px; top: 87.45px; }

/* Responsive ---------------------------------------------------------------- */

/* Not enough room for both columns: intro moves above the work. */
@media (max-width: 1229.98px) {
  .home {
    flex-direction: column;
    gap: 56px;
    width: min(100%, 830px + 48px);
  }

  /* The intro sits on the same two columns as the cards under it, which puts
     the "Open to…" block level with the left edge of the Virtual Event Summit
     card. (minmax(0, …) keeps the columns exactly as wide as the cards'
     whatever is in them: the intro text is wider than the first column and
     simply runs on past it, above the "Open to…" block.) */
  .intro {
    display: grid;
    grid-template-columns: minmax(0, 316fr) minmax(0, 474fr);
    align-items: end;
    gap: var(--gap);
    padding-block: 0;
  }

  /* With the width of the page to spread into, the intro text gets twice the
     line length it has in the desktop column. (On phones, below, it runs the
     full width of the column instead.) */
  .intro__text {
    width: calc(2 * 248.64px);
  }

  /* The status line takes the width of its column instead of its desktop
     222px, so it needs fewer lines and stays clear of the intro text above. */
  .intro__status {
    width: auto;
  }

  .work {
    width: 100%;
  }
}

/* Small tablets and phones: one column. */
@media (max-width: 809.98px) {
  .home {
    gap: 48px;
    width: min(100%, 474px + 40px);
    padding: 32px 20px;
  }

  /* The intro's wrappers step aside here, so its three parts line up in the
     page column next to the cards and can be put in a different order:
     the text, then the cards, then the two groups of links. */
  .intro,
  .intro__about {
    display: contents;
  }

  .intro__text {
    order: 1;
  }

  /* The cards come 32px under the text, which is how far the text itself is
     from the top of the page: the 48px gap, less 16px. */
  .work {
    order: 2;
    margin-top: -16px;
  }

  .intro__about > .links {
    order: 3;
  }

  .intro__open {
    order: 4;
  }

  /* The intro text runs the full width of the column here, instead of
     keeping the fixed line length it has on desktop. */
  .intro__text,
  .intro__status {
    width: auto;
  }

  .work {
    grid-template-columns: 1fr;
    gap: 24px;
  }

  .card--crowd {
    grid-column: auto;
  }

  /* The name and tags keep their size while the artwork shrinks, so here the
     Virtual Event Summit card stacks them under the artwork instead of on top
     of it. At the design width (474px) this gives the same 414px card. */
  .card--ves {
    aspect-ratio: auto;
    padding-top: calc(323.36 / 474 * 100%);
  }

  .card--ves .card__art {
    inset: 0 0 auto;
    aspect-ratio: 474 / 414;
  }

  .card--ves .card__meta {
    position: static;
    margin-top: 14px;
  }
}

/* Very narrow phones: tighten the card labels so they still fit on one line. */
@media (max-width: 359.98px) {
  .card--communi,
  .card--ves {
    --title-size: 20px;
  }

  .tags {
    gap: 6px;
  }

  .tag {
    padding-inline: 6px;
  }
}
