/* ============================================================
   LAYOUT.CSS — Page Structure & Background Effects
   ============================================================
   Covers the full-page stage, the name pill, the colour blob
   system, the film grain overlay, and the SVG connector lines.
   ============================================================ */


/* ------------------------------------------------------------
   RESET — Minimal box model normalisation
   ------------------------------------------------------------ */
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
  font-family: var(--font-family);
  color: var(--color-ink);
  background: var(--color-background);
  overflow: hidden;                    /* no scrollbars — full viewport experience */
  -webkit-font-smoothing: antialiased;
}


/* ------------------------------------------------------------
   BLOOM STAGE — Container for the colour blobs
   This div lives at position 0,0 with zero size. It moves
   with the cursor via CSS custom properties --bx and --by
   set by JS every frame. All blobs are children of this,
   so they all move together as one unit.
   ------------------------------------------------------------ */
.bloom-stage {
  position: fixed;
  left: 0; top: 0;
  width: 0; height: 0;           /* zero size — blobs extend via negative margins */
  z-index: 1;
  pointer-events: none;
  transform: translate3d(var(--bx, 50vw), var(--by, 50vh), 0);
  will-change: transform;        /* GPU layer for smooth cursor tracking */
}


/* ------------------------------------------------------------
   BLOB — Individual colour cloud
   Each blob is a circle with multiple radial gradients layered
   inside it, creating an iridescent, organic colour effect.
   Blobs are hidden (opacity 0) until their word is nearby,
   then they fade in by adding the class .on
   ------------------------------------------------------------ */
.blob {
  position: absolute;
  left: -18vmin; top: -18vmin;   /* offset so blob centre aligns with stage origin */
  width: 36vmin; height: 36vmin;
  border-radius: 50%;
  filter: blur(88px);            /* heavy blur = soft, glowing colour cloud */
  opacity: 0;
  transition: opacity var(--transition-blob);
  animation: blob-drift 22s linear infinite;
  will-change: opacity, transform;
}

/* Activated state — word is near cursor */
.blob.on { opacity: 0.42; }

/* Slow organic drift animation — keeps blobs feeling alive even when still */
@keyframes blob-drift {
  0%   { transform: rotate(0deg)   scale(1.00) skewX(0deg);  }
  28%  { transform: rotate(102deg) scale(1.09) skewX(4deg);  }
  55%  { transform: rotate(195deg) scale(0.95) skewX(-3deg); }
  78%  { transform: rotate(271deg) scale(1.07) skewX(2deg);  }
  100% { transform: rotate(360deg) scale(1.00) skewX(0deg);  }
}


/* ------------------------------------------------------------
   BLOB COLOURS — One per discipline word
   Each blob uses layered radial-gradient circles at different
   positions to create a rich, non-uniform colour field.
   The colours come from tokens.css.
   ------------------------------------------------------------ */

