/* ===========================================================
   David Swales — Portfolio
   =========================================================== */

@import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&family=Space+Grotesk:wght@400;500;700&display=swap');

:root {
  --purple-1: 26, 0, 104;
  --purple-2: 37, 0, 71;
  --purple-3: 18, 0, 35;
  --purple-4: 9, 0, 18;
  --purple-5: 5, 0, 9;

  --grey-50: #ffffff;
  --grey-100: #e4e4e4;
  --grey-300: #9a9a9a;
  --grey-700: #4e4e4e;
  --grey-900: #151515;

  --blue-200: #8ed6ff;
  --blue-300: #34b5ff;
  --blue-500: #1f88ff;
  --blue-700: #0073f6;

  --green-300: #7ed796;
  --green-500: #56cb75;
  --green-900: #2c9047;

  --card-max: 1104px;
  --grid-size: 5px;
  --pad: clamp(16px, 3.6vw, 32px);
  --gap-section: clamp(16px, 3vw, 24px);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  min-height: 100%;
}

body {
  background: #000;
  color: var(--grey-100);
  font-family: 'Space Grotesk', sans-serif;
  font-feature-settings: 'onum' 1, 'pnum' 1;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(20px, 6vh, 64px) clamp(16px, 5vw, 48px);
  overflow-x: hidden;
}

@media (max-width: 799.98px) {
  body {
    padding-top: 24px;
    padding-bottom: 24px;
  }
}

a { color: inherit; }

/* -----------------------------------------------------------
   Full-viewport background: ambient glow, cursor glow, grid
   ----------------------------------------------------------- */

.bg-layer {
  position: fixed;
  inset: 0;
  pointer-events: none;
}

#bg-ambient {
  z-index: 0;
  background: radial-gradient(
    ellipse 60vw 58vh at 50% 48%,
    rgba(var(--purple-2), 0.85) 0%,
    rgba(var(--purple-3), 0.9) 40%,
    rgba(var(--purple-4), 0.95) 70%,
    rgba(var(--purple-5), 1) 88%,
    #000 100%
  );
}

#bg-cursor-glow {
  z-index: 1;
  background: radial-gradient(
    circle 210px at var(--mx, 50%) var(--my, 50%),
    rgba(150, 80, 255, 0.15),
    rgba(150, 80, 255, 0.055) 45%,
    transparent 70%
  );
  mix-blend-mode: screen;
  transition: opacity 0.4s ease;
  opacity: var(--glow-opacity, 0);
}

#bg-grid {
  z-index: 2;
  background-image:
    linear-gradient(to right, rgba(255, 255, 255, 0.07) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 255, 255, 0.07) 1px, transparent 1px);
  background-size: var(--grid-size) var(--grid-size);
  mask-image: radial-gradient(ellipse 75vw 75vh at 50% 50%, black 0%, black 55%, transparent 100%);
  -webkit-mask-image: radial-gradient(ellipse 75vw 75vh at 50% 50%, black 0%, black 55%, transparent 100%);
}

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

.card-wrap {
  position: relative;
  z-index: 10;
  width: 100%;
  max-width: var(--card-max);
}

