/* ============================================================
   MADAM LEGAL — "The Case File" (v2)
   Story-driven build: cover + click-unlocked threads.

   PAGE LAYOUT ONLY. Every token this file consumes now comes from
   @madamlegal/design-system:

     ../design-system/tokens.css      palette, type, space, motion
     ../design-system/components.css  glass material, .g / .solid / .btn-glass

   Both must load before this file. The token block that used to sit here
   was a second, drifting copy of the same values; the design system is now
   the single source and serves both the Noir and Ivory themes.

   This file consumes the primitives directly. The compatibility aliases it
   used to rely on (--noir, --ivory, --gold, --s-1…--s-8) are retired, and
   check-drift.mjs fails the build if any of them reappears here. They were
   misnomers: in the Ivory theme --ivory resolved to near-black, because its
   role was "primary text" rather than "the colour ivory".
   ============================================================ */

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

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

html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

body {
  background: var(--ink-950);
  color: var(--ink-050);
  font-family: var(--f-text);
  font-size: 17px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Film grain over everything — print texture, light hardware layer */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
  opacity: 0.15;
  background-image: radial-gradient(var(--grain) 1px, transparent 0);
  background-size: 20px 20px;
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }
button { font: inherit; background: none; border: none; color: inherit; cursor: pointer; }

::selection { background: var(--gold-500); color: var(--ink-950); }
:focus-visible { outline: 2px solid var(--gold-500); outline-offset: 2px; }

/* ---------- Type ---------- */

.label {
  font-family: var(--f-ui);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
}

.eyebrow {
  font-family: var(--f-ui);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: var(--tr-eyebrow);
  text-transform: uppercase;
  color: var(--gold-500);
  display: flex;
  align-items: center;
  gap: var(--rhythm-3);
  margin-bottom: var(--rhythm-4);
}
.eyebrow::after {
  content: "";
  height: 1px;
  width: 64px;
  background: var(--gold-500);
  opacity: 0.4;
}

.display-l {
  font-family: var(--f-display);
  font-weight: 350;
  font-size: clamp(44px, 6.5vw, 92px);
  line-height: 1.02;
}
.display-m {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: clamp(32px, 4.5vw, 56px);
  line-height: 1.1;
}
.quote-type {
  font-family: var(--f-display);
  font-style: italic;
  font-weight: 350;
  font-size: clamp(28px, 3.5vw, 44px);
  line-height: 1.3;
}
.body-l { font-size: clamp(19px, 1.6vw, 22px); line-height: 1.65; }
.em-italic { font-style: italic; font-weight: 380; }
.text-dim { color: var(--ink-200); }
.text-gold { color: var(--gold-500); }

.container {
  max-width: var(--measure-page);
  margin-inline: auto;
  padding-inline: var(--margin-page);
}

.hairline { height: 1px; background: var(--gold-500); opacity: 0.3; border: none; transform-origin: left center; }

/* ---------- Navigation ---------- */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  transition: background var(--micro) ease;
}
.nav.is-scrolled {
  background:
    radial-gradient(140% 110% at var(--mx, 50%) var(--my, -10%), color-mix(in srgb, var(--gold-500) 18%, transparent), transparent 58%),
    color-mix(in srgb, var(--ink-950) 78%, transparent);
  backdrop-filter: blur(24px) saturate(190%) brightness(1.06);
  -webkit-backdrop-filter: blur(24px) saturate(190%) brightness(1.06);
  border-bottom: 1px solid color-mix(in srgb, var(--gold-500) 28%, transparent);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--ink-050) 28%, transparent),
    inset 0 -1px 0 color-mix(in srgb, var(--gold-500) 15%, transparent),
    var(--shadow-floating);
}
.nav__inner {
  max-width: var(--measure-page);
  margin-inline: auto;
  padding: var(--rhythm-4) var(--margin-page);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.nav__seal { display: flex; align-items: center; gap: var(--rhythm-3); }
.nav__seal svg { width: 40px; height: 40px; }
/* The seal takes its colour from `color` and its face from the stylesheet, so
   the SVG carries no literals and follows the theme. Both pages previously
   inlined their own copy with stroke="#C6A15B" baked in. */
.seal { color: var(--gold-500); }
.seal__mark { font-family: var(--f-display); }
.nav__wordmark {
  font-family: var(--f-display);
  font-size: 15px;
  letter-spacing: var(--tr-wordmark);
  text-transform: uppercase;
}
.nav__cta {
  position: relative;
  font-family: var(--f-display);
  font-size: 15px;
  font-weight: 350;
  letter-spacing: var(--tr-wordmark);
  text-transform: uppercase;
  color: var(--ink-200);
  background: none;
  border: none;
  cursor: pointer;
  transition: color var(--micro) ease;
  padding-block: var(--rhythm-1);
}
.nav__cta::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--gold-500);
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform var(--micro) var(--ease-reveal);
}
.nav__cta:hover { color: var(--ink-050); }
.nav__cta:hover::after { transform: scaleX(1); transform-origin: left center; }

/* ============================================================
   THE COVER — centered, portrait assembled from pieces
   ============================================================ */

.cover {
  min-height: 100svh;
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
  padding-block: 76px 12px;
}

/* The vortex: global fixed galaxy background canvas and orbiting rings */
.vortex {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: max(140vw, 140vh);
  aspect-ratio: 1;
  pointer-events: none;
  z-index: 0;
  opacity: 0.75;
}
.vortex__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.vortex__rings {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0.055;
  color: var(--gold-500);
}