.blob[data-key="tech"] {
  background:
    radial-gradient(circle at 18% 22%, var(--blob-tech-1) 0%, transparent 48%),
    radial-gradient(circle at 78% 18%, var(--blob-tech-2) 0%, transparent 42%),
    radial-gradient(circle at 70% 72%, var(--blob-tech-3) 0%, transparent 50%),
    radial-gradient(circle at 14% 78%, var(--blob-tech-4) 0%, transparent 38%),
    radial-gradient(circle at 88% 55%, var(--blob-tech-6) 0%, transparent 44%),
    radial-gradient(circle at 44% 10%, var(--blob-tech-5) 0%, transparent 36%);
}
.blob[data-key="ux"] {
  background:
    radial-gradient(circle at 12% 30%, var(--blob-ux-1) 0%, transparent 50%),
    radial-gradient(circle at 82% 22%, var(--blob-ux-2) 0%, transparent 44%),
    radial-gradient(circle at 66% 80%, var(--blob-ux-3) 0%, transparent 48%),
    radial-gradient(circle at 20% 82%, var(--blob-ux-4) 0%, transparent 36%),
    radial-gradient(circle at 90% 66%, var(--blob-ux-5) 0%, transparent 42%),
    radial-gradient(circle at 50%  8%, var(--blob-ux-6) 0%, transparent 38%);
}
.blob[data-key="art"] {
  background:
    radial-gradient(circle at 16% 18%, var(--blob-art-1) 0%, transparent 46%),
    radial-gradient(circle at 76% 14%, var(--blob-art-2) 0%, transparent 50%),
    radial-gradient(circle at 72% 78%, var(--blob-art-3) 0%, transparent 46%),
    radial-gradient(circle at 10% 74%, var(--blob-art-4) 0%, transparent 40%),
    radial-gradient(circle at 86% 44%, var(--blob-art-5) 0%, transparent 38%),
    radial-gradient(circle at 40% 88%, var(--blob-art-6) 0%, transparent 42%);
}
.blob[data-key="phot"] {
  background:
    radial-gradient(circle at 18% 14%, var(--blob-phot-1) 0%, transparent 44%),
    radial-gradient(circle at 78% 18%, var(--blob-phot-2) 0%, transparent 42%),
    radial-gradient(circle at 88% 52%, var(--blob-phot-3) 0%, transparent 38%),
    radial-gradient(circle at 68% 82%, var(--blob-phot-4) 0%, transparent 46%),
    radial-gradient(circle at 22% 78%, var(--blob-phot-5) 0%, transparent 44%),
    radial-gradient(circle at 10% 44%, var(--blob-phot-6) 0%, transparent 40%),
    radial-gradient(circle at 50% 10%, var(--blob-phot-7) 0%, transparent 36%),
    radial-gradient(circle at 44% 88%, var(--blob-phot-8) 0%, transparent 38%);
}
.blob[data-key="mus"] {
  background:
    radial-gradient(circle at 14% 24%, var(--blob-mus-1) 0%, transparent 48%),
    radial-gradient(circle at 84% 16%, var(--blob-mus-2) 0%, transparent 44%),
    radial-gradient(circle at 74% 80%, var(--blob-mus-3) 0%, transparent 50%),
    radial-gradient(circle at 10% 76%, var(--blob-mus-4) 0%, transparent 36%),
    radial-gradient(circle at 90% 50%, var(--blob-mus-5) 0%, transparent 42%),
    radial-gradient(circle at 48%  6%, var(--blob-mus-6) 0%, transparent 40%);
}
.blob[data-key="mass"] {
  background:
    radial-gradient(circle at 16% 20%, var(--blob-mass-1) 0%, transparent 50%),
    radial-gradient(circle at 80% 16%, var(--blob-mass-2) 0%, transparent 44%),
    radial-gradient(circle at 76% 74%, var(--blob-mass-3) 0%, transparent 48%),
    radial-gradient(circle at 12% 82%, var(--blob-mass-4) 0%, transparent 38%),
    radial-gradient(circle at 86% 58%, var(--blob-mass-5) 0%, transparent 42%),
    radial-gradient(circle at 42% 92%, var(--blob-mass-6) 0%, transparent 36%);
}


/* ------------------------------------------------------------
   GRAIN — Film noise overlay
   A subtle SVG noise texture at very low opacity, layered over
   everything. Gives the page a tactile, printed quality.
   ------------------------------------------------------------ */
.grain {
  position: fixed; inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
}


/* ------------------------------------------------------------
   STAGE — The main full-viewport canvas
   On desktop: CSS Grid, single cell, name centred.
   On mobile: overridden to flex column (see below).
   ------------------------------------------------------------ */
.stage {
  position: relative;
  z-index: 3;
  width: 100vw;
  height: 100vh;
  display: grid;
  place-items: center;   /* centres the name pill */
  overflow: hidden;
}


/* ------------------------------------------------------------
   NAME WRAP — Pill-shaped border container around the name
   The border is drawn directly on the element using the
   CSS border property (simple, clean, no pseudo-elements).
   ------------------------------------------------------------ */
.name-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--name-padding-v) var(--name-padding-h);
  border-radius: var(--name-radius);
  border: var(--border-width) solid var(--border-color-name);
  z-index: 6;
  pointer-events: none;
  user-select: none;
}


/* ------------------------------------------------------------
   NAME — The "Ralph Monteath" headline text
   Uses a subtle gradient fill (dark → mid-grey → dark) to give
   the text a slight dimensional quality, like embossed type.
   ------------------------------------------------------------ */