.card {
  position: relative;
  z-index: 2;
  border-radius: 16px;
  border: 1.5px solid transparent;
  /* Fill is near-opaque and nearly neutral at the edges/corners (traced from the Figma
     PDF export — corners sample as flat ~rgb(17,17,17), no purple bleed-through) with
     only a soft blue-violet glow pooling behind the centre. A faint diagonal sheen and
     a two-tone (blue/violet) border gradient approximate the "Glass" effect settings
     (Light -29°/80%, Frost 60, Dispersion 50, Refraction 80, Depth 1). */
  background:
    linear-gradient(-29deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.015) 22%, transparent 42%) padding-box,
    radial-gradient(ellipse 30% 26% at 50% 42%, rgba(64, 24, 190, 0.15) 0%, rgba(64, 24, 190, 0.05) 55%, transparent 85%) padding-box,
    linear-gradient(rgba(17, 17, 20, 0.99), rgba(17, 17, 20, 0.99)) padding-box,
    linear-gradient(
      160deg,
      rgba(225, 242, 255, 0.95) 0%,
      rgba(120, 190, 255, 0.65) 16%,
      rgba(0, 61, 96, 0.5) 38%,
      rgba(150, 90, 220, 0.38) 58%,
      rgba(0, 61, 96, 0.5) 76%,
      rgba(180, 140, 255, 0.6) 100%
    ) border-box;
  backdrop-filter: blur(8px) saturate(120%);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
  box-shadow:
    0 40px 100px rgba(0, 0, 0, 0.65),
    0 8px 24px rgba(0, 0, 0, 0.45),
    0 0 60px rgba(90, 40, 200, 0.12),
    0 0 20px rgba(34, 117, 212, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 0 30px rgba(70, 20, 180, 0.04);
  padding: var(--pad);
  display: flex;
  flex-direction: column;
  gap: var(--gap-section);
}

/* The card is nearly opaque (see above), so the page's cursor-follow glow behind it
   would otherwise just vanish while the pointer is over the card. This paints a matching
   low-opacity glow directly onto the card's own surface at the same pointer position, so
   it reads as the glow continuing through a touch of glass transparency rather than
   stopping dead at the card's edge. Tracked in script.js (--card-mx/--card-my, and
   --card-glow-opacity toggled on pointer enter/leave). */
.card-cursor-glow {
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  border-radius: inherit;
  overflow: hidden;
  background: radial-gradient(
    circle 220px at var(--card-mx, 50%) var(--card-my, 50%),
    rgba(150, 80, 255, 0.11),
    rgba(150, 80, 255, 0.035) 45%,
    transparent 70%
  );
  mix-blend-mode: screen;
  opacity: var(--card-glow-opacity, 0);
  transition: opacity 0.35s ease;
}

/* Soft ambient wash centred behind the card — the "purple glow" reads as coming from
   the card itself rather than the whole page (page-wide ambient is kept minimal, see #bg-ambient) */
.card-ambient-glow {
  position: absolute;
  inset: -40px -50px;
  z-index: 0;
  pointer-events: none;
  border-radius: 40%;
  filter: blur(90px);
  background: radial-gradient(
    ellipse at center,
    rgba(140, 75, 235, 0.5) 0%,
    rgba(110, 55, 210, 0.28) 40%,
    rgba(90, 45, 190, 0.12) 65%,
    transparent 80%
  );
}

/* On narrow viewports the card-wrap has little horizontal margin around it (as
   little as ~16px), so the glow's -50px horizontal bleed pushes past the edge of
   the screen and forces the whole page to scroll horizontally, throwing off the
   card's apparent centring. Pull the bleed in to fit safely inside that margin. */
@media (max-width: 480px) {
  .card-ambient-glow {
    inset: -40px -12px;
  }
}

/* Soft glow pooling beneath the card's bottom edge, like it's floating over a light source */
.card-glow {
  position: absolute;
  left: 4%;
  right: 4%;
  bottom: -40px;
  height: 100px;
  z-index: 0;
  pointer-events: none;
  border-radius: 50%;
  filter: blur(38px);
  background: radial-gradient(
    ellipse at center,
    rgba(160, 100, 255, 0.65) 0%,
    rgba(120, 70, 230, 0.36) 40%,
    rgba(90, 45, 190, 0.16) 65%,
    transparent 80%
  );
}

/* -----------------------------------------------------------
   Header: avatar + identity + CTAs
   ----------------------------------------------------------- */

.header {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.identity {
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 24px);
}

.avatar {
  position: relative;
  width: 92px;
  height: 92px;
  flex-shrink: 0;
}