.cover__stage {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  width: 100%;
  /* .cover__stage is a grid item of .cover, and grid items default to
     min-width: auto: they refuse to shrink below their widest descendant's
     intrinsic width, width: 100% notwithstanding. .cover__name is nowrap
     (see its own rule) and its fade-out tween stretches its letter-spacing
     to 0.22em; nothing stops it wrapping, but at 375px that stretch alone is
     wide enough to become the widest thing on the page, and every flex/grid
     ancestor between it and .cover__stage inherits the same default, so the
     one overflow dragged the whole centered stage, portrait included,
     right along with it, mid-fade. min-width: 0 opts out: the stage stays
     fixed at 100%, and the overflow it was always meant to have during that
     fade clips quietly inside .cover's own overflow: hidden instead. */
  min-width: 0;
  padding-inline: var(--margin-page);
}

.eyebrow--center { justify-content: center; }
.eyebrow--center::after { display: none; }

/* --- The portrait: the transparent cut-out, its lower edge faded so
       she melts into the page and the wordmark sits in that fade --- */

.lego-wrap {
  position: relative;
  display: grid;
  place-items: center;
  height: min(42vh, 420px);
  aspect-ratio: 4 / 5;
  max-width: 82vw;
}
.portrait-fallback {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter: saturate(0.85) contrast(1.05);
  /* fade the bottom of the photo into the background */
  -webkit-mask-image: linear-gradient(to bottom, #000 52%, transparent 94%);
  mask-image: linear-gradient(to bottom, #000 52%, transparent 94%);
}

.cover__ripple { display: none; }

.cover__lockup {
  position: relative;
  /* sit at the faded bottom edge of the portrait, not up over her face */
  margin-top: max(-7vh, -64px);
  z-index: 3;
}
/* Legibility over the star-portrait comes from a soft text shadow only,
   no rectangular scrim behind the type */
.cover__name, .cover__brand, .cover__brand-group {
  text-shadow: 0 2px 30px var(--halo), 0 0 60px var(--halo);
  will-change: transform, opacity;
  backface-visibility: hidden;
  transform: translateZ(0);
}
.cover__eyebrow { margin-bottom: var(--rhythm-4); position: relative; z-index: 3; }

/* Name and brand share the same spot — one dissolves, the other arrives */
.cover__swap {
  display: grid;
  justify-items: center;
  align-items: center;
  will-change: transform, opacity;
}
.cover__swap > * { grid-area: 1 / 1; }
html.no-js .cover__swap { display: flex; flex-direction: column; gap: var(--rhythm-2); align-items: center; }
html.no-js .cover__name { display: none; }

/* The cover has three states, not two, and only two of them used to be
   styled.

   `no-js` means "JavaScript is off" and is styled above. cover.js's opening
   gsap.set(..., autoAlpha: 0) is the state the animation starts from. But
   site.js — which is tiny, has no dependencies, and runs the moment the
   parser reaches it — removes `no-js` long before cover.js exists, because
   cover.js waits on gsap, ScrollTrigger and lenis to download first.

   In that gap the page was styled as if the animation had already taken
   over while nothing had actually hidden anything: .cover__swap became a
   grid again with the name and the brand group both in cell 1/1, so
   "Smriti Ranjan Mishra" rendered on top of "MADAM LEGAL" with 57px of
   overlap. On localhost that lasts no time at all. On a hard refresh over
   a real connection it is however long ~110KB of libraries takes — two to
   three seconds of what reads as a broken page.

   So: hold the animation's own starting state from the first paint. Scoped
   to exactly the elements cover.js reveals, and no others — [data-cover]
   also matches the threads container, the magnifier and the two children of
   the brand group, none of which anything ever animates back to visible.
   visibility, not opacity, so it matches the autoAlpha these are tweened
   with; gsap writes visibility inline on reveal and inline wins over this. */
html:not(.no-js) [data-cover="eyebrow"],
html:not(.no-js) [data-cover="name"],
html:not(.no-js) [data-cover="brand-group"],
html:not(.no-js) [data-cover="statement"],
html:not(.no-js) [data-cover="hint"],
html:not(.no-js) .thread-tags .thread-tag { visibility: hidden; }

.cover__name {
  font-family: var(--f-text);
  font-style: italic;
  font-size: clamp(26px, 3.4vw, 44px);
  color: var(--ink-050);
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.cover__brand-group {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.cover__prefix {
  font-family: var(--f-text);
  font-style: italic;
  font-size: clamp(14px, 1.6vw, 22px);
  letter-spacing: 0.12em;
  line-height: 1.0;
  margin-bottom: -1px;
  white-space: nowrap;
  background: linear-gradient(110deg, var(--gold-500) 0%, var(--gold-200) 25%, var(--gold-300) 50%, var(--gold-200) 75%, var(--gold-500) 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: goldPrefixSweep 4.8s linear infinite;
  filter: drop-shadow(0 2px 12px color-mix(in srgb, var(--gold-500) 45%, transparent));
}

.cover__brand {
  font-family: var(--f-display);
  font-weight: 330;
  font-size: clamp(38px, 6.4vw, 104px);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-050);
  white-space: nowrap;
}

.cover__statement {
  font-family: var(--f-display);
  font-weight: 350;
  font-size: clamp(15px, 1.7vw, 22px);
  line-height: 1.45;
  margin-top: var(--rhythm-3);
  margin-inline: auto;
}
.cover__statement-line { display: block; white-space: nowrap; }
.magnifier {
  width: 1em;
  height: 1em;
  color: var(--gold-500);
  vertical-align: -0.12em;
  margin-left: 0.35em;
  display: inline-block;
}

/* --- The threads out of Madam Legal --- */

.cover__threads {
  position: relative;
  /* 100%, not 92vw. vw is the viewport, which does not know that .cover__stage
     has already spent --margin-page on each side — so the two stacked: at 375px
     the fan asked for 345px and the padding added 48 more, forcing the stage to
     393px inside a 375px screen. Centring clamps to the start edge rather than
     spill off it, so the whole 18px overhang landed on the right and the fan
     sat visibly off-centre. Above ~956px the 880px cap bound first, which is
     why this only ever showed on phones and small tablets. */
  width: min(880px, 100%);
  margin-top: var(--rhythm-3);
}
.threads-svg {
  /* Height is the gap between the statement and the tags — the fan's own
     spacing, which is why .thread-tags needs no margin of its own.

     No viewBox, on purpose. Without one, a user unit is a CSS pixel and there
     is no scale between what cover.js measures and what the browser draws;
     the two previous versions of this fan both failed inside exactly that
     gap. overflow: visible because the dots sit below this box, inside the
     flex row underneath — the curves have to leave the element to reach them,
     and the UA default of overflow:hidden on an <svg> root would clip them at
     the floor. */
  display: block;
  width: 100%;
  height: clamp(42px, 6vh, 76px);
  overflow: visible;
}
.threads-svg__path {
  fill: none;
  stroke: var(--gold-500);
  stroke-width: 1;
  opacity: 0.55;
}
.thread-tags {
  display: flex;
}
.thread-tag {
  flex: 1 1 20%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: var(--rhythm-2) var(--rhythm-1) var(--rhythm-3);
  color: var(--ink-200);
  transition: color var(--micro) ease, transform var(--micro) var(--ease-reveal);
}
.thread-tag::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  border: 1px solid var(--gold-500);
  background: transparent;
  transition: background var(--micro) ease, box-shadow var(--micro) ease;
}
.thread-tag:hover { color: var(--gold-300); transform: translateY(4px); }
.thread-tag:hover::before { background: var(--gold-500); box-shadow: 0 0 12px color-mix(in srgb, var(--gold-500) 70%, transparent); }
.thread-tag.is-pulled { color: var(--gold-500); }
.thread-tag.is-pulled::before { background: var(--gold-500); }
.thread-tag__num {
  font-family: var(--f-ui);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.thread-tag__title {
  font-family: var(--f-display);
  font-size: clamp(14px, 1.5vw, 19px);
  font-weight: 400;
}
.cover__hint {
  margin-top: var(--rhythm-3);
  font-family: var(--f-ui);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-200);
}

/* The "Loose threads" rail that used to sit above the contact block, and the
   1px gold `.thread-line` tick that used to sit at the top of every thread
   section, both lived here. Removed by request: the rail's `.loose__items`
   was never populated by anything, and the tick read as a stray artefact
   rather than as the section-to-section connector it was meant to be. The
   five curves fanning out of the cover were the last of the thread motif;
   they have since been removed too — see the note in index.html. */

/* ---------- Threads docked in the header ---------- */

.nav__threads {
  display: flex;
  align-items: center;
  gap: clamp(8px, 1.6vw, 24px);
  min-height: 40px;
  opacity: 0;
  transform: translateY(-10px);
  pointer-events: none;
  transition: opacity 0.45s ease, transform 0.45s var(--ease-reveal);
}
.nav__threads.is-visible {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
.nav__threads .thread-tag { opacity: 0; transform: translateY(-8px); transition: opacity 0.4s ease, transform 0.4s var(--ease-reveal); }
.nav__threads.is-visible .thread-tag { opacity: 1; transform: none; }
.nav__threads.is-visible .thread-tag:nth-child(1) { transition-delay: 0.02s; }
.nav__threads.is-visible .thread-tag:nth-child(2) { transition-delay: 0.06s; }
.nav__threads.is-visible .thread-tag:nth-child(3) { transition-delay: 0.10s; }
.nav__threads.is-visible .thread-tag:nth-child(4) { transition-delay: 0.14s; }
.nav__threads.is-visible .thread-tag:nth-child(5) { transition-delay: 0.18s; }
.nav__threads .thread-tag {
  flex: 0 0 auto;
  flex-direction: row;
  gap: 8px;
  padding: 6px 4px;
}
.nav__threads .thread-tag::before { width: 5px; height: 5px; }
.nav__threads .thread-tag__num { display: none; }
.nav__threads .thread-tag__title { font-size: 14px; }
.nav__threads .thread-tag:hover { transform: none; }

/* ---------- Threads docked at the foot of the screen ---------- */

/* Phone only. The max-width:768px block turns this on and turns .nav__threads
   off; above that width the header row is the one that shows. Both are in the
   markup on both pages, and exactly one is ever displayed — display:none also
   takes the hidden set out of the accessibility tree, so a visitor reaches
   five tags, not ten. */
.bottom-nav { display: none; }

/* ============================================================
   THREADS
   ============================================================ */

.thread { position: relative; }

/* The header is fixed, so a browser-driven jump to #thread-3 parks the section
   at y=0 and the heading lands *underneath* it. In-page navigation never showed
   this because the thread tags scroll through Lenis with an explicit -80 offset;
   arriving from about.html, whose header links out to index.html#thread-N, gets
   the native jump with no offset at all. Measured: "SERVICES" sat 31px behind
   the header and "THE SCHOLAR" 36px.

   scroll-margin-top fixes it for every route into an anchor — the native jump,
   the Lenis scroll, and find-in-page — without JavaScript. The values clear the
   header (88px desktop, ~60px at the mobile breakpoint) with room to breathe. */
.thread,
#contact {
  scroll-margin-top: 104px;
}

.thread__inner {
  padding-top: clamp(20px, 2.5vw, 36px);
  padding-bottom: clamp(36px, 4vw, 60px);
}

#thread-1 .thread__inner,
#thread-2 .thread__inner,
#thread-3 .thread__inner {
  min-height: calc(100vh - 80px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: clamp(16px, 2vw, 32px);
  padding-bottom: clamp(24px, 3vw, 48px);
}


.thread__kicker {
  display: flex;
  align-items: center;
  gap: var(--rhythm-4);
  margin-bottom: var(--rhythm-4);
}
.thread__num {
  font-family: var(--f-display);
  font-weight: 320;
  font-size: clamp(56px, 7vw, 104px);
  line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1px color-mix(in srgb, var(--gold-500) 55%, transparent);
}
.thread__kicker .eyebrow { margin-bottom: 0; }

.thread-cta {
  text-align: center;
  padding-block: var(--rhythm-6) clamp(64px, 8vw, 104px);
}
.thread-cta.is-done { display: none; }

/* --- Thread 01 — The Name --- */

.name-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(40px, 6vw, 104px);
  align-items: flex-start;
}
.name-copy p + p { margin-top: var(--rhythm-4); }
.name-copy .lede::first-letter {
  font-family: var(--f-display);
  font-weight: 400;
  font-size: 3.3em;
  line-height: 0.85;
  float: left;
  color: var(--gold-500);
  padding-right: 0.12em;
  padding-top: 0.05em;
}
/* The design system caps .lede at --measure-lede (54ch) — right for a lede
   standing on its own, wrong here. In this two-column grid the copy column is
   597px at 1440, so the cap left 111px of the column empty and the paragraph
   visibly stopped short of the stat tiles directly beneath it, which do fill
   the column. --measure-prose (68ch, ~612px) is just wider than the column, so
   the column itself becomes the constraint and the text runs its full width —
   while still capping at a sane measure if the column is ever widened. */
.name-copy .lede { max-width: var(--measure-prose); }

.name-copy strong { color: var(--ink-050); font-weight: 400; }
.name-copy { color: color-mix(in srgb, var(--ink-050) 82%, transparent); }
.name-copy .lede {
  color: color-mix(in srgb, var(--ink-050) 88%, transparent);
  font-weight: 300;
  font-size: clamp(14px, 1.1vw, 16px);
  line-height: 1.65;
  letter-spacing: 0.015em;
  margin-bottom: var(--rhythm-2);
}

/* Gold Shimmer & Ambient Glow for Brand & Name in Thread 1 */
.brand-shimmer, .name-shimmer {
  display: inline-block;
  font-style: normal;
  background: linear-gradient(110deg, var(--ink-050) 0%, var(--gold-500) 25%, var(--gold-200) 50%, var(--gold-500) 75%, var(--ink-050) 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: goldShimmerSweep 4.5s linear infinite;
  filter: drop-shadow(0 2px 10px color-mix(in srgb, var(--gold-500) 45%, transparent));
}
.brand-shimmer {
  font-style: italic;
  font-family: var(--f-text);
}
.name-shimmer {
  font-weight: 500;
}

@keyframes goldShimmerSweep {
  0% { background-position: 0% center; }
  100% { background-position: 200% center; }
}

/* Compact 4-stat grid inside Thread 1 with Liquid Glass material */
.name-stats-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px 20px;
  margin-block: var(--rhythm-4);
  padding: 0;
  border-block: none;
}
.name-stat {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 20px 24px;
  border-radius: var(--r-md);
  background:
    radial-gradient(140% 110% at var(--mx, 50%) var(--my, -10%), color-mix(in srgb, var(--gold-500) 16%, transparent), transparent 58%),
    color-mix(in srgb, var(--ink-850) 65%, transparent);
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  border: 1px solid color-mix(in srgb, var(--gold-500) 22%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink-050) 20%, transparent), var(--shadow-raised);
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.name-stat:hover {
  transform: translateY(-2px);
  border-color: var(--gold-300);
  box-shadow: inset 0 1px 0 var(--lens-top), var(--glow-gold);
}
.name-stat__value {
  font-family: var(--f-display);
  font-size: clamp(32px, 3.5vw, 48px);
  line-height: 1;
  color: var(--gold-300);
  font-weight: 330;
  margin: 0 0 6px 0;
}
.name-stat__label {
  font-size: clamp(10px, 0.72vw, 11px);
  color: var(--ink-200);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-top: 0;
  line-height: 1.35;
}

/* Transparent portrait asset styling (matching homepage hero) */
.transparent-portrait-wrap {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  margin-top: -36px;
}
.transparent-portrait-img {
  width: 100%;
  max-width: 380px;
  height: auto;
  object-fit: contain;
  filter: saturate(0.85) contrast(1.05);
  -webkit-mask-image: linear-gradient(to bottom, #000 52%, transparent 94%);
  mask-image: linear-gradient(to bottom, #000 52%, transparent 94%);
}

/* Typewriter animation styling for punchline */
.typewriter-line {
  font-family: var(--f-text);
  font-style: italic;
  color: var(--gold-300);
  font-size: clamp(15px, 1.15vw, 18px);
  margin-top: var(--rhythm-3);
  min-height: 1.6em;
  display: inline-flex;
  align-items: center;
}
.typewriter-cursor {
  display: inline-block;
  color: var(--gold-500);
  font-weight: 300;
  margin-left: 3px;
  opacity: 1;
  transition: opacity 0.4s ease;
  animation: blinkCursor 0.75s infinite;
}
@keyframes blinkCursor {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

/* --- Thread 02 — The Decade --- */

.stats__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--rhythm-4); }
.stat { text-align: center; padding-block: var(--rhythm-5); position: relative; }
.stat + .stat::before {
  content: "";
  position: absolute;
  left: calc(var(--rhythm-2) * -1); top: 25%; bottom: 25%;
  width: 1px;
  background: color-mix(in srgb, var(--gold-500) 18%, transparent);
}
.stat__value {
  font-family: var(--f-display);
  font-weight: 320;
  font-size: clamp(48px, 6vw, 88px);
  line-height: 1;
  color: var(--gold-500);
  display: inline-flex;
  align-items: baseline;
  /* The design system's own .stat (components.css) is display: flex, which
     makes this a flex item — and a flex item's `inline-flex` is blockified to
     `flex`. So this box stretched the full column width and `text-align:
     center` on .stat stopped reaching it: every figure sat hard against the
     left edge while its label below stayed centred. Centre the figure and its
     suffix along this box's own main axis instead, which holds whether the
     box ends up inline-flex or flex. */
  justify-content: center;
}
.stat__suffix { font-size: 0.55em; margin-left: 2px; }
.stat__label { margin-top: var(--rhythm-3); color: var(--ink-200); }

.workedwith { text-align: center; margin-top: clamp(48px, 7vw, 88px); }

/* No box around each name. These were glass tiles, and six bordered panels in
   a grid read as a UI control set — buttons — rather than as a roll of
   institutions. What carries the weight instead is the display face and a
   short gold hairline under each name, the same accent .eyebrow uses.

   Three explicit columns, not auto-fit: there are exactly six names, and 3x2
   is the only arrangement that fills both rows. auto-fit gave four across at
   common desktop widths and left the last two stranded in a half-empty row.

   No text-transform. Uppercasing flattened "sKarn Analytics" into "SKARN
   ANALYTICS" and destroyed a deliberate piece of that firm's own wordmark;
   these names are set as their owners set them. */
.workedwith__names {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(28px, 4vw, 52px) clamp(24px, 4vw, 72px);
  margin-top: var(--rhythm-5);
  list-style: none;
}
@media (max-width: 860px) {
  .workedwith__names { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 430px) {
  .workedwith__names { grid-template-columns: 1fr; }
}
.workedwith__name {
  position: relative;
  padding-bottom: var(--rhythm-3);
  font-family: var(--f-display);
  font-size: clamp(15px, 1.45vw, 20px);
  font-weight: 350;
  line-height: 1.3;
  letter-spacing: 0.005em;
  color: var(--ink-100);
  transition: color var(--quick) ease;
}
.workedwith__name::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 28px;
  height: 1px;
  transform: translateX(-50%);
  background: var(--gold-500);
  opacity: 0.4;
  transition: width var(--quick) ease, opacity var(--quick) ease;
}
.workedwith__name:hover { color: var(--gold-300); }
.workedwith__name:hover::after { width: 52px; opacity: 0.85; }

#thread-3 .thread__inner {
  padding-top: clamp(10px, 1.2vw, 20px);
  padding-bottom: clamp(10px, 1.2vw, 20px);
}

#thread-3 .thread__kicker {
  margin-bottom: 8px;
}

.services-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 8px;
}
.service-row {
  display: grid;
  grid-template-columns: 1fr 1.6fr 1fr;
  gap: clamp(16px, 2vw, 32px);
  padding: 18px 28px;
  background:
    radial-gradient(140% 110% at var(--mx, 50%) var(--my, -10%), color-mix(in srgb, var(--gold-500) 16%, transparent), transparent 58%),
    color-mix(in srgb, var(--ink-850) 65%, transparent);
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  border: 1px solid color-mix(in srgb, var(--gold-500) 22%, transparent);
  border-radius: var(--r-lg);
  align-items: center;
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink-050) 20%, transparent), var(--shadow-raised);
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.service-row:hover {
  background:
    radial-gradient(140% 110% at var(--mx, 50%) var(--my, -10%), color-mix(in srgb, var(--gold-300) 25%, transparent), transparent 58%),
    color-mix(in srgb, var(--ink-800) 85%, transparent);
  border-color: var(--gold-300);
  transform: translateY(-2px);
  box-shadow: inset 0 1px 0 var(--lens-top), var(--glow-gold);
}
.service-row__col {
  display: flex;
  flex-direction: column;
}
.service-row__col--title {
  gap: 4px;
}
.service-row__num {
  font-family: var(--f-ui);
  font-size: 11px;
  letter-spacing: 0.15em;
  color: var(--gold-300);
}
.service-row__col--title h3 {
  font-family: var(--f-text);
  font-size: clamp(18px, 1.6vw, 22px);
  font-weight: 400;
  color: var(--ink-050);
  line-height: 1.15;
}
.service-row__col--desc p {
  color: var(--ink-200);
  line-height: 1.45;
  font-size: 13.5px;
  margin: 0;
}
.stat-block {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
}
.stat-block__value {
  font-family: var(--f-display);
  font-size: clamp(26px, 2.5vw, 36px);
  line-height: 1;
  color: var(--gold-500);
  font-weight: 320;
}
.stat-block__plus {
  font-size: 0.7em;
  vertical-align: super;
}
.stat-block__label {
  font-family: var(--f-ui);
  font-size: 10px;
  color: var(--ink-200);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-top: 4px;
}

