/* ==========================================================================
   Orion V3 layer (dispatch D3, 2026-10-05) = V1 + the V2 pieces Eric picked + his layout notes.
   Spec: Client Projects/Orion/Orion/Orion Website v2/feedback/ERIC_ROUND1_2026-10-05.md (verbatim) and BUILD_STATE D3.
   Loaded ONLY by the PUBLIC_VARIANT=v3 build. Colour: theme tokens only. Hairlines 1px.

   1. Static (applies with or without motion)
      1a The Message (V2's concept layout; active entry = bold date + full-width ink rule, no title underline)
      1b Layout fixes. Each block is a clean layout change: when V3 becomes the default build, move it into the
         page's own stylesheet (src/styles/pages/<page>.css) and drop the html[data-variant="v3"] prefix.
      1c Hover end-states without motion (reduced motion = the same states, instantly)
      1d Round 2 (D4): menu current row bold rule, sticky masthead < 960, The Message archive + Signal on portrait tablets
   2. Motion: everything inside ONE prefers-reduced-motion: no-preference block (reduce = static, nothing moves)
      2a title travel (Home Writing row -> The Message / Signal; The Message list -> note)
      2b headline word rise (no hairline sweep)      2c page-load fade-ins, sequences, card cascade
      2d index rows                                    2e Services cards        2f text links
   Not in V3 (V1 behavior kept): masthead/divider/section line draws, nav sliding underline, mobile-menu type-in,
   contact field line draws + accent focus line, success type-in, page cross-fades.
   ========================================================================== */

/* ---------- 1a. The Message (V2 concept layout, V3 states) ---------- */
html[data-variant="v3"] .page--message-concept .pub .t{width:fit-content}
html[data-variant="v3"] .page--message-concept .feat .ttl{font-weight:500;font-size:clamp(25px,2.65vw,34px);line-height:1.12;
  letter-spacing:-.017em;text-wrap:balance;max-width:22ch;width:fit-content}
html[data-variant="v3"] .page--message-concept .feat .ttl:focus:not(:focus-visible){outline:0}
html[data-variant="v3"] .page--message-concept .opened .more{margin-top:clamp(14px,2.2vh,22px)}

/* one note open at a time. JS on: .is-open. JS off: the entries are #fragment links and :target shows the note. */
html[data-variant="v3"] .page--message-concept .opened{display:none}
html[data-variant="v3"] .page--message-concept .opened.is-open{display:block}
html[data-variant="v3"]:not(.v3-js) .page--message-concept .opens:has(.opened:target) .opened.is-open{display:none}
html[data-variant="v3"]:not(.v3-js) .page--message-concept .opened:target{display:block}

html[data-variant="v3"] .page--message-concept .right{min-height:0}
html[data-variant="v3"] .page--message-concept .arc-l{display:block;margin-bottom:clamp(11px,1.7vh,17px)}
html[data-variant="v3"] .page--message-concept .years{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  scrollbar-width:thin;scrollbar-color:var(--rule) transparent}
html[data-variant="v3"] .page--message-concept .yr + .yr{margin-top:clamp(14px,2.2vh,22px)}
html[data-variant="v3"] .page--message-concept .yr summary{display:flex;justify-content:space-between;align-items:baseline;
  list-style:none;cursor:pointer;padding-bottom:clamp(9px,1.4vh,13px);border-bottom:1px solid var(--ink);position:relative}