.avatar__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(
    from 0deg,
    rgba(0, 61, 96, 0.5) 0%,
    rgba(52, 181, 255, 0.9) 25%,
    rgba(0, 61, 96, 0.5) 50%,
    rgba(52, 181, 255, 0.9) 75%,
    rgba(0, 61, 96, 0.5) 100%
  );
  box-shadow: 0 0 16px 0 rgba(34, 117, 212, 0.45);
  animation: ring-spin 7s linear infinite;
}

@keyframes ring-spin {
  to { transform: rotate(360deg); }
}

.avatar__photo {
  position: absolute;
  inset: 2px;
  border-radius: 50%;
  overflow: hidden;
  background: #111;
}

.avatar__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.identity__text {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "name chip"
    "role role"
    "loc  loc";
  row-gap: 4px;
  column-gap: 12px;
  align-items: center;
}

.identity__name {
  grid-area: name;
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 400;
  font-size: 20px;
  line-height: 24px;
  color: var(--grey-100);
  margin: 0;
  white-space: nowrap;
}

.identity__role,
.identity__loc {
  font-family: 'Space Mono', monospace;
  font-size: 12px;
  line-height: 20px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--grey-300);
  margin: 0;
  font-feature-settings: 'dlig' 1;
}

.identity__role { grid-area: role; }
.identity__loc { grid-area: loc; }

/* Status chip with tooltip */
.chip {
  grid-area: chip;
  position: relative;
  display: inline-flex;
  width: fit-content;
}

.chip__pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 12px;
  border-radius: 16px;
  border: 1px solid var(--green-900);
  font-family: 'Space Mono', monospace;
  font-size: 12px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--green-500);
  font-feature-settings: 'dlig' 1;
  cursor: default;
  transition: border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}

.chip__dot {
  width: 8px;
  height: 8px;
  color: var(--green-500);
  flex-shrink: 0;
}

.chip:hover .chip__pill,
.chip:focus-visible .chip__pill {
  border-color: var(--green-500);
  color: var(--green-300);
  box-shadow: 0 0 12px 0 var(--green-300);
}

.chip:hover .chip__dot,
.chip:focus-visible .chip__dot {
  color: var(--green-300);
}

/* Generic tooltip */
.tooltip {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  /* --tooltip-shift is set by script.js right before the tooltip opens, nudging it
     back onto screen when centering it under a trigger near the edge of the
     viewport would otherwise push it off (see clampTooltip in script.js). */
  transform: translateX(calc(-50% + var(--tooltip-shift, 0px))) translateY(4px);
  background: var(--grey-900);
  border: 1px solid var(--grey-700);
  border-radius: 4px;
  padding: 4px 8px;
  font-family: 'Space Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.6px;
  color: var(--grey-100);
  white-space: nowrap;
  text-transform: none;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.15s ease, transform 0.15s ease;
  z-index: 20;
}

.chip:hover .tooltip,
.chip:focus-visible .tooltip,
.redacted:hover .tooltip,
.redacted:focus-visible .tooltip,
.chip.is-open .tooltip,
.redacted.is-open .tooltip {
  opacity: 1;
  visibility: visible;
  transform: translateX(calc(-50% + var(--tooltip-shift, 0px))) translateY(0);
}