@media (max-width: 900px) {
  .service-card__inner {
    grid-template-columns: 1fr;
    gap: var(--rhythm-3);
    padding: 24px;
  }
  .service-card__footer {
    align-items: flex-start;
    text-align: left;
    padding-top: var(--rhythm-2);
    border-top: 1px dashed color-mix(in srgb, var(--gold-500) 15%, transparent);
  }
}

/* --- Thread 04 — The Scholar Timeline (100% matched to Who Am I typography) --- */
#thread-4 .thread__inner {
  min-height: calc(100vh - 80px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: clamp(16px, 2vw, 28px);
  padding-bottom: clamp(20px, 2.5vw, 36px);
}

.scholar-wrapper {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(20px, 3vw, 40px);
  align-items: center;
}

.scholar-timeline {
  position: relative;
  max-width: 1000px;
  margin-block: clamp(12px, 1.4vw, 20px);
  padding-left: 30px;
  border-left: 1px solid color-mix(in srgb, var(--gold-500) 22%, transparent);
  display: flex;
  flex-direction: column;
  gap: clamp(14px, 1.5vw, 20px);
}

.scholar-item {
  position: relative;
  display: grid;
  grid-template-columns: 145px 1fr;
  gap: 20px;
  align-items: center;
  padding-block: 4px;
}