.name {
  font-family: var(--font-family);
  font-variation-settings: var(--font-fvar-name);
  font-size: var(--font-size-name);
  line-height: var(--line-height-name);
  letter-spacing: var(--letter-spacing-name);

  /* Gradient text fill */
  background: linear-gradient(30deg, #050507 0%, #52545e 46%, #050507 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;

  white-space: nowrap;
  user-select: none;
  pointer-events: none;
}


/* ------------------------------------------------------------
   ORBIT — Invisible container for the floating words
   Position absolute, covers the full stage. The words inside
   are placed with percentage-based --x and --y CSS variables.
   ------------------------------------------------------------ */
.orbit {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
}


/* ------------------------------------------------------------
   WORD — Individual floating discipline label
   Each word sits at a fixed percentage position, then gets
   nudged by --dx and --dy (set by JS) for the magnetic effect.
   The .drift child handles the float animation separately so
   the two motions (magnetic pull + float) stack cleanly.
   ------------------------------------------------------------ */
.word {
  position: absolute;
  left: var(--x);
  top:  var(--y);
  transform: translate3d(var(--dx, 0px), var(--dy, 0px), 0) translate(-50%, -50%);
  text-decoration: none;
  cursor: pointer;
  pointer-events: auto;
  user-select: none;
}

/* The inner animated element — handles the gentle floating motion */
.word .drift {
  display: inline-block;
  font-family: var(--font-family);
  font-variation-settings: var(--font-fvar-word);
  font-size: var(--font-size-word);
  letter-spacing: var(--letter-spacing-word);
  color: var(--color-ink);
  white-space: nowrap;
  transform-origin: center;
  transform: scale(var(--scale, 1));   /* JS sets --scale for magnetic size effect */
  will-change: transform;
  /* Shadow on the text itself composites without rerasterizing the filter,
     which eliminates the blur artifact during magnetic movement.           */
  text-shadow: 0 8px 28px rgba(0,0,0,0.22), 0 2px 6px rgba(0,0,0,0.14);
}

/* Parenthetical label e.g. "(Patented)" — lighter weight, softer colour */
.word .paren {
  color: var(--color-ink-soft);
  font-variation-settings: var(--font-fvar-paren);
  margin-left: 0.35em;
}

.word:hover .drift { color: #000; }


/* ------------------------------------------------------------
   FLOAT ANIMATIONS — Gentle organic drift per word
   Each word gets a unique animation (float-a through float-f)
   with different durations and delays so they never sync up.
   The translate values are small (px range) for subtlety.
   ------------------------------------------------------------ */
.word[data-key="tech"] .drift { animation: float-a 13.4s ease-in-out -2.1s  infinite alternate; }
.word[data-key="ux"]   .drift { animation: float-b 16.2s ease-in-out -5.7s  infinite alternate; }
.word[data-key="art"]  .drift { animation: float-c 11.8s ease-in-out -3.4s  infinite alternate; }
.word[data-key="phot"] .drift { animation: float-d 17.6s ease-in-out -0.9s  infinite alternate; }
.word[data-key="mus"]  .drift { animation: float-e 14.9s ease-in-out -7.2s  infinite alternate; }
.word[data-key="mass"] .drift { animation: float-f 12.5s ease-in-out -4.4s  infinite alternate; }

@keyframes float-a {
  0%   { transform: translate(  0px,  0px) scale(var(--scale,1)); }
  33%  { transform: translate(  6px, -4px) scale(var(--scale,1)); }
  66%  { transform: translate( -3px, -7px) scale(var(--scale,1)); }
  100% { transform: translate(  4px,  5px) scale(var(--scale,1)); }
}
@keyframes float-b {
  0%   { transform: translate(  0px,  0px) scale(var(--scale,1)); }
  40%  { transform: translate( -5px,  6px) scale(var(--scale,1)); }
  75%  { transform: translate(  7px,  3px) scale(var(--scale,1)); }
  100% { transform: translate( -2px, -4px) scale(var(--scale,1)); }
}
@keyframes float-c {
  0%   { transform: translate(  0px,  0px) scale(var(--scale,1)); }
  30%  { transform: translate( -6px, -3px) scale(var(--scale,1)); }
  70%  { transform: translate(  3px,  7px) scale(var(--scale,1)); }
  100% { transform: translate(  5px, -4px) scale(var(--scale,1)); }
}
@keyframes float-d {
  0%   { transform: translate(  0px,  0px) scale(var(--scale,1)); }
  45%  { transform: translate(  5px,  4px) scale(var(--scale,1)); }
  80%  { transform: translate( -6px, -5px) scale(var(--scale,1)); }
  100% { transform: translate(  2px,  6px) scale(var(--scale,1)); }
}
@keyframes float-e {
  0%   { transform: translate(  0px,  0px) scale(var(--scale,1)); }
  35%  { transform: translate(  7px,  2px) scale(var(--scale,1)); }
  65%  { transform: translate( -4px,  6px) scale(var(--scale,1)); }
  100% { transform: translate( -5px, -3px) scale(var(--scale,1)); }
}
@keyframes float-f {
  0%   { transform: translate(  0px,  0px) scale(var(--scale,1)); }
  25%  { transform: translate( -4px,  5px) scale(var(--scale,1)); }
  60%  { transform: translate(  6px, -4px) scale(var(--scale,1)); }
  100% { transform: translate( -3px,  7px) scale(var(--scale,1)); }
}


/* ------------------------------------------------------------
   CONNECTOR LINES — SVG lines from words to name pill
   The SVG element covers the full stage. Each line's endpoint
   nearest the name (x2/y2) is fixed and computed once by JS.
   The word end (x1/y1) updates every frame to follow the drift.
   ------------------------------------------------------------ */
.lines {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 4;
  pointer-events: none;
  overflow: visible;
  opacity: 0;
  transition: opacity 600ms ease;
}

/* Added by JS after captureAnchor() — fades lines in once positioned */
.lines.ready { opacity: 1; }
.lines line {
  stroke: var(--line-color);
  stroke-width: var(--line-weight);
  vector-effect: non-scaling-stroke;
  opacity: var(--line-opacity);
}


/* ------------------------------------------------------------
   MOBILE LAYOUT — Stack layout for narrow screens
   On mobile the absolute-positioned orbit is replaced with a
   simple vertical flex column:
     [top 3 words] → [name pill] → [bottom 3 words]
   The flex: 1 on each stack makes them share space evenly.
   ------------------------------------------------------------ */
@media (max-width: 760px) {
  :root {
    /* On mobile, font sizes use clamp() to fill the available width.
       These override the desktop values set above.
       clamp(smallest, preferred-fluid, largest)              */
    --font-size-name: clamp(28pt, 9vw, 40pt);
    --font-size-word: clamp(16pt, 5vw, 22pt);
  }

  /* Grid: top words | name pill | bottom words
     1fr auto 1fr = stacks share remaining space equally,
     name pill takes only what it needs. Perfectly symmetric.
     No padding — the 1fr rows absorb all available space equally. */
  .stage {
    display: grid;
    grid-template-rows: 1fr auto 1fr;
    grid-template-columns: 1fr;
    align-items: center;
    height: 100dvh;
  }

  /* Orbit becomes transparent to layout — its word children
     flow directly into the stack-top / stack-bot grid rows */
  .orbit { position: static; display: contents; }

  /* Hide SVG lines on mobile — too small to be meaningful */
  .lines { display: none; }

  /* Each word becomes a simple centred block */
  .word {
    position: static;
    transform: none;
    display: block;
    text-align: center;
  }

  /* Stack containers — fill their grid row, spread words evenly inside */
  .stack-top,
  .stack-bot {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-evenly;
    width: 100%;
    height: 100%;   /* fill the 1fr grid row */
  }

  /* Name pill fills width minus 40px each side */
  .name-wrap {
    width: calc(100vw - 80px);
    justify-self: center;
  }

  /* Allow name to wrap to two lines on very narrow screens */
  .name { white-space: normal; text-align: center; }

}

/* On desktop, stack-top and stack-bot vanish from layout
   so their orbit children become direct stage children     */
@media (min-width: 761px) {
  .stack-top, .stack-bot { display: contents; }
}


/* ------------------------------------------------------------
   BPM WIDGET — Heart rate display (testbpm.html)
   Heart SVG sized to match the name pill height.
   Number inside in a clean sans-serif, visually centred.
   ------------------------------------------------------------ */
.bpm-widget {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transform-origin: center;
  animation: bpm-beat var(--beat-duration, 0.857s) ease-in-out infinite;
}

.bpm-widget svg {
  display: block;
  height: var(--bpm-heart-size);
  width: auto;
}

/* Number centred inside the heart. Counter-scaled against the beat
   animation so it stays visually fixed while the heart pulses around it. */
.bpm-number {
  position: absolute;
  font-family: var(--bpm-font-family);
  font-size: var(--bpm-font-size);
  font-weight: 400;
  color: rgba(0,0,0,1);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  letter-spacing: 0.02em;
  transform: translateY(-12%);
  pointer-events: none;
  user-select: none;
  animation: bpm-counter var(--beat-duration, 0.857s) ease-in-out infinite;
}

/* Inverse of bpm-beat: divides out the parent scale so number stays still */
@keyframes bpm-counter {
  0%   { transform: translateY(-12%) scale(1.0000); }
  15%  { transform: translateY(-12%) scale(0.9524); } /* 1 / 1.05 */
  30%  { transform: translateY(-12%) scale(1.0000); }
  100% { transform: translateY(-12%) scale(1.0000); }
}

/* Quick spike at 15% of cycle, back to rest by 30%,
   hold for remainder — mimics a real heartbeat shape  */
@keyframes bpm-beat {
  0%   { transform: scale(1.00); }
  15%  { transform: scale(1.05); }
  30%  { transform: scale(1.00); }
  100% { transform: scale(1.00); }
}