/* CTAs */
.ctas {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.btn {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 12px 12px 12px 16px;
  border-radius: 4px;
  font-family: 'Space Mono', monospace;
  font-size: 12px;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  border: none;
  transition: background 0.2s ease, box-shadow 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.btn__texture {
  position: absolute;
  inset: -1px;
  opacity: 0.05;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    var(--grey-50) 0px,
    var(--grey-50) 1px,
    transparent 1px,
    transparent 3.5px
  );
}

.btn svg { width: 16px; height: 16px; position: relative; z-index: 1; }
.btn span:not(.btn__texture) { position: relative; z-index: 1; }

.btn--secondary {
  border: 1px solid var(--blue-700);
  color: var(--blue-300);
  box-shadow: 0 0 8px 0 rgba(34, 117, 212, 0.45);
}

.btn--secondary:hover,
.btn--secondary:focus-visible {
  background: rgba(31, 136, 255, 0.2);
  color: var(--blue-200);
  box-shadow: 0 0 16px 0 rgba(34, 117, 212, 0.55);
}

.btn--primary {
  color: var(--grey-50);
  background: linear-gradient(102deg, #0073f6 0%, #005fed 50%, #0069e0 100%);
  box-shadow: 0 0 8px 0 rgba(34, 117, 212, 0.45);
}

.btn--primary:hover,
.btn--primary:focus-visible {
  /* A gradient-to-solid-color swap can't be interpolated by the browser and snaps
     mid-transition, reading as a flicker — keep hover a gradient too so it crossfades. */
  background: linear-gradient(102deg, var(--blue-500) 0%, var(--blue-300) 50%, var(--blue-500) 100%);
  box-shadow: 0 0 16px 0 rgba(34, 117, 212, 0.55);
}

/* -----------------------------------------------------------
   Separator
   ----------------------------------------------------------- */

/* Heading rule: a hairline sits above the label itself (its width tracks the label,
   via ::before on .section__heading), eases through a small rounded corner (traced
   from the Figma PDF export), then continues as a hairline across the rest of the
   card. The whole stroke is one continuous white gradient that fades in, peaks past
   its midpoint, and fades out again — no boxed-in "wrap" around the label. */
.section__heading-row {
  display: flex;
  align-items: stretch;
  height: 24px;
}

.heading-corner {
  width: 27px;
  height: 24px;
  flex-shrink: 0;
  display: block;
  overflow: visible;
}

.heading-corner path {
  stroke: url(#headingLineGrad);
  stroke-width: 1;
  fill: none;
  vector-effect: non-scaling-stroke;
}

.heading-line {
  flex: 1 1 auto;
  align-self: flex-end;
  height: 1px;
  background: linear-gradient(
    to right,
    rgba(255, 255, 255, 0.08) 0%,
    rgba(255, 255, 255, 0.14) 15%,
    rgba(255, 255, 255, 0.19) 45%,
    rgba(255, 255, 255, 0.17) 60%,
    rgba(255, 255, 255, 0.1) 80%,
    rgba(255, 255, 255, 0.02) 100%
  );
}

/* -----------------------------------------------------------
   Content sections
   ----------------------------------------------------------- */

.section {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.section__heading {
  position: relative;
  font-family: 'Space Mono', monospace;
  font-weight: 400;
  font-size: 12px;
  line-height: 20px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--grey-300);
  margin: 0;
  font-feature-settings: 'dlig' 1;
  display: flex;
  align-items: flex-start;
  white-space: nowrap;
  /* extra top padding clears the hairline above it; the hairline itself (::before)
     is absolutely positioned so it stays put at the top edge regardless of this */
  padding: 6px 10px 0 0;
}

/* Hairline above the label only — no left/vertical edge, so it reads as a rule the
   label sits on rather than a box wrapped around it. */
.section__heading::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.08) 100%);
}

.section__body {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 14px;
  line-height: 20px;
  color: var(--grey-100);
  font-feature-settings: 'onum' 1, 'pnum' 1;
}

.section__body p {
  margin: 0 0 8px 0;
}

.section__body p:last-child { margin-bottom: 0; }

.section__body strong { font-weight: 700; }

/* Redacted bar */
.redacted {
  position: relative;
  display: inline-flex;
  cursor: default;
}

.redacted__bar {
  display: inline-block;
  background: #000;
  border-radius: 2px;
  color: transparent;
  user-select: none;
  padding: 0 2px;
  transition: box-shadow 0.2s ease;
}

.redacted:hover .redacted__bar,
.redacted:focus-visible .redacted__bar {
  box-shadow: 0 0 0 1px var(--grey-700);
}