.scholar-item__year-wrap {
  position: relative;
  display: flex;
  align-items: center;
  padding-top: 0;
}

.scholar-item__dot {
  position: absolute;
  left: -36.5px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--ink-950);
  border: 2px solid var(--gold-300);
  box-shadow: 0 0 10px color-mix(in srgb, var(--gold-500) 70%, transparent);
}

.scholar-item__year {
  font-family: var(--f-ui);
  font-size: clamp(12px, 0.85vw, 13.5px);
  letter-spacing: 0.12em;
  color: var(--gold-300);
  font-weight: 600;
  text-transform: uppercase;
  line-height: 1.4;
}

.scholar-item__content {
  position: relative;
  padding: 20px 24px;
  border-radius: var(--r-md);
  background:
    radial-gradient(140% 110% at var(--mx, 50%) var(--my, -10%), color-mix(in srgb, var(--gold-500) 16%, transparent), transparent 58%),
    color-mix(in srgb, var(--ink-850) 65%, transparent);
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  border: 1px solid color-mix(in srgb, var(--gold-500) 22%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink-050) 20%, transparent), var(--shadow-raised);
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.scholar-item__content:hover {
  transform: translateY(-2px);
  border-color: var(--gold-300);
  box-shadow: inset 0 1px 0 var(--lens-top), var(--glow-gold);
}

