/* ==========================================================================
   Components: reusable building blocks shared by every page.
   ========================================================================== */

/* --- Container ---------------------------------------------------------- */
.container {
  width: 100%;
  max-width: calc(var(--container-width, var(--width-page)) + var(--gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.container--content { --container-width: var(--width-content); }
.container--wide    { --container-width: var(--width-wide); }

/* --- Trimmed text: removes the font's built-in space above caps and below
       the baseline, so gaps match Figma's "text-box-trim". Browsers without
       support simply show slightly larger gaps. --------------------------- */
.trim {
  text-box: trim-both cap alphabetic;
}

/* --- Arrow -------------------------------------------------------------- */
/* Uses the exported Figma asset as a mask, so it takes the text color in
   any theme without editing the file. */
.arrow {
  display: block;
  flex: none;
  width: var(--arrow-size, 1em);
  aspect-ratio: 1;
  background-color: currentColor;
  -webkit-mask: url("../img/arrow.svg") no-repeat center / contain;
  mask: url("../img/arrow.svg") no-repeat center / contain;
}

/* --- Display link: big headline + light subtitle (the hub's main links,
       reusable for project lists later). Carries an optional small arrow on
       the right (__arrow); pages can hide it and use their own. ---------- */
.display-link {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  column-gap: var(--space-4);
  color: var(--color-text);
  text-decoration: none;
}

.display-link__arrow {
  --arrow-size: calc(var(--text-display) * 0.5);   /* ~ cap height */
}

.display-link__text {
  display: flex;
  flex-direction: column;
  gap: calc(var(--text-display) / 3);   /* Figma: 48px @ 144px */
}

.display-link__title {
  font-size: var(--text-display);
  font-weight: var(--weight-regular);
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

.display-link__subtitle {
  font-size: var(--text-display-sub);
  font-weight: var(--weight-light);
  line-height: 1.1;
  color: var(--color-text);
  transition: color var(--duration-base) var(--ease-out);
}

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

/* Keep each line whole on wide screens, as in Figma. */
@media (min-width: 75rem) {
  .display-link__title,
  .display-link__subtitle {
    white-space: nowrap;
  }
}

/* --- Text link ---------------------------------------------------------- */
.text-link {
  color: var(--color-text);
  text-decoration: underline;
  text-decoration-color: transparent;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25em;
  transition: text-decoration-color var(--duration-fast) var(--ease-out);
}

.text-link:hover {
  text-decoration-color: currentColor;
}

/* --- Entrance animation (off automatically with reduced motion) --------- */
@media (prefers-reduced-motion: no-preference) {
  .reveal {
    animation: reveal var(--duration-slow) var(--ease-out) both;
    animation-delay: calc(var(--reveal-index, 0) * 90ms);
  }

  .reveal--1 { --reveal-index: 1; }
  .reveal--2 { --reveal-index: 2; }
  .reveal--3 { --reveal-index: 3; }

  @keyframes reveal {
    from {
      opacity: 0;
      transform: translateY(12px);
    }
  }
}

/* --- Scramble text (see /assets/js/scramble.js) ------------------------- */
/* Each letter decodes in two phases:
   - is-ghost: random glyphs in white at low opacity
   - is-green: the real letter in solid accent green
   Then it fades to full white. */
.scramble-char {
  display: inline-block;
  text-align: center;
  white-space: pre;
  transition:
    color 300ms var(--ease-out),
    opacity 300ms var(--ease-out);
}

.scramble-char.is-green {
  color: var(--color-accent);
  opacity: 1;
  transition: none;
}

.scramble-char.is-ghost {
  color: var(--color-text);
  opacity: var(--ghost-opacity, 0.3);   /* set per letter by scramble.js */
  transition: none;
}

/* --- Custom cursor (see /assets/js/cursor.js) ---------------------------
   All measures are "base px × --u" (base = 1440 frame), so the cursor
   scales with the layout. */
:root {
  --cursor-size:   calc(50 * var(--u));
  --cursor-stroke: max(1.5px, calc(2.5 * var(--u)));
}

/* Crosshair: 50 × 50, 2.5 stroke, 5 centre gap at base size. */
.cursor {
  --cursor-gap:  calc(5 * var(--u));    /* centre gap at rest */
  --cursor-open: calc(8 * var(--u));    /* ?cursor=classic: extra distance per arm on hover */
  --cursor-arm:  calc((var(--cursor-size) - var(--cursor-gap)) / 2);

  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  width: var(--cursor-size);
  height: var(--cursor-size);
  margin: calc(var(--cursor-size) / -2) 0 0 calc(var(--cursor-size) / -2);
  color: var(--color-text);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--duration-fast) linear;
  will-change: transform;
}

.cursor.is-visible {
  opacity: 1;
}

.cursor__arm {
  position: absolute;
  background: currentColor;
  transition:
    transform var(--duration-base) var(--ease-out),
    opacity var(--duration-fast) linear;
}

.cursor__arm--top,
.cursor__arm--bottom {
  left: calc(50% - var(--cursor-stroke) / 2);
  width: var(--cursor-stroke);
  height: var(--cursor-arm);
}

.cursor__arm--left,
.cursor__arm--right {
  top: calc(50% - var(--cursor-stroke) / 2);
  width: var(--cursor-arm);
  height: var(--cursor-stroke);
}

.cursor__arm--top    { top: 0; }
.cursor__arm--bottom { bottom: 0; }
.cursor__arm--left   { left: 0; }
.cursor__arm--right  { right: 0; }

/* Classic mode: arms slide outwards over links. */
.cursor.is-open .cursor__arm--top    { transform: translateY(calc(var(--cursor-open) * -1)); }
.cursor.is-open .cursor__arm--bottom { transform: translateY(var(--cursor-open)); }
.cursor.is-open .cursor__arm--left   { transform: translateX(calc(var(--cursor-open) * -1)); }
.cursor.is-open .cursor__arm--right  { transform: translateX(var(--cursor-open)); }

/* Live coordinates, bottom-right of the crosshair. */
.cursor__coords {
  position: absolute;
  top: 100%;
  left: 100%;
  margin: calc(2 * var(--u)) 0 0 calc(2 * var(--u));
  font-size: max(10px, calc(13 * var(--u)));
  font-weight: var(--weight-light);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  line-height: 1;
  white-space: pre;
  color: var(--color-text-muted);
  transition: opacity var(--duration-fast) linear;
}

/* Over a link the crosshair steps aside for the selection frame. */
.cursor.is-snapped .cursor__arm {
  opacity: 0;
  transform: scale(0.4);
}

.cursor.is-snapped .cursor__coords {
  opacity: 0;
}

/* Selection frame: four corner brackets that snap around the hovered link.
   Position/size come from JS as --fx / --fy / --fw / --fh. */
.cursor-frame {
  --corner: calc(16 * var(--u));

  position: fixed;
  top: 0;
  left: 0;
  z-index: 1000;
  width: var(--fw, 0px);
  height: var(--fh, 0px);
  transform: translate3d(var(--fx, 0px), var(--fy, 0px), 0);
  color: var(--color-text);
  pointer-events: none;
  opacity: 0;
  transition:
    transform 420ms var(--ease-out),
    width 420ms var(--ease-out),
    height 420ms var(--ease-out),
    opacity var(--duration-fast) linear;
}

.cursor-frame.is-active {
  opacity: 1;
}

.cursor-frame.is-placing {
  transition: none;
}

.cursor-frame__corner {
  position: absolute;
  width: var(--corner);
  height: var(--corner);
  border: 0 solid currentColor;
}

.cursor-frame__corner--tl { top: 0;    left: 0;  border-top-width: var(--cursor-stroke);    border-left-width: var(--cursor-stroke); }
.cursor-frame__corner--tr { top: 0;    right: 0; border-top-width: var(--cursor-stroke);    border-right-width: var(--cursor-stroke); }
.cursor-frame__corner--bl { bottom: 0; left: 0;  border-bottom-width: var(--cursor-stroke); border-left-width: var(--cursor-stroke); }
.cursor-frame__corner--br { bottom: 0; right: 0; border-bottom-width: var(--cursor-stroke); border-right-width: var(--cursor-stroke); }

/* Destination label under the frame (e.g. "Figma ↗"). */
.cursor-frame__label {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: calc(18 * var(--u));
  font-size: max(11px, calc(16 * var(--u)));
  font-weight: var(--weight-light);
  line-height: 1;
  white-space: nowrap;
  color: var(--color-text);
}

/* Compact frame for small links: shorter corners, label above (small links
   sit in rows next to other text, and often near the bottom edge). */
.cursor-frame.is-compact {
  --corner: calc(7 * var(--u));
}

.cursor-frame.is-compact .cursor-frame__label {
  top: auto;
  bottom: 100%;
  margin: 0 0 calc(10 * var(--u));
}

/* Hide the system cursor only when the custom one is running. */
@media (hover: hover) and (pointer: fine) {
  .has-custom-cursor,
  .has-custom-cursor * {
    cursor: none !important;
  }
}