html[data-variant="v3"] .page--message-concept .yr summary::-webkit-details-marker{display:none}
html[data-variant="v3"] .page--message-concept .yr summary:hover .y{color:var(--ink)}
html[data-variant="v3"] .page--message-concept .yr .c::after{content:"\2009\2212"}
html[data-variant="v3"] .page--message-concept .yr:not([open]) .c::after{content:"\2009+"}
html[data-variant="v3"] .page--message-concept .list{list-style:none}
html[data-variant="v3"] .page--message-concept .note{grid-template-columns:4.7em minmax(0,1fr);row-gap:2px}
html[data-variant="v3"] .page--message-concept .note .t{justify-self:start}
html[data-variant="v3"] .page--message-concept .note .x{grid-column:2;min-width:0;font-family:var(--serif);font-size:12.5px;
  line-height:1.45;color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* the open entry: date in bold + the full-width rule under the whole entry in ink. The title is NOT underlined. */
html[data-variant="v3"] .page--message-concept .note[aria-current]{border-bottom-color:var(--ink)}
html[data-variant="v3"] .page--message-concept .note[aria-current] .d{color:var(--ink);font-weight:600}
/* the page stays one screen from 768 up: the archive scrolls inside its column if a year runs long */
@media (min-width:768px){
  html[data-variant="v3"] .page.page--message-concept{height:100vh;height:100svh}
  html[data-variant="v3"] .page--message-concept .split{grid-template-rows:minmax(0,1fr)}
  html[data-variant="v3"] .page--message-concept .left{min-height:0}
}
@media (min-width:960px) and (max-height:960px){
  html[data-variant="v3"] .page--message-concept .left,
  html[data-variant="v3"] .page--message-concept .right{padding-top:clamp(20px,3.4vh,52px);padding-bottom:clamp(14px,2.2vh,38px)}
  html[data-variant="v3"] .page--message-concept .note{padding:clamp(6px,.95vh,11px) 0}
}
@media (max-width:767px){
  html[data-variant="v3"] .page--message-concept .years{overflow:visible}
  html[data-variant="v3"] .page--message-concept .opened{scroll-margin-top:16px}
}

/* ---------- 1b. Layout fixes (move into the page stylesheets when V3 is the default) ---------- */

/* Item 7. The Message: the description under "The Message" sets in two lines (55 em of text; 30 em measure) and
   never leaves one word alone on the last line where the column is narrower (tablets). */
html[data-variant="v3"] .page--message .pub .s{max-width:30em;text-wrap:pretty}

/* Item 4. Services header lede: the block sits flush with the right margin (its right edge = the rule's right end),
   text left-aligned inside it, wide enough to set in two lines (61 em of text; 34 em measure, em-based so the break is
   the same at every desktop width). The headline column takes the rest. 960 up only: below that the band stacks
   (768 x 1024) or keeps George's split (tall portrait), unchanged. */
@media (min-width:960px){
  html[data-variant="v3"] .page--services .band{grid-template-columns:minmax(0,1fr) auto}
  html[data-variant="v3"] .page--services .lede{max-width:34em;justify-self:end}
}

/* Item 9. Article: the reading column is 1.5x George's (61ch -> 91.5ch: 559 -> 839 px at 1440), the head and every
   section use the same column, the pull quote's measure goes 24ch -> 36ch. At 17 px the wider column runs 105
   characters a line (median), so the body takes one step up George's reading scale, 17 -> 19 px (the lead paragraph
   19 -> 21): median 97, see BUILD_STATE D3 item 9. text-wrap:pretty keeps a lone word off a paragraph's last line.
   The in-copy list steps with the body (14.5/14 -> 16/15.5 px) and keeps a shorter measure (notes, not running text). */
html[data-variant="v3"] .page--article .article,
html[data-variant="v3"] .page--article .head,
html[data-variant="v3"] .page--article .sec{grid-template-columns:3.6em minmax(0,91.5ch) 1fr}
html[data-variant="v3"] .page--article .body :is(p,li){font-size:19px;line-height:1.7;text-wrap:pretty}
html[data-variant="v3"] .page--article .body p.first{font-size:21px;line-height:1.62}
html[data-variant="v3"] .page--article .pull p{max-width:36ch}
/* Eric 10-05: the pull quote's top/bottom rules end where the body copy and the section rules end (George's .pull spans
   grid columns 2-4, which ran its rules into the right gutter). Desktop only; the phone layout is one column already. */
@media (min-width:881px){html[data-variant="v3"] .page--article .pull{grid-column:2}}
html[data-variant="v3"] .page--article .clist .t{font-size:16px}
html[data-variant="v3"] .page--article .clist .d{font-size:15.5px;max-width:80ch}
@media (max-width:880px){
  html[data-variant="v3"] .page--article .article,
  html[data-variant="v3"] .page--article .head,
  html[data-variant="v3"] .page--article .sec{grid-template-columns:1fr}
}
@media (max-width:767px){
  html[data-variant="v3"] .page--article .body :is(p,li){font-size:17px;line-height:1.75}
  html[data-variant="v3"] .page--article .body p.first{font-size:19px;line-height:1.66}
  html[data-variant="v3"] .page--article .clist .t{font-size:14.5px}
  html[data-variant="v3"] .page--article .clist .d{font-size:14px}
}

/* Item 10. Article in-copy numbered lists: no hairlines above the numbers (too many lines). Two sources: the section
   numeral's ink rule (George's `.sec .n` also matches the list's `.n`), and the list's own rules (ink above the first
   item, a soft rule between items). Spacing unchanged. */
html[data-variant="v3"] .page--article .clist .n{border-top:0;padding-top:0}
html[data-variant="v3"] .page--article .clist li,
html[data-variant="v3"] .page--article .clist li:first-child{border-top:0;border-bottom:0}

/* Item 12. Contact: "Send it" sits directly below the form fields (no longer pinned to the bottom of the column), the
   note sits directly below it, and the message field gets its own underline (so it darkens on focus like the others).
   The field is four lines tall; it no longer stretches to fill the column. The page stays one screen. */
html[data-variant="v3"] .page--contact form{flex:none}
html[data-variant="v3"] .page--contact .f.grow{flex:none}
html[data-variant="v3"] .page--contact .f.grow textarea{flex:none;height:7.2em;border-bottom:1px solid var(--rule)}
html[data-variant="v3"] .page--contact .f.grow textarea:focus{border-bottom-color:var(--ink)}
html[data-variant="v3"] .page--contact .f.grow textarea[aria-invalid="true"]{border-bottom-color:var(--accent)}
html[data-variant="v3"] .page--contact .send{margin-top:clamp(4px,.8vh,10px);padding-top:0;border-top:0;
  flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:clamp(8px,1.2vh,11px)}
/* Send it = the same link style as "Contact ->": light underline at rest (darkens on hover, 1c / 2f) */
html[data-variant="v3"] .page--contact button[type="submit"]{border-bottom-color:var(--rule)}
@media (max-width:767px){
  html[data-variant="v3"] .page--contact .f.grow textarea{height:auto;min-height:8em}
  html[data-variant="v3"] .page--contact .send{margin-top:2px}
}

/* Item 13. Contact, Speaking & board sessions: the copy sets in two lines (40 em of text; 23 em measure). */
html[data-variant="v3"] .page--contact .direct .sp{max-width:23em;text-wrap:pretty}

/* ---------- 1d. Round 2 (Eric 10-05 PM, dispatch D4; spec feedback/ERIC_ROUND2_2026-10-05.md) ---------- */

/* D4-1. Mobile menu: the current page's row gets a bold bottom rule (2 px, ink) instead of an underline under its word.
   A deliberate exception to George's 1 px hairline rule (Eric 10-05). Drawn as the row's own 1 px rule in ink plus a 1 px
   inset line directly above it, so the row keeps its height (no 1 px jump between pages) and the lint's border rule holds. */
html[data-variant="v3"] .menu .menu-list li:has(> a[aria-current]){border-bottom-color:var(--ink);box-shadow:inset 0 -1px 0 var(--ink)}
html[data-variant="v3"] .menu .menu-list a[aria-current] .nm{border-bottom-color:transparent}

/* D4-2. Sticky masthead wherever the menu replaces the nav (< 960 px: phones, portrait tablets): wordmark + Menu stay at
   the top while the page scrolls. Ground background carried edge to edge (box-shadow spread, clipped to the masthead's
   own height + 1 px: a clip on the fractional bottom edge would cut into the rule and paint it half-strength on tablets)
   so text never shows through the page margins; the masthead rule stays (its own border). Anchor and focus
   scrolling stop below it through scroll-margin-top on the targets (main, every focusable thing inside it, The Message
   note), = its height (variant.js keeps --v3-mast exact; the calc is the JS-off estimate). NOT scroll-padding on the
   root: that also covers the masthead's own Menu button, and focusing it (Shift+Tab) then scrolled the page ~420 px up.
   The menu overlay (z 40) still covers it, and its top bar sits exactly where this one sits. */
@media (max-width:959px){
  html[data-variant="v3"]{--v3-mast-h:var(--v3-mast,calc(clamp(20px,3.2vh,34px) + clamp(16px,2.4vh,24px) + 21px))}
  html[data-variant="v3"] .page > .masthead{position:sticky;top:0;z-index:30;background:var(--ground);
    box-shadow:0 0 0 100vmax var(--ground);clip-path:inset(0 -100vmax -1px)}
  html[data-variant="v3"] main{scroll-margin-top:var(--v3-mast-h)}
  html[data-variant="v3"] main :is(a,button,input,select,textarea,summary,[tabindex]){scroll-margin-top:calc(var(--v3-mast-h) + 12px)}
  html[data-variant="v3"] .page--message-concept .opened{scroll-margin-top:calc(var(--v3-mast-h) + 16px)}
}

/* D4-3. The Message archive on portrait tablets (768-959, the split's narrowest range; the date column took ~75 of
   ~232 px): the date sits above the title, title and excerpt take the full column, the excerpt shows two lines with a
   clean clamp instead of a one-line cut. Active state unchanged (bold date + ink rule). 960 up keeps the side column.
   600-767 (an iPad mini at 744 lays the page out stacked, the archive full width under the note): the date column has
   room there, so it stays beside the title, but the excerpt gets the same two-line clamp. Phones (< 600) unchanged. */
@media (min-width:600px) and (max-width:959px){
  html[data-variant="v3"] .page--message-concept .note .t{text-wrap:pretty}
  html[data-variant="v3"] .page--message-concept .note .x{white-space:normal;text-overflow:clip;text-wrap:pretty;
    display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden}
}
@media (min-width:768px) and (max-width:959px){
  html[data-variant="v3"] .page--message-concept .note{grid-template-columns:minmax(0,1fr);row-gap:3px}
  html[data-variant="v3"] .page--message-concept .note .x{grid-column:1}
}

/* D4-4. Signal on portrait tablets (768-959): the cover and the headline + short description sit side by side; the
   issue line and the 01-04 contents run below both at the column's full width. 960 up unchanged. */
@media (min-width:768px) and (max-width:959px){
  html[data-variant="v3"] .page--signal .feature{grid-template-columns:auto minmax(0,1fr);grid-template-rows:auto 1fr;row-gap:0}
  html[data-variant="v3"] .page--signal .feature > div{display:contents}
  html[data-variant="v3"] .page--signal .feature > .covlink{grid-column:1;grid-row:1 / span 2}
  html[data-variant="v3"] .page--signal .feature h1{grid-column:2;grid-row:1}
  html[data-variant="v3"] .page--signal .feature .stand{grid-column:2;grid-row:2;align-self:start}
  html[data-variant="v3"] .page--signal .feature .meta{grid-column:1 / -1;margin-top:clamp(18px,2.2vh,26px)}
  html[data-variant="v3"] .page--signal .feature .toc{grid-column:1 / -1}
}

/* ---------- 1c. Hover end-states (no motion needed; reduced motion shows these instantly) ---------- */
/* Text links (item 6): the underline darkens to ink; the text keeps its ink (no accent swap). */
html[data-variant="v3"] :is(.page--home,.page--about,.page--contact,.page--signal,.page--message-concept)
  :is(.mail,.more):is(:hover,:focus-visible),
html[data-variant="v3"] .page--contact button[type="submit"]:is(:hover,:focus-visible):not(:disabled){
  color:var(--ink);border-bottom-color:var(--ink)}
/* Index rows that are not links (items 2 + 5): the rule darkens on hover (V1 had no hover on these). */
html[data-variant="v3"] :is(.page--about .prin li,.page--about .area,.page--contact .steps li):hover{border-bottom-color:var(--ink)}

/* ---------- 2. Motion ---------- */
@media (prefers-reduced-motion:no-preference){

  /* 2a. Title travel only. Cross-document View Transitions are on, but every navigation except the Home Writing row
     -> The Message / Signal is skipped (boot script + variant.js), and the root has no animation by default, so even
     with JS off a navigation never cross-fades. During a title travel (html.v3-morphing) the content fades as in V2. */
  @view-transition{navigation:auto}
  ::view-transition-old(root),::view-transition-new(root){animation:none}
  html.v3-morphing::view-transition-old(root){animation:v3-vt-out .2s cubic-bezier(.4,0,1,1) both}
  html.v3-morphing::view-transition-new(root){animation:v3-vt-in .44s cubic-bezier(.2,.7,.2,1) .06s both}
  html.v3-morphing .page > .masthead{view-transition-name:v3-mast}
  html.v3-morphing .page > .colophon{view-transition-name:v3-colo}
  ::view-transition-group(v3-mast),::view-transition-group(v3-colo){animation-duration:.3s}
  ::view-transition-group(v3-title){animation-duration:.62s;animation-timing-function:cubic-bezier(.2,.7,.2,1)}
  ::view-transition-old(v3-title){animation:v3-fade-out .3s cubic-bezier(.4,0,1,1) both}
  ::view-transition-new(v3-title){animation:v3-fade-in .38s cubic-bezier(.2,.7,.2,1) .16s both}
  /* in-page open on The Message: the list barely changes, the note body rises in below the arriving title */
  html.v3-vt-msg::view-transition-old(root){animation:v3-fade-out .16s linear both}
  html.v3-vt-msg::view-transition-new(root){animation:v3-fade-in .16s linear both}
  ::view-transition-group(v3-opens){animation:none}
  ::view-transition-old(v3-opens){animation:v3-fade-out .16s cubic-bezier(.4,0,1,1) both}
  ::view-transition-new(v3-opens){animation:v3-rise-in .4s cubic-bezier(.2,.7,.2,1) .34s both}
  /* the arriving headline is already in place, so it does not rise again */
  html.v3-morph-message .page--message-concept .pub .t .wi{animation:none}
  html.v3-morphing.v3-morph-message .page--message-concept .pub .t{view-transition-name:v3-title}
  html.v3-morphing.v3-morph-signal .page--signal .feature .cover .mh{view-transition-name:v3-title}
  html[data-variant="v3"] .page--signal .feature .cover .mh{align-self:flex-start}
  html[data-variant="v3"] .opens.v3-swapped .ttl .wi{animation:none}

  /* 2b. Headline word rise (V2): words split at build time (src/lib/split-words.ts) rise from a mask at their baseline
     in sequence, the italic serif word last after a beat. Pure CSS, works with JS off. No hairline sweep in V3. */
  html[data-variant="v3"] .v3-h .w{display:inline-block;clip-path:inset(-.3em -.4em -.22em -.4em);
    animation:v3-unclip 0s linear calc((var(--d) + 600) * 1ms) forwards}
  html[data-variant="v3"] .v3-h .wi{display:inline-block;animation:v3-word .58s cubic-bezier(.2,.7,.2,1) calc(var(--d) * 1ms) backwards}

  /* 2c. Page-load fade-ins (768 up, where the page is one screen; phones keep a fast first paint).
     The content under the masthead fades up 10 px (V2's arrival); masthead + colophon hold still. Then the sequences:
     Services cards in reading order (row 1, a beat, row 2), Signal previous issues one after another (item 11),
     Contact "What happens next" one after another (item 14). A title-travel arrival skips the page fade (the
     transition already fades the content). Long pages (article, privacy) fade their opening blocks only: nothing
     below the fold animates unseen (and Chromium's beyond-viewport capture of a once-animated <main> comes out blank). */
  @media (min-width:768px){
    html[data-variant="v3"] .page:not(.page--scroll) > main,
    html[data-variant="v3"] .page--scroll > main > * > :nth-child(-n+2){animation:v3-vt-in .44s cubic-bezier(.2,.7,.2,1) .06s backwards}
    html.v3-vt .page > main{animation:none}
    html[data-variant="v3"] .page--services .card{animation:v3-rise .44s cubic-bezier(.2,.7,.2,1) backwards;
      animation-delay:calc(160ms + var(--n,0) * 45ms)}
    html[data-variant="v3"] .page--signal .prior .pi{animation:v3-rise .52s cubic-bezier(.2,.7,.2,1) backwards;
      animation-delay:calc(300ms + var(--n,0) * 170ms)}
    html[data-variant="v3"] .page--contact .steps li{animation:v3-rise .5s cubic-bezier(.2,.7,.2,1) backwards;
      animation-delay:calc(280ms + var(--n,0) * 140ms)}
  }
  html[data-variant="v3"] .page--services .card:nth-child(2){--n:1}
  html[data-variant="v3"] .page--services .card:nth-child(3){--n:2}
  html[data-variant="v3"] .page--services .card:nth-child(4){--n:3}
  html[data-variant="v3"] .page--services .card:nth-child(5){--n:5}
  html[data-variant="v3"] .page--services .card:nth-child(6){--n:6}
  html[data-variant="v3"] .page--services .card:nth-child(7){--n:7}
  html[data-variant="v3"] .page--services .card:nth-child(8){--n:8}
  html[data-variant="v3"] :is(.page--signal .prior .pi,.page--contact .steps li):nth-child(2){--n:1}
  html[data-variant="v3"] :is(.page--signal .prior .pi,.page--contact .steps li):nth-child(3){--n:2}
  html[data-variant="v3"] :is(.page--signal .prior .pi,.page--contact .steps li):nth-child(4){--n:3}

  /* 2d. Index rows on every page (items 2 + 5). The row's OWN bottom rule darkens by sliding: an ink line draws left to
     right over the light rule (the Services tag-rule effect). No underline under the row title, no extra line.
     Links (Home Practice + Writing, The Message archive): the numeral shifts and inks, the name + details move right
     (George's 8 px indent, as a transform), the arrow travels. Keyboard focus = hover.
     Non-link rows (About method, Contact next steps): the numeral shifts; About bench rows: the rule only. */
  html[data-variant="v3"] :is(.page--home .row,.page--message-concept .note,.page--about .prin li,.page--about .area,.page--contact .steps li){
    position:relative}
  html[data-variant="v3"] :is(.page--home .row,.page--message-concept .note,.page--about .prin li,.page--about .area,.page--contact .steps li)::after{
    content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;background:var(--ink);pointer-events:none;
    transform:scaleX(0);transform-origin:left center;transition:transform .44s cubic-bezier(.2,.7,.2,1)}
  html[data-variant="v3"] :is(.page--home .row,.page--message-concept .note):is(:hover,:focus-visible)::after,
  html[data-variant="v3"] :is(.page--about .prin li,.page--about .area,.page--contact .steps li):hover::after,
  html[data-variant="v3"] .page--message-concept .note[aria-current]::after{transform:scaleX(1)}
  /* the light rule stays light underneath while the ink one draws over it */
  html[data-variant="v3"] :is(.page--home .row,.page--message-concept .note):is(:hover,:focus-visible):not([aria-current]),
  html[data-variant="v3"] :is(.page--about .prin li,.page--about .area,.page--contact .steps li):hover{border-bottom-color:var(--rule-soft)}

  html[data-variant="v3"] .page--home .row:hover,
  html[data-variant="v3"] .page--message-concept .note:hover{padding-left:0}
  html[data-variant="v3"] :is(.page--home .row,.page--message-concept .note) > *{
    transition:transform .34s cubic-bezier(.2,.7,.2,1),color .2s ease}
  html[data-variant="v3"] :is(.page--home .row,.page--message-concept .note):is(:hover,:focus-visible) :is(.num,.d){
    transform:translateX(3px);color:var(--ink)}
  html[data-variant="v3"] :is(.page--home .row,.page--message-concept .note):is(:hover,:focus-visible) :is(.nm,.dsc,.t,.x){
    transform:translateX(8px)}
  html[data-variant="v3"] .page--home .row:is(:hover,:focus-visible) .mark{transform:translateX(5px);color:var(--ink)}
  /* D4-3: with the date stacked above the title (768-959) the whole entry moves together */
  @media (min-width:768px) and (max-width:959px){
    html[data-variant="v3"] .page--message-concept .note:is(:hover,:focus-visible) .d{transform:translateX(8px)}
  }
  html[data-variant="v3"] :is(.page--about .prin .n,.page--contact .steps .n){display:inline-block;
    transition:transform .34s cubic-bezier(.2,.7,.2,1),color .2s ease}
  html[data-variant="v3"] :is(.page--about .prin li,.page--contact .steps li):hover .n{transform:translateX(3px);color:var(--ink)}

  /* 2e. Services cards (item 3): the same hover on all eight. George's 2 px lift, the tag rule draws across in ink,
     the scope middots tick to the accent one by one. */
  html[data-variant="v3"] .page--services .card{
    transition:box-shadow .26s ease,transform .32s cubic-bezier(.2,.7,.2,1),border-color .26s ease}
  html[data-variant="v3"] .page--services .card .cat{position:relative}
  html[data-variant="v3"] .page--services .card .cat::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;
    background:var(--ink);transform:scaleX(0);transform-origin:left center;transition:transform .44s cubic-bezier(.2,.7,.2,1)}
  html[data-variant="v3"] .page--services .card:hover .cat::after{transform:scaleX(1)}
  html[data-variant="v3"] .page--services .card .scope .dot{transition:color .18s ease}
  html[data-variant="v3"] .page--services .card:hover .scope .dot{color:var(--accent);
    transition-delay:calc(140ms + var(--k,0) * 70ms)}

  /* 2f. Text links (item 6 + Send it, item 12): light underline at rest; on hover/focus a darker line slides across it.
     Home + About "Contact ->", Contact "Speaking inquiry ->", Speaking "Contact ->", The Message "Read the full
     article ->", Signal "All issues of Signal", Contact "Send it ->". */
  html[data-variant="v3"] :is(.page--home,.page--about,.page--contact,.page--signal,.page--message-concept) :is(.mail,.more),
  html[data-variant="v3"] .page--contact button[type="submit"]{position:relative}
  html[data-variant="v3"] :is(.page--home,.page--about,.page--contact,.page--signal,.page--message-concept) :is(.mail,.more)::after,
  html[data-variant="v3"] .page--contact button[type="submit"]::after{content:"";position:absolute;left:0;right:0;bottom:-1px;
    height:1px;background:var(--ink);pointer-events:none;transform:scaleX(0);transform-origin:left center;
    transition:transform .44s cubic-bezier(.2,.7,.2,1)}
  html[data-variant="v3"] :is(.page--home,.page--about,.page--contact,.page--signal,.page--message-concept) :is(.mail,.more):is(:hover,:focus-visible),
  html[data-variant="v3"] .page--contact button[type="submit"]:is(:hover,:focus-visible):not(:disabled){border-bottom-color:var(--rule)}
  html[data-variant="v3"] :is(.page--home,.page--about,.page--contact,.page--signal,.page--message-concept) :is(.mail,.more):is(:hover,:focus-visible)::after,
  html[data-variant="v3"] .page--contact button[type="submit"]:is(:hover,:focus-visible):not(:disabled)::after{transform:scaleX(1)}

  @keyframes v3-word{from{transform:translateY(calc(100% + .3em))}}
  @keyframes v3-unclip{to{clip-path:none}}
  /* fade-ins start at 1% opacity, not 0: invisible to the eye, but Chrome then counts the first paint for LCP instead of
     waiting for the fade to end (measured desktop LCP: 0 -> ~590 ms, .01 -> ~85 ms; V1 ~75 ms) */
  @keyframes v3-rise{from{opacity:.01;transform:translateY(8px)}}
  @keyframes v3-vt-out{to{opacity:0;transform:translateY(-6px)}}
  @keyframes v3-vt-in{from{opacity:.01;transform:translateY(10px)}}
  @keyframes v3-fade-out{to{opacity:0}}
  @keyframes v3-fade-in{from{opacity:0}}
  @keyframes v3-rise-in{from{opacity:0;transform:translateY(6px)}}
}