.scholar-item__degree {
  font-family: var(--f-display);
  font-size: clamp(19px, 1.65vw, 24px);
  line-height: 1.15;
  margin-bottom: 4px;
}

.scholar-item__institution {
  font-family: var(--f-ui);
  font-size: clamp(10px, 0.72vw, 11px);
  color: var(--ink-200);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 4px;
}

.scholar-item__detail {
  font-family: var(--f-text);
  color: color-mix(in srgb, var(--ink-050) 88%, transparent);
  font-weight: 300;
  font-size: clamp(13.5px, 1vw, 15.5px);
  line-height: 1.6;
  letter-spacing: 0.012em;
  max-width: 68ch;
}

.scholar-portrait-wrap {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
}

.scholar-portrait-img {
  width: 100%;
  max-width: 580px;
  height: auto;
  object-fit: contain;
  filter: saturate(0.95) contrast(1.04);
  -webkit-mask-image: linear-gradient(to bottom, #000 68%, transparent 98%);
  mask-image: linear-gradient(to bottom, #000 68%, transparent 98%);
}

@media (max-width: 900px) {
  .scholar-wrapper {
    grid-template-columns: 1fr;
  }
  .scholar-portrait-wrap {
    margin-top: 16px;
  }
}



/* --- Thread 05 — The Verdict --- */

/* No background of its own. This was a solid --oxblood panel, which made The
   Verdict the one screen that broke out of the noir-and-starfield the rest of
   the page shares. Left transparent, the global .vortex canvas shows through
   here exactly as it does behind every other thread. */
.verdict {
  min-height: calc(100vh - 80px);
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.verdict__inner {
  padding-block: clamp(36px, 5vw, 72px);
  max-width: 900px;
  margin-inline: auto;
  text-align: center;
}
.verdict__mark {
  font-family: var(--f-display);
  font-size: clamp(120px, 16vw, 220px);
  line-height: 0;
  color: var(--gold-500);
  opacity: 0.5;
  display: block;
  height: 0.35em;
}
.verdict__attr { margin-top: var(--rhythm-5); color: var(--gold-300); }

.contact {
  min-height: calc(100vh - 80px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(36px, 5vw, 72px);
}
.contact__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(40px, 6vw, 104px);
  align-items: center;
}
.contact .display-l { max-width: 12ch; }
.contact-list { display: flex; flex-direction: column; gap: var(--rhythm-4); }
.contact-list__item {
  display: flex;
  flex-direction: column;
  gap: var(--rhythm-1);
  padding-bottom: var(--rhythm-4);
  border-bottom: 1px solid color-mix(in srgb, var(--gold-500) 20%, transparent);
}
.contact-list__item .label { color: var(--ink-200); }
.contact-list__item a, .contact-list__item .contact-static {
  font-family: var(--f-display);
  font-weight: 380;
  font-size: clamp(18px, 2vw, 26px);
  transition: color var(--micro) ease;
}
.contact-list__item a:hover { color: var(--gold-500); }
.contact-static__sep { color: color-mix(in srgb, var(--gold-500) 55%, transparent); padding-inline: 4px; }

/* ---------- Footer ---------- */

.footer {
  position: relative;
  background:
    radial-gradient(140% 110% at var(--mx, 50%) var(--my, -10%), color-mix(in srgb, var(--gold-500) 14%, transparent), transparent 58%),
    color-mix(in srgb, var(--ink-950) 75%, transparent);
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  border-top: 1px solid color-mix(in srgb, var(--gold-500) 30%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ink-050) 20%, transparent), var(--shadow-inverted);
  padding-block: var(--rhythm-6) var(--rhythm-5);
}
.footer__fineprint {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--rhythm-3);
  color: var(--ink-200);
  font-family: var(--f-ui);
  font-size: 11px;
  letter-spacing: 0.08em;
}
.footer__tagline { font-family: var(--f-display); font-style: italic; color: var(--ink-050); }

/* Above the fineprint row, separated by the same gold hairline the footer
   already uses at its top edge. Deliberately quiet — dimmer and smaller than
   the fineprint — because it has to be findable without competing with the
   name it sits under.

   One paragraph, running the full width of the container. No max-width and no
   column split: at 11px this is two or three lines across a desktop container,
   so the long measure never becomes a wall of text, and the footer stays
   shallow. */
/* Two paragraphs now — the madamlegal.com non-affiliation notice and the
   declaration Rule 36's Schedule requires.

   Two columns for line length, not for height: measured both ways at 1280px
   and the block is 175px as a grid against 170px stacked, so this buys nothing
   vertically and it would be wrong to claim otherwise. What it buys is the
   measure. The container runs 876px wide, and 11px text across all of it is
   about 140 characters a line — roughly double the point where a line stops
   being comfortably readable. Halved, each column lands near 68.

   Falls back to one column below 900px, where two columns would be too narrow
   to help. Note the rule assumes two children: a single paragraph in a
   two-column grid would sit in the left half as a half-width orphan, which is
   exactly the layout the practitioner rejected earlier. If the declaration is
   ever removed, drop back to a plain block here. */
.footer__disclaimer {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--rhythm-3) var(--rhythm-5);
  margin-bottom: var(--rhythm-5);
  padding-bottom: var(--rhythm-4);
  border-bottom: 1px solid color-mix(in srgb, var(--gold-500) 16%, transparent);
  font-family: var(--f-ui);
  font-size: 11px;
  line-height: 1.7;
  letter-spacing: 0.03em;
  color: color-mix(in srgb, var(--ink-200) 78%, transparent);
}
@media (max-width: 900px) {
  .footer__disclaimer { grid-template-columns: 1fr; }
}
.footer__disclaimer strong {
  color: var(--ink-100);
  font-weight: 600;
}

