/*
  Homepage layout. Coordinates are carried over directly from the AxureRP
  export's files/index/styles.css so every element lands at the same pixel
  position and size as the original prototype. Where Axure recomputed a
  near-identical offset per nav item (a few px of drift from its own
  bounding-box math), the offsets are unified here as one rule reused by
  all three nav items, vertically centered instead of hard-coded per item.
*/

body {
  overflow: hidden;
  height: 100vh;
}

.hero {
  position: fixed;
  left: 50%;
  top: 50%;
  margin-left: -501px;
  margin-top: -275px;
  width: 1202px;
  height: 750px;
}

/* The rail sits ON the outline's old centreline (206,147) — the pieces are 1px
   now, so there is no half-stroke to offset for.
   Opacity (0 at rest, animated to 0.5) comes from the shared .intro-animate
   rules in components.css. */
.rail {
  left: 206px;
  top: 147px;
  width: 1069px;
  height: 533px;
}

/* The home-page wordmark. Deliberately NOT called .logo -- components.css owns
   that name for the round client-logo badge on the case-study cards, and its
   border-radius/background/display would leak in here. */
.wordmark {
  position: absolute;
  left: 18px;
  top: 0;
  width: 844px;
  height: 136px;
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 400;
  font-size: 84px;
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(40px);
  /* See the failsafe note further down: the parking is unconditional, so
     something has to be able to undo it without JS. `intro-failsafe` is
     components.css's keyframe — they are global, and this is the same state. */
  animation: intro-failsafe 0.5s var(--ease-out-cubic) 2.5s forwards;
}
.wordmark .dim { color: rgba(255, 255, 255, 0.49); font-size: 95px; vertical-align: super; }
.wordmark .accent { color: var(--color-accent-dim); font-size: 95px; letter-spacing: -4.4px; vertical-align: super; }
.wordmark .name { color: var(--color-white); font-size: 106px; letter-spacing: 4.5px; }

.tagline {
  position: absolute;
  left: 217px;
  top: 117px;
  width: 632px;
  height: 34px;
  margin: 0;
  font-family: var(--font-body);
  font-size: 27px;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-40px);
  /* Its own keyframe, not the shared one: the tagline's settled state is not
     just visible, it is 315,440 — the end of the arc js/intro-timeline.js flies
     it along. A failsafe that only restored opacity would leave it parked at
     217,117, on top of the wordmark. */
  animation: tagline-failsafe 0.5s var(--ease-out-cubic) 2.5s forwards;
}

@keyframes tagline-failsafe {
  to { opacity: 1; transform: none; left: 315px; top: 440px; }
}

.tagline .accent { color: var(--color-accent); }

/* The entrance start state comes from .intro-panel in components.css, shared
   with the About page's overview block.

   ==========================================================================
   THE LEADING IS 30px, IT IS A LITERAL, AND IT MUST STAY ONE.
   ==========================================================================
   This is the source's own value — files/index/styles.css states
   `#u5 / #u24 / #u25 { font-size: 19px; line-height: 30px }`, and data.js
   confirms `lineSpacing: 30px` on all three widgets. The 209px boxes were drawn
   for it: six lines at 30 is 180, which sits inside 209 with room under the
   last item. At 22.5 the content is 135 and the columns float in their boxes.

   IT USED TO READ `var(--line-height-body)` AND THAT IS THE BUG. That token is
   base.css's SITE-WIDE reading voice — Axure's 30px body leading reduced 25% to
   22.5px, a decision about running prose on the About pages and the case
   studies. These three columns are not running prose; they are six one-word
   lines in a fixed box. Pointing them at the token meant every future
   adjustment to the site's body copy silently re-broke this page, which is
   exactly what kept happening. There is no longer any path from another page's
   stylesheet to this number.

   DO NOT re-tokenise this, and do not "tidy" it back to
   var(--line-height-body). If the site's body leading changes again, these
   columns must not move.

   The selector is `p.aspect`, not `.aspect`, on purpose. `.aspect` is (0,1,0),
   the same weight as a single-class rule in components.css — and components.css
   loads FIRST, so a future `.intro-panel { line-height: ... }` there would beat
   this by nothing more than file order. That trap has already cost this project
   once (see CLAUDE.md, "Equal specificity means file order decides"). The
   element selector makes it (0,1,1), which no single-class rule can reach. */