/* Invisible easter-egg link */
.stealth-link {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
}

/* Focus area tiles */
.tiles {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.tile {
  padding: 8px 12px 8px 16px;
  border-left: 4px solid var(--blue-500);
  border-radius: 4px;
  background: linear-gradient(110deg, rgba(85, 85, 85, 0.3) 2%, rgba(85, 85, 85, 0.4) 100%);
  font-family: 'Space Mono', monospace;
  font-size: 12px;
  letter-spacing: 0.6px;
  color: var(--grey-100);
  transition: background 50ms ease-in-out;
  cursor: default;
  /* the whole padding box (including the small gaps the border-radius rounds off) is the
     hit area, so hover can't flicker off while the cursor sits anywhere inside the tile */
}

.tile:hover,
.tile:focus-visible,
.tile.is-hover {
  /* A flat 5% white wash layered over the base gradient — both hover and rest states
     stay gradients so the transition crossfades instead of snapping. */
  background:
    linear-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05)),
    linear-gradient(110deg, rgba(85, 85, 85, 0.3) 2%, rgba(85, 85, 85, 0.4) 100%);
}

/* Links row */
.links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-top: 20px;
}

.links__label {
  font-family: 'Space Mono', monospace;
  font-size: 12px;
  line-height: 20px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--grey-300);
  font-feature-settings: 'dlig' 1;
}

.links__list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.link-anim {
  display: inline-flex;
  align-items: center;
  font-family: 'Space Mono', monospace;
  font-size: 12px;
  line-height: 20px;
  letter-spacing: 2.4px;
  text-transform: uppercase;
  text-decoration: underline;
  text-decoration-style: dotted;
  color: var(--blue-300);
  transition: color 0.2s ease;
}

/* Icon is collapsed (0 width) at rest so it takes no space; on hover it grows in,
   which reflows the flex row and visibly nudges the next link over. */
.link-anim svg {
  width: 0;
  height: 16px;
  margin-left: 0;
  opacity: 0;
  overflow: hidden;
  transform: translate(-4px, 0);
  transition:
    width 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    margin-left 0.3s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.22s cubic-bezier(0.22, 1, 0.36, 1),
    transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
  flex-shrink: 0;
}

.link-anim:hover,
.link-anim:focus-visible {
  color: var(--blue-200);
}

.link-anim:hover svg,
.link-anim:focus-visible svg {
  width: 16px;
  margin-left: 4px;
  opacity: 1;
  transform: translate(0, 0);
}

/* CV is not a link — it reveals a tooltip instead of navigating */
.link-anim--tooltip {
  position: relative;
  cursor: default;
}

.link-anim--tooltip:hover .tooltip,
.link-anim--tooltip:focus-visible .tooltip,
.link-anim--tooltip.is-open .tooltip {
  opacity: 1;
  visibility: visible;
  transform: translateX(calc(-50% + var(--tooltip-shift, 0px))) translateY(0);
}

/* -----------------------------------------------------------
   Responsive: desktop layout (matches Figma desktop reference)
   ----------------------------------------------------------- */

@media (min-width: 800px) {
  .header {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
  }

  .identity__text {
    grid-template-columns: auto auto;
    grid-template-areas:
      "name chip"
      "role role"
      "loc  loc";
  }

  .identity__name { white-space: nowrap; }

  .chip { margin-left: 4px; }

  .ctas {
    grid-template-columns: auto auto;
    flex-shrink: 0;
  }

  .btn { width: auto; }
}

@media (max-width: 799.98px) {
  .identity__text {
    grid-template-columns: 1fr;
    grid-template-areas:
      "name"
      "role"
      "loc"
      "chip";
    row-gap: 4px;
  }

  .chip { margin-top: 4px; }
}

@media (prefers-reduced-motion: reduce) {
  .avatar__ring { animation: none; }
  .link-anim svg, .btn, .tile, .chip__pill { transition: none; }
}