/* Quieter again than .footer__disclaimer above it — this is a build note, not
   a legal one, and shouldn't compete with either the disclaimer or the
   fineprint for attention. No border, no top margin of its own; it just
   trails after the fineprint as the last, smallest thing in the footer. */
.footer__notice {
  margin-top: var(--rhythm-3);
  font-family: var(--f-ui);
  font-size: 10px;
  line-height: 1.6;
  letter-spacing: 0.04em;
  color: color-mix(in srgb, var(--ink-200) 55%, transparent);
}

/* ---------- Reveal helpers ---------- */

[data-reveal] { opacity: 0; }
html.no-js [data-reveal], html.reduced-motion [data-reveal] { opacity: 1; }

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

@media (max-width: 1024px) {
  .nav__threads .thread-tag__title { font-size: 12px; }
  .name-grid, .contact__grid { grid-template-columns: 1fr; }
  .blend-portrait { max-width: 480px; }
  .stats__grid { grid-template-columns: repeat(2, 1fr); }
  .stat:nth-child(odd)::before { display: none; }
  .philosophy-grid { grid-template-columns: 1fr; }
}

@media (max-width: 768px) {
  /* .cover__name is not exempted here — it needs to stay nowrap.
     Its fade-out tween in cover.js stretches letter-spacing to 0.22em and
     scales it to 1.04; on a 375px screen that's enough extra width to wrap
     "Smriti Ranjan Mishra" onto a second line. .cover__swap is a CSS-grid
     stack sized to whichever of {name, brandGroup} is tallest, so that
     transient two-line wrap — of text that is already most of the way to
     opacity 0 — became the tallest item and pushed the whole reserved height
     up by ~37px, shifting the portrait above it up with it. The name is
     never longer than the viewport at its resting letter-spacing, so nowrap
     costs nothing there; it only ever overflows during the fade, by which
     point nothing is visible to overflow. */
  .cover__brand { white-space: normal; }
  .cover__statement-line { white-space: normal; }
  .nav__wordmark { display: none; }

  /* The bar drops to ~60px here, so the desktop anchor offset would overshoot
     and leave a band of empty page above every heading. */
  .thread,
  #contact { scroll-margin-top: 76px; }

  /* The five threads are the primary navigation of the site. These rules used
     to hide .thread-tag__title in favour of .thread-tag__num — an element that
     exists in no page — so below 768px every thread rendered as a blank 69x31
     pill and the only way through the page disappeared. The labels stay. */
  .thread-tag { min-height: 44px; } /* touchTarget.default */
  .thread-tag:hover { transform: none; } /* nothing to lift on a touch screen */
  .thread-tag__title { font-size: 15px; }

  /* Two editorial grids kept their desktop column counts all the way down and
     were never given a breakpoint, so on a 375px phone they divided the screen
     instead of stacking on it.

     .service-row held three columns at 81px / 115px / 72px. "Intellectual
     Property (IPR)" wrapped over three lines in the first, the description ran
     at two to four words a line in the second, and "TRADEMARKS PROSECUTED &
     CLEARED" ran to the screen edge in the third. Services is one of the five
     threads — this is a primary surface, not a corner.

     .scholar-item held 145px of date beside a 170px card, which left the
     research summary inside it 120px wide. */
  .service-row {
    grid-template-columns: 1fr;
    gap: 12px;
    padding: 20px 22px;
  }
  /* The metric is right-aligned on desktop because it sits in the last of three
     columns, against the card's right edge. Stacked, that left the number
     floating at x=207 while the title, description and its own label all
     started at 47.

     .stat-block is a *column* flex, so what pushes the number right is
     align-items on the cross axis — text-align does nothing to it, and setting
     only that moved the full-width label while leaving the number behind. */
  .service-row__col--stats { justify-self: start; }
  .service-row__col--stats .stat-block {
    align-items: flex-start;
    text-align: left;
  }

  .scholar-item {
    grid-template-columns: 1fr;
    gap: 10px;
    align-items: start;
  }

  /* Justified text needs width to distribute. At these measures it opens
     rivers between words instead — worst in the scholar detail, which was
     justifying inside 120px. */
  .scholar-item__detail,
  .name-copy .lede {
    text-align: left;
  }

  /* The five threads leave the header and dock at the foot of the screen.

     Sharing one bar with the seal and the CTA left them about 180px, so the
     row had to scroll horizontally and two of the five were always off the
     end — on the primary navigation of the site. A full-width bottom bar
     gives all five equal columns, all visible at once, and puts them under
     the thumb instead of at the far top of the reach. */
  .nav__threads { display: none; }

  .bottom-nav {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 100;
    /* The scrolled header's material, mirrored: the sheen reads from the edge
       the bar is attached to, so the gradient origin and the inset move to the
       bottom and the border to the top. */
    background:
      radial-gradient(140% 110% at var(--mx, 50%) 110%, color-mix(in srgb, var(--gold-500) 16%, transparent), transparent 58%),
      color-mix(in srgb, var(--ink-950) 82%, transparent);
    backdrop-filter: blur(24px) saturate(190%) brightness(1.06);
    -webkit-backdrop-filter: blur(24px) saturate(190%) brightness(1.06);
    border-top: 1px solid color-mix(in srgb, var(--gold-500) 28%, transparent);
    box-shadow:
      inset 0 1px 0 color-mix(in srgb, var(--ink-050) 22%, transparent),
      var(--shadow-inverted);
    /* Keeps the labels clear of the iOS home indicator and the Android
       gesture bar. The floor is for the browsers that report no inset at all,
       where the labels would otherwise sit 9px off the screen edge. */
    padding-bottom: max(5px, env(safe-area-inset-bottom));
    /* Same reveal contract as .nav__threads, in the other direction: hidden
       until the visitor is past the cover, where the fan is still on screen
       and a second copy of the same five would be noise. */
    opacity: 0;
    transform: translateY(100%);
    pointer-events: none;
    transition: opacity 0.35s ease, transform 0.35s var(--ease-reveal);
  }
  .bottom-nav.is-visible {
    opacity: 1;
    transform: none;
    pointer-events: auto;
  }
  .bottom-nav .thread-tag {
    /* .thread-tag turns into a row below 560px. These stay stacked — dot over
       label, the way the fan on the cover reads. */
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    padding: 9px 3px;
    /* touchTarget.default is 44. The extra height is for the label, which
       wraps to two lines in a fifth of a 375px screen. */
    min-height: 56px;
    text-align: center;
  }
  .bottom-nav .thread-tag::before { width: 5px; height: 5px; }
  .bottom-nav .thread-tag__title { font-size: 11px; line-height: 1.2; }

  /* The bar floats over the page, so the last band of the footer would sit
     underneath it. Clear its height plus whatever the safe area adds. */
  .footer { padding-bottom: calc(var(--rhythm-5) + 66px + env(safe-area-inset-bottom)); }

  /* "Contact Us" wrapped to two lines at 15px, which pushed the whole bar to
     107px — a seventh of the viewport, spent on chrome. */
  .nav__inner { padding-block: var(--rhythm-2); gap: var(--rhythm-2); }
  /* index.html uses .nav__cta, about.html a .btn-glass. Both wrapped to two
     lines at their desktop size and collided with the threads row. */
  .nav__cta { font-size: 12px; white-space: nowrap; letter-spacing: var(--tr-label); }
  .nav .btn-glass {
    flex: 0 0 auto;
    padding: var(--space-2) var(--space-4);
    font-size: 11px;
    white-space: nowrap;
    min-height: 44px;
  }

  /* touchTarget.default is 44px and these were 40, 28 and 31. The nav does not
     get taller for it: the 40px seal already sets the bar's height. */
  .nav__seal svg { width: 44px; height: 44px; }
  .nav__cta { min-height: 44px; }
  .contact-list__item a { display: inline-flex; align-items: center; min-height: 44px; }

  .thread__kicker { flex-direction: column; gap: var(--rhythm-3); }
}