p.aspect {
  position: absolute;
  top: 213px;
  height: 209px;
  margin: 0;
  font-family: var(--font-body);
  font-size: 19px;
  line-height: 30px;
  white-space: nowrap;
}
.aspect .lead { color: rgba(255, 255, 255, 0.7); }
.aspect .colon { color: var(--color-accent); font-family: var(--font-body-black); font-weight: 900; }
.aspect .item { display: block; color: rgba(255, 255, 255, 0.39); }

.aspect-1 { left: 320px; width: 89px; }
.aspect-2 { left: 527px; width: 100px; }
.aspect-3 { left: 744px; width: 118px; }

.site-nav {
  --nav-spine: 187px;

  /* The group spans the whole hero, but the line belongs to the rail: it starts
     where the corner's arc ends (147 + 30.5) and runs to y=710, where the
     outline's leg used to — 40px short of the hero's 750.

     The .5 is the corner's own height, not a fudge — see .nav-line in
     components.css for why butting at 30.5 rather than 30 or 31. */
  --nav-line-top: 177.5px;
  --nav-line-bottom: 40px;
  position: absolute;
  left: 19px;
  width: 205px;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* The nav item lives in components.css and dresses itself. This page sets only
   where it goes. */
.nav-item {

  width: 205px;
  /* Entrance start state comes from .intro-animate .nav-item in components.css. */
}
.nav-about   { top: 197px; height: 62px; }
.nav-projects{ top: 276px; height: 57px; }
.nav-contact { top: 349px; height: 58px; }

/* Reduced-motion / no-JS fallback for the two pieces unique to this page. The
   nav items, the aspect columns and the outline are covered by the shared
   .intro-animate rules in components.css.

   NOTE: this used to target `.logo`, a leftover from before the home-page
   wordmark was renamed away from that class (components.css owns `.logo` for
   the round client badge — see CLAUDE.md). Nothing matched, so on a genuine
   reduced-motion or no-JS load the wordmark stayed at opacity 0 and the page
   opened with no name on it. */
/* Same arrangement as the shared .intro-animate rules in components.css, and
   for the same reason: `no-js` is in the markup, so using it to UNDO the
   parking made these two visible at first paint and then dropped them to
   nothing when the script removed the class. The parking stays unconditional
   and the fallback is a failsafe. */
html.no-motion .wordmark,
html.no-motion .tagline {
  opacity: 1;
  transform: none;
  animation: none;
}
html.no-motion .tagline {
  left: 315px;
  top: 440px;
}

html:not(.no-js) .wordmark,
html:not(.no-js) .tagline { animation: none; }

@media (scripting: none) {
  .wordmark, .tagline { opacity: 1; transform: none; animation: none; }
  .tagline { left: 315px; top: 440px; }
}


/* ############################################################################
   THE 2026 REWRITE
   ============================================================================
   Everything below was developed as a separate stylesheet loaded AFTER this
   file (css/index-next.css, since deleted) and is appended here verbatim rather
   than merged into the blocks above. That is deliberate.

   Appending reproduces the cascade that was actually tested. "A fourth
   stylesheet loaded last" and "the same rules at the end of this file" resolve
   identically — same specificity, same order — so promoting the candidate this
   way cannot change a single computed value. Hand-merging the declarations up
   into the original blocks would be a rewrite of verified geometry with nothing
   verifying it, and this project has been bitten twice by exactly that class of
   change (CLAUDE.md, "Equal specificity means file order decides").

   THE CONSEQUENCE, STATED PLAINLY: some selectors are now declared twice in
   this file, and the LATER one wins. `.wordmark` and `.tagline` in particular
   are set up for the old "I'm Jesse Morano" layout above and re-set for the new
   one here. When reading this file, read to the bottom before believing
   anything about those two.

   A de-duplication pass — folding these into the blocks above and deleting the
   originals — is worth doing and is NOT done. It needs the same measurement
   pass the numbers below already carry, against a page that is by then live.

   WHAT THIS REPLACED, at a glance:
     the teaser    three word-columns ("Web", "Mobile", "Diplomat")  ->  three outcomes
     the tagline   "Solving complexity + Delivering positive outcomes"
                   ->  "AI-First Design Lead : Research Strategy Code"
     the wordmark  "I'm Jesse Morano"  ->  "Jesse Morano" with a green triad
                   in the word space
     the entrance  wordmark and tagline now reveal from a shared horizon inside
                   clipping slots, instead of fading through one another
   ############################################################################ */


/* ==========================================================================
   THE TAGLINE — now the positioning line, not a mood line
   --------------------------------------------------------------------------
   THE SIZE STAYS AT index.css's 27px, AND THAT IS A MEASURED RESULT.

   This was first built at 30px on the reasoning that a positioning line needs
   more weight than the mood line it replaces, with a comment here claiming the
   shorter string would land at roughly the old line's width anyway. Measured,
   that was simply wrong — character-count arithmetic ignored the 26px of
   padding on each of the three disciplines. Actual ink, by Range:

       at 27px   625.1     <- the original line was 622
       at 28px   645.3
       at 30px   685.8

   So 27 is the size at which the new string occupies the footprint the layout
   was drawn around: it ends at x 940 against the old line's 937, keeping the
   same relationship to the wordmark's right edge at 861. 30px pushed it to
   1001 and quietly changed the page's right-edge rhythm.

   It also does not need the extra size. The claim gets its weight from being
   the only sentence on a page of fragments, from the green marks, and from the
   blue run — and the three figures above it are already set at 58px, so a
   grown tagline would have been competing with its own evidence.

   There is therefore no font-size declaration below: index.css's 27px is
   correct and is left to stand.

   (A paragraph here used to explain why a single green COLON separated the
   phrase from the disciplines, and why the disciplines themselves were then
   separated by space rather than interpuncts. Both are gone: the colon became a
   three-dot mark, and that became three green pluses. The reasoning that
   replaced it is on `.tagline .accent` further down.)
   ========================================================================== */

.tagline {
  /* index.css states 632px for the old string. Harmless with `nowrap` — the
     text overflows a narrow box rather than wrapping — but a stale number that
     describes nothing is worse than none. */
  width: auto;
}

/* ---- the line's four items, and the three green pluses ---------------------
   THE BLUE COVERS THE ROLE ONLY — "Design Lead" — NOT "AI-First".

   ⚠️ This block briefly claimed the opposite, on the argument that pluses join
   peers so the whole leading phrase had to be blue like the three disciplines.
   The owner's instruction is that "AI-First" stays white, and it is the better
   call: "AI-First" is the qualifier and "Design Lead" is the thing being
   claimed, so colouring the role alone makes the line say what the résumé's
   own title says. The whole-line version flattened that distinction.

   So the line reads in three colours, each meaning one thing:

       white   AI-First                     the qualifier
       blue    Design Lead / Research /     the claims themselves
               Strategy / Code
       green   + + +                        punctuation only

   base.css reserves the saturated green for NAVIGATION and gives text that must
   stand apart from body copy this blue instead, which is why the claims are
   blue and only the marks are green.

   The pluses stay green, from `.tagline .accent` near the top of this file.
   Green as PUNCTUATION rather than as text is the page's existing pattern: the
   wordmark's triad, the nav dots and the rail are all this colour.

   GAP: 13px each side of every plus, and it is a margin so it is symmetric by
   construction. The old arrangement could not say that — the colon sat with 0px
   before it and 26px after, because the glyph butted against the "d" while the
   first discipline carried a padding-left. There is no padding on the words at
   all now; every gap in the line is one of these margins. */
.tagline {
  --tagline-plus-gap: 13px;
}

/* The qualifier. Near-white, as it was before the pluses went in. */
.tagline .claim {
  color: rgba(255, 255, 255, 0.92);
}

/* ---- the capital "I" of "AI-First" -----------------------------------------
   Kumbh Sans gives that letter no serifs and no distinguishing height — its cap
   height and its lowercase ascender are the same — so the glyph is the lowercase
   "l" and the word reads as "Al". This swaps the FACE for that one character.

     Josefin Slab    the wordmark's sister face — Josefin's own skeleton with
                     serifs, so the letter belongs to the page rather than
                     arriving from outside it

   `font-size` is NOT inherited blindly. Two faces at the same nominal size do
   not share a cap height, so the substitute is scaled until ITS cap matches
   Kumbh's — otherwise the I stands taller or shorter than the A beside it and
   the fix reads as a mistake. The number below is measured, not chosen; see the
   note where it is set. */
.tagline .claim-eye {
  font-family: "Josefin Slab", Georgia, serif;
  font-size: 1.0463em;
  font-weight: 380;

  /* TWO NUMBERS, BOTH MEASURED, NEITHER OPTIONAL.

     ── size: 1.0463em ──────────────────────────────────────────────
     Cap height of "I" at 100px, canvas actualBoundingBoxAscent:

         Kumbh Sans     73.24     <- what the "A" beside it is set in
         Josefin Slab   70.00     ->  73.24 / 70.00 = 1.0463

     Two faces at one nominal size do not share a cap height. Without this
     the substituted I stands ~4.5% short of the A and the repair reads as
     a typo.

     ── weight: 380 ─────────────────────────────────────────────────
     A face's weight NUMBER is not a promise about its stroke. Josefin Slab
     200 sets a stem 41.7% thinner than Kumbh Sans 200 at the same optical
     size, which is why the I first went in looking spindly. Stem widths,
     measured across the middle row of the glyph at 10x on-page size:

         Kumbh Sans 200      12      <- the target
         Josefin Slab 200     7
         Josefin Slab 300     9
         Josefin Slab 400    13
         Josefin Slab 700    24

     No static cut hits 12, which is why base.css loads the VARIABLE face.
     Sweeping the axis, 370 / 375 / 380 / 385 / 390 all measure 12. 380 is
     the middle of that band, so rounding drift in either direction still
     lands on a match.

     Re-measure both if the tagline's size or either face changes. Cap
     heights and stem widths are per-face, and this project has been caught
     assuming otherwise before (CLAUDE.md, Birdzi).
 */
}

/* `.claim-role`, not `.role`: css/about-testimonials.css already owns `.role`.
   That one is scoped to `.quote` so it could not actually reach in here, but
   components.css is global and this project has been bitten by a shared class
   name leaking before (`.logo` into the home-page wordmark — CLAUDE.md). A name
   that cannot be confused costs nothing. */
.tagline .claim-role,
.tagline .item {
  color: var(--color-accent-cool);
}

/* Colour comes from `.tagline .accent` above — the port's own rule, unchanged.
   This adds only the spacing. */
.tagline .accent {
  margin: 0 var(--tagline-plus-gap);
}

/* ==========================================================================
   THE TEASER — three outcomes
   --------------------------------------------------------------------------
   Occupies the same slot the three word-columns did: x 320 (level with the
   tagline's 315 to within the optical difference between a cap A and a cap S),
   y 213.

   780 WIDE, NOT 862. Three equal columns on the full 862 put the last caption's
   right edge at x 1182 — 20px off the canvas edge, and 323px past where the old
   columns' ink stopped at 859. Measured. 780 lands it at 1098, which sits under
   the wordmark's right end rather than past it.
   ========================================================================== */

.proof {
  position: absolute;
  left: 320px;
  top: 213px;
  width: 780px;
  display: flex;
  gap: 56px;
}

.proof-item {
  flex: 1 1 0;

  /* THE <p> DEFAULT MARGIN HAS TO GO, AND ITS ABSENCE IS NOT COSMETIC.
     These are `<p>` elements, so they arrive with UA `margin: 1em 0` — 16px.
     Flex items do not collapse margins, so that 16px sat inside the row and
     the first figure's ink began at y 228 while this block is placed at 213.
     The stated coordinate has to be the rendered one or every later
     measurement against it is off by a number nobody wrote down. index.css
     zeroes `p.aspect`'s margin for exactly this reason. */
  margin: 0;

  /* Carries .intro-panel in the markup, so each column fades in on its own
     delay — see the stagger note in index.html. */
}

/* Josefin, like the wordmark, and NOT Kumbh like the rest of the page. These
   are the only other display-size glyphs on the home page; setting them in the
   body face at 58px reads as body copy that has been zoomed, because Kumbh's
   200 weight has no display cut and thins out as it scales.

   WHITE, not the cool blue. The blue is spent once, on the tagline's claim.
   These are the proof of that claim and want maximum brightness — and a second
   blue at 58px would out-shout the 30px line it is supposed to be supporting.
   Flip `--proof-figure` to `var(--color-accent-cool)` to see the alternative;
   it is a one-line change and both were rendered before this was chosen. */
:root { --proof-figure: rgba(255, 255, 255, 0.9); }

.proof-figure {
  display: block;
  font-family: var(--font-heading);
  font-weight: 400;
  /* 66, up from 58 (owner, 2026-10-13: "slightly larger"). The captions are
     in flow beneath, so they move down with it; the tagline is placed on its
     own and does not. */
  font-size: 66px;
  line-height: 1;
  letter-spacing: 1px;
  color: var(--proof-figure);

  /* Shrink-wrapped so the hover line below is centred on the FIGURE, not on
     the 223px column. Positioned so the line can hang off it. */
  position: relative;
  width: fit-content;
  transition: color 0.2s var(--ease-out-cubic);
}

/* HOVER — the whole teaser is the target (see .proof-link::after), and on it
   the figure turns the same blue as the link, and a line grows under it.
   Owner, 2026-10-13.

   The line is the green-dot nav buttons' own (components.css, .nav-item::after):
   1px, the nav ring grey, scaleX 0 -> 1 over 0.5s on --ease-out-cubic. Two
   differences, both asked for: it grows OUT FROM ITS CENTRE (transform-origin
   center, where the nav's is pinned right), and so it fades at BOTH ends
   rather than one. It sits on the figure box's bottom edge, 66px down — which
   lands it midway between the figure's baseline (49) and the caption (77). */
.proof-figure::after {
  content: "";
  position: absolute;
  left: 0;
  top: 100%;
  width: 100%;
  height: 1px;
  background: linear-gradient(to right,
    transparent, var(--color-nav-ring), transparent);
  transform-origin: center;
  transform: scaleX(0);
  transition: transform 0.5s var(--ease-out-cubic);
  pointer-events: none;
}

.proof-item:hover .proof-figure,
.proof-item:has(.proof-link:focus-visible) .proof-figure {
  color: var(--color-accent-cool);
}

.proof-item:hover .proof-figure::after,
.proof-item:has(.proof-link:focus-visible) .proof-figure::after {
  transform: scaleX(1);
}

.proof-caption {
  display: block;
  /* 11, was 14 — captions moved up 3 (owner, 2026-10-13). */
  margin-top: 11px;
  font-family: var(--font-body);
  font-size: 17px;
  /* 26px, stated as a literal for the same reason index.css states 30 for the
     old columns: this is a three-line caption in a fixed box, not running
     prose, and it must not move when the site's body leading is next tuned. */
  line-height: 26px;
  color: rgba(255, 255, 255, 0.5);
}

/* The noun the number is about. Not bold — the page has no bold anywhere — just
   returned to full strength out of the caption's 0.5. */
.proof-caption b,
.proof-link {
  font-weight: 400;
  color: rgba(255, 255, 255, 0.85);
}

/* The noun links to its case study (owner, 2026-10-13). At rest it is the
   same 0.85 white as the <b> it replaced, so the settled page is unchanged.
   The hover is the site's existing text-link hover — Manual Quoting's "View
   Detailed List" (css/projects-xom-MQ.css): the cool blue and a 3px-offset
   underline — not a new one. */
.proof-link {
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 3px;
  transition: color 0.2s var(--ease-out-cubic),
              text-decoration-color 0.2s var(--ease-out-cubic);
}

.proof-link:hover,
.proof-link:focus-visible {
  color: var(--color-accent-cool);
  text-decoration-color: currentColor;
}

/* THE WHOLE TEASER IS THE CLICK TARGET; THE HOVER STILL SHOWS ON THE NOUN.
   (Owner, 2026-10-13.) The link's ::after is stretched over its .proof-item.
   A hit on a pseudo-element resolves to the element that generates it, so a
   pointer anywhere on the teaser — figure, caption, the gaps between — is ON
   the <a>: it matches :hover, so the noun turns blue exactly as before, it
   shows the link cursor, and the click navigates. Same principle as the nav
   items' hit areas (components.css). Still one <a> per teaser, so a screen
   reader hears the noun as the link text, not the whole caption. */
.proof-item {
  position: relative;
}

.proof-link::after {
  content: '';
  position: absolute;
  inset: 0;
}

/* The focus ring goes round the click target, not just the noun, so a keyboard
   user sees what Enter will follow. Pointer users never see it. */
.proof-link:focus-visible {
  outline: none;
}

.proof-link:focus-visible::after {
  outline: 1px solid var(--color-accent-cool);
  outline-offset: 8px;
}

/* ==========================================================================
   THE HORIZON REVEAL
   --------------------------------------------------------------------------
   The wordmark rises out of a fixed line and the tagline drops out of the same
   line, and NEITHER can ever paint on the other's side of it. They do not fade
   through one another any more because they are not merely faded apart — they
   are geometrically prevented from meeting.

   WHY THERE WAS AN OVERLAP AT ALL. Measured on this page:

       wordmark ink   y   0 -> 127      (its box runs to 136)
       tagline ink    y 117 -> 151      at its pre-arc start position

   so the two INK boxes overlap by 10px, over the x range they share, before
   anything animates. On top of that the entrance drives them THROUGH each
   other: the wordmark starts 40px low (ink 40->167) and the tagline starts
   40px high (ink 77->111), which puts the whole tagline inside the wordmark's
   ink band at t=0. Both are semi-transparent on the way in, so they show
   through each other. That is the artefact being removed.

   THE MECHANISM IS THE SITE'S OWN, NOT A NEW INVENTION. `overflow: hidden` on
   a wrapper plus a transform on the child is exactly how the nav chevron
   wipes in and out of its 12x26 slot (CLAUDE.md, "THE CHEVRON SLIDES INSIDE A
   CLIPPING SLOT"), and it is what Axure itself does — `visibility.js`,
   `_makeContainer`. This is the same idea with the slot's edge shared by two
   elements instead of wrapping one.

   THE HORIZON IS 132px, AND THAT NUMBER IS DERIVED. It has to sit below the
   wordmark's ink (127) and at or above the tagline's start. 132 gives the
   wordmark 5px of clearance and puts the tagline's ink top exactly on the
   line. This moves the tagline's pre-arc start from the source's y=117 down
   to 132 — a 15px change to a position that is on screen for 2.8s and then
   flies away, which is the cheapest place to pay for a clean horizon. The
   post-arc resting spot (315, 440) is untouched.

   Change `--horizon` and everything follows: both slot heights, the wordmark's
   parked distance, and the arc's origin (which js/intro-timeline.js now reads
   from the slot's own computed `top` rather than a literal).
   ========================================================================== */

.hero { --horizon: 132px; }

/* Everything above the line. Full width so nothing is clipped sideways. */
.wordmark-slot {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: var(--horizon);
  overflow: hidden;
}

/* Everything below the line — and this box MOVES, carrying the mask with the
   text. That is the whole trick, and it is the thing Axure cannot express: its
   masked images are pinned to their mask. Here the arc animates the SLOT's
   left/top, so the text and its clipping window travel as one object.

   No width or height: the tagline inside is switched to `position: static`
   below, so this shrink-wraps it. A stated height would be a magic number that
   silently clips the day the string changes length or the face is re-cut. */
.tagline-slot {
  position: absolute;
  left: 217px;
  top: var(--horizon);
  overflow: hidden;
}

/* ---- the two inners --------------------------------------------------- */

/* Parked a full slot-height DOWN, so every pixel that would paint is below the
   slot's bottom edge. Translating by exactly the slot's own height is what
   makes "hidden" provable rather than eyeballed. index.css parks this at 40px,
   which was never enough to clear anything — it only moved it. */
.wordmark {
  transform: translateY(var(--horizon));
}

/* Static, so .tagline-slot can shrink-wrap it. index.css positions this
   absolutely at (217, 117) with a stated 632x34; all of that now belongs to
   the slot, so it is unwound here rather than fought with. */
.tagline {
  position: static;
  left: auto;
  top: auto;
  width: auto;
  height: auto;
  margin: 0;
  /* -100% is its own height, which is the slot's height, so this parks it
     exactly clear of the slot's TOP edge. */
  transform: translateY(-100%);
  /* index.css runs `tagline-failsafe` here, which lands this element at
     left:315/top:440. Those properties no longer do anything on a static box,
     and the slot is what has to move instead — see the failsafe below. */
  animation: intro-failsafe 0.5s var(--ease-out-cubic) 2.5s forwards;
}

/* ---- failsafes -------------------------------------------------------- */
/*
   THIS BLOCK IS NOT OPTIONAL AND IT IS NOT DEFENSIVE PROGRAMMING.

   The parking above is unconditional, and with `overflow: hidden` in play the
   failure is worse than it was before: a wordmark parked below its slot is not
   merely transparent, it is CLIPPED AWAY. If js/intro-timeline.js 404s or
   throws, the page renders with no name on it at all. That exact failure —
   scripting on, script never arrived — is the one that once rendered this whole
   site blank, and it is why the parking is not simply `opacity: 0`. See
   "Hidden by default" in CLAUDE.md and the header of js/intro-timeline.js.

   Four states, matching the pattern index.css already uses:
     the parking       unconditional, above
     the script        removes `no-js`, which cancels the failsafe below
     scripting: none   scripting off for good — show at once
     the 2.5s keyframe scripting ON but the script never arrived
*/

@keyframes tagline-slot-failsafe { to { left: 315px; top: 440px; } }

.tagline-slot {
  animation: tagline-slot-failsafe 0.5s var(--ease-out-cubic) 2.5s forwards;
}

/* The script is running, so it owns these. An animation beats a plain
   declaration in the cascade, so a failsafe left armed would lock the slot at
   315/440 and the arc would have nothing to animate. */
html:not(.no-js) .wordmark,
html:not(.no-js) .tagline,
html:not(.no-js) .tagline-slot { animation: none; }

@media (scripting: none) {
  .wordmark, .tagline { opacity: 1; transform: none; animation: none; }
  .tagline-slot { left: 315px; top: 440px; animation: none; }
}

/* index.css's own no-motion rules put `.tagline` back at left:315/top:440.
   That is now the SLOT's job, and the inner must land at transform:none. */
html.no-motion .wordmark,
html.no-motion .tagline {
  opacity: 1;
  transform: none;
  animation: none;
  left: auto;
  top: auto;
}
html.no-motion .tagline-slot {
  left: 315px;
  top: 440px;
  animation: none;
}

/* ==========================================================================
   THE TRIAD — three dots where "I'm" used to be
   --------------------------------------------------------------------------
   A colon with an extra dot. It reads as punctuation introducing the name, and
   it inherits the site's own colon language: `.aspect .colon` on the old teaser
   columns and `.tagline .colon` below are both a green mark doing exactly this
   job. This is that mark, drawn rather than set.

   HALF-SCALE IS LITERAL, NOT APPROXIMATE. `.nav-dot` (components.css) is a
   16px border-box circle with a 2px border and a 24.3% green fill, giving a
   12px visible core. Half of that is 8px with a 1px border — the border has to
   halve too, or the ring reads four times heavier relative to its own diameter
   and the dots turn into solid pips. The fill colour is taken unchanged, so
   these are the nav dot at 50%, not a new object that resembles it.

   IT SITS IN THE WORD SPACE, BETWEEN "Jesse" AND "Morano" — not before the name,
   which is where it started out. It is absolutely positioned and therefore
   OVERLAYS that space rather than widening it, so "Morano" does not shift and
   the wordmark's right edge stays at 861.14. That matters: 861 is what the
   tagline (ends 940) and the proof block (ends 1100) are balanced against.

   THE LOGO DOES NOT MOVE. Its ink starts at x 165.1 and that is fixed by
   `left` on the box itself.

   IT USED TO BE HELD THERE BY A PHANTOM MARGIN, AND IS NOT ANY MORE. The box
   sat at `left: 18px` — the Axure original's coordinate, from when "I'm"
   occupied that space — and a `margin-left: 147.1px` on the name pushed the
   ink back out to 165.1. Two numbers, neither of which described anything: 18
   was where nothing started, and 147.1 was holding a gap open for a mark that
   had since moved into the word space.

   Now the box's own `left` IS where the ink starts, and there is no margin.
   Same rendered position to the pixel — 165.1 either way, verified — with one
   number instead of two and no space being reserved for an absent glyph.

   ⚠️ 165.1 IS INHERITED, NOT A SOURCE COORDINATE. It is where the name landed
   when "I'm Jesse Morano" was set at the original's `left: 18`, and the owner's
   instruction is that it stays there: the right edge at 861 is what the tagline
   (ends 982) and the outcomes block (ends 1100) are balanced against, and the
   horizon reveal is measured off this ink. Do not "restore" it to 18.
   ========================================================================== */

.wordmark {
  /* WHERE THE LOGO STARTS. Overrides the `left: 18px` in the original block
     near the top of this file, which described the old "I'm Jesse Morano"
     setting and describes nothing now. See the header above before changing
     it — it is load-bearing for three other things. */
  left: 165.1px;

  /* "mark" throughout these variables means THE THREE GREEN DOTS between
     "Jesse" and "Morano" — not the logo. `--mark-x` is the dots' left edge in
     logo-local coordinates. */
  --mark-size: 8px;

  /* THE TRIAD NOW SITS IN THE WORD SPACE, AND SPANS THE "e".
     All four numbers are measured on this exact face at this exact size.

       word space     x 422.81 -> 460.80   (Range over the two words in the
                      text node; 37.99px of room)
       its centre     441.80  ->  an 8px dot leaves ~15px of air either side
       x-height       45.69   canvas actualBoundingBoxAscent of "e"
       baseline       y 100   zero-height vertical-align:baseline probe
       so the "e"     y 54.31 -> 100

     `--mark-x` / `--mark-y` are LOGO-LOCAL, so both subtract the box's own
     origin — now left 165.1, top 21. (`--mark-x` was 419.8 while the box sat at
     left 18; the box moved 147.1 right and the dots' local offset moved 147.1
     left, so the dots land on the same page pixel, 437.8.) */
  --mark-x: 272.7px;       /* 441.80 - 165.1 - 4  (centre the 8px dot in the space) */
  --mark-y: 33.31px;       /* 54.31 - 21          (top of the "e") */
  --mark-span: 45.69px;    /* the x-height itself */

  /* ⚠️ DELETING "I'm" MOVED THE NAME UP 21px, AND THAT IS NOT OBVIOUS.
     "I" and "m" were set at 95px with `vertical-align: super` (index.css), and
     a super-aligned inline box is part of what the line box is sized around.
     With them gone the line is built on the 106px name alone, and it re-seated
     itself higher: measured, the name's ink box went from y 20 -> 127 to
     y -1 -> 106.

     -1 means THE TOP OF THE "J" AND "M" WERE BEING CLIPPED — .wordmark-slot's
     window starts at y 0, so a pixel of the caps was cut off. That is the kind
     of fault a screenshot hides and a measurement does not.

     21px puts the name back exactly where it was: ink top 20, and 5px of
     clearance under the horizon at 132 — both identical to the values the
     horizon was derived from. Nothing else on the page has to be re-derived. */
  top: 21px;
}

/* Absolutely positioned, so the triad cannot influence the line box. An inline
   mark at this size next to a 106px face would sit on the baseline and drag the
   line's metrics around; the name's own ink position is measured and must not
   move. .wordmark is already `position: absolute`, so it is the containing
   block and these coordinates are wordmark-local. */
.wordmark-mark {
  position: absolute;
  left: var(--mark-x);

  /* CENTRED ON THE CAP BAND, AND EVERY NUMBER IN THAT IS MEASURED.

     Not centred on the ink box: that box runs to the descender of the "J", and
     "J" is the only glyph in "Jesse Morano" that descends at all, so centring
     on it would hang the triad visibly low against eleven letters that stop at
     the baseline.

       cap height     79.08px   canvas TextMetrics, actualBoundingBoxAscent of
                                "M" at this exact face/size — no descender, so
                                the ascent IS the cap height
       baseline       y 100     zero-height inline-block probe with
                                vertical-align: baseline (the same technique
                                that found the nav bar's shared y=50 baseline)
       cap band       20.92 -> 100
       its centre     60.46
       triad height   36        3 x 8 dot + 2 x 6 gap
       local top      21.46     60.46 - 18, minus the wordmark's own 21px top

     Re-derive all of it if the face, the size or the string changes — cap
     heights are per-string, not per-face (CLAUDE.md, Birdzi). */
  top: var(--mark-y);
  height: var(--mark-span);
  display: flex;
  flex-direction: column;

  /* THE GAPS ARE AN OUTPUT, NOT A SETTING. The brief fixes the SPAN (top of the
     "e" to the baseline) and the dot size independently, so a stated gap would
     be a third number that has to agree with the other two and silently will
     not. `space-between` on a fixed-height column derives it: at 45.69 span and
     8px dots the gaps come out at 10.84 each. Change `--mark-size` and they
     re-derive on their own; the triad still spans the "e" exactly. */
  justify-content: space-between;
}

.wordmark-mark i {
  display: block;
  width: var(--mark-size);
  height: var(--mark-size);
  box-sizing: border-box;
  border: 1px solid var(--color-accent);
  border-radius: 50%;
  background: rgba(53, 255, 0, 0.243);   /* .nav-dot's fill, unchanged */
}