/* Phone. Five threads in a row stop fitting somewhere around here, so the row
   wraps to two columns — and the fan goes with it: five curves reaching down
   to a single row have nothing to point at once that row becomes two. It is
   decorative and aria-hidden, so it simply goes. drawThreads() also detects
   the wrap on its own (it compares the tags' tops) and declines to draw, so
   the fan stays correct even if this breakpoint and the wrap ever disagree. */
@media (max-width: 560px) {
  .threads-svg { display: none; }
  .thread-tags { flex-wrap: wrap; justify-content: center; gap: var(--rhythm-2); }
  .thread-tag {
    flex: 0 1 calc(50% - var(--rhythm-2));
    flex-direction: row;
    justify-content: flex-start;
    gap: var(--rhythm-2);
    padding: var(--rhythm-2);
  }
  /* The two columns already land on the same x per side — flex-basis is
     identical across both rows. Centering each label within that shared box
     was what staggered them: "Who Am I?" and "Services" are different widths,
     so their centered content started at different x even though their boxes
     didn't. Left-aligning fixes both columns at once — but left-aligning both
     pulls all the ink toward the shared centre seam, since the right column's
     words never reach the outer edge of their own box either. Mirroring the
     right column outward instead puts the empty space where both columns
     already met — one gap down the centre — rather than splitting it behind
     every row, and reads as the same fan-from-the-centre idea the desktop
     thread curves draw explicitly. */
  .thread-tag:nth-child(2), .thread-tag:nth-child(4) { justify-content: flex-end; }

  /* The fifth tag has no partner and was already centered as a whole box by
     .thread-tags' own justify-content — this only re-centers its label inside
     that box, so The Verdict stays exactly where it was. */
  .thread-tag:nth-child(5) { justify-content: center; }
}

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
}
