/* =====================================================================
   Aradhya Pavan H S — Portfolio (v4)
   Indigo / Violet · Light-default · Bootstrap 5 layer + custom skin
   Type: Plus Jakarta Sans (display) · Inter (body) · JetBrains Mono
   Libraries: Bootstrap 5.3, Bootstrap Icons, AOS, Vanilla-Tilt
   ===================================================================== */

/* ---------- Tokens ---------- */
:root{
    --font-display:'Space Grotesk',ui-sans-serif,system-ui,sans-serif;
    --font-heading:'Plus Jakarta Sans',ui-sans-serif,system-ui,sans-serif;
    --font-title:'Fraunces',Georgia,'Times New Roman',serif;
    --font-body:'Inter',ui-sans-serif,system-ui,sans-serif;
    --font-mono:'JetBrains Mono',ui-monospace,Menlo,Consolas,monospace;

    --radius-sm:10px;
    --radius:16px;
    --radius-lg:22px;
    --radius-xl:30px;

    --container:1320px;
    --section-y:clamp(72px,9vw,128px);

    --ease:cubic-bezier(.22,1,.36,1);
    --t-fast:.16s; --t:.28s; --t-slow:.5s;
}

/* LIGHT (default) — clean, professional, blue accent */
:root,[data-theme="light"]{
    --bg:#EFF2F7;
    --bg-2:#E4E8F0;
    --surface:#FFFFFF;
    --surface-2:#F4F5F9;
    --surface-3:#E2E5ED;
    --border:rgba(15,23,42,.09);
    --border-strong:rgba(15,23,42,.15);

    --text:#0F172A;
    --text-soft:#475569;
    --text-mute:#94A3B8;

    --accent:#2563EB;
    --accent-2:#3B82F6;
    --accent-ink:#1D4ED8;
    --on-accent:#FFFFFF;
    --accent-soft:rgba(37,99,235,.09);
    --accent-line:rgba(37,99,235,.22);
    --accent-glow:rgba(37,99,235,.26);
    --grad:linear-gradient(135deg,#2563EB 0%,#3B82F6 55%,#0EA5E9 100%);
    --grad-soft:linear-gradient(135deg,rgba(37,99,235,.10),rgba(14,165,233,.05));

    --nav-bg:rgba(239,242,247,.90);
    --code-bg:rgba(37,99,235,.09);

    --glow-1:rgba(59,130,246,.18);
    --glow-2:rgba(14,165,233,.13);
    --grid-line:rgba(15,23,42,.05);

    --neu-bg:linear-gradient(145deg,#ffffff,#e2e5ed);
    --neu-shadow:6px 6px 14px rgba(15,23,42,.10),-6px -6px 14px rgba(255,255,255,.90);
    --neu-shadow-in:inset 4px 4px 10px rgba(15,23,42,.12),inset -4px -4px 10px rgba(255,255,255,.88);

    color-scheme:light;
}

/* DARK — glossy gradient black + electric blue accent */
[data-theme="dark"]{
    --bg:#070708;
    --bg-2:#0C0D10;
    --surface:#141519;
    --surface-2:#1B1D22;
    --surface-3:#262930;
    --border:rgba(255,255,255,.08);
    --border-strong:rgba(255,255,255,.16);

    --text:#F0F2F6;
    --text-soft:#A6AEBC;
    --text-mute:#6E7785;

    --accent:#3B82F6;
    --accent-2:#60A5FA;
    --accent-ink:#7DB0FF;
    --on-accent:#FFFFFF;
    --accent-soft:rgba(59,130,246,.14);
    --accent-line:rgba(59,130,246,.30);
    --accent-glow:rgba(59,130,246,.50);
    --grad:linear-gradient(135deg,#2563EB 0%,#3B82F6 55%,#60A5FA 100%);
    --grad-soft:linear-gradient(135deg,rgba(59,130,246,.18),rgba(96,165,250,.07));

    --nav-bg:rgba(7,7,8,.76);
    --code-bg:rgba(59,130,246,.16);

    --glow-1:rgba(59,130,246,.30);
    --glow-2:rgba(96,165,250,.18);
    --grid-line:rgba(255,255,255,.05);

    --neu-bg:linear-gradient(145deg,#1b1d22,#0e0f12);
    --neu-shadow:7px 7px 18px rgba(0,0,0,.72),-6px -6px 14px rgba(255,255,255,.03);
    --neu-shadow-in:inset 5px 5px 12px rgba(0,0,0,.74),inset -5px -5px 12px rgba(255,255,255,.025);

    color-scheme:dark;
}

/* ---------- Base ---------- */
*{box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:84px}
body{
    font-family:var(--font-body);
    background:var(--bg);color:var(--text);
    -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
    overflow-x:hidden;position:relative;
    transition:background var(--t-slow) ease,color var(--t-slow) ease;
}
/* light theme: layered gradient canvas with visible depth */
/* Ocean Breeze — clean sky-blue wash (no purple), on-brand for the blue accent */
[data-theme="light"] body{background:
    radial-gradient(ellipse 120% 65% at 50% -12%,rgba(127,205,255,.42),transparent 55%) fixed,
    radial-gradient(ellipse 85% 55% at 100% 6%,rgba(127,205,255,.28),transparent 55%) fixed,
    radial-gradient(ellipse 80% 60% at 0% 100%,rgba(127,205,255,.24),transparent 55%) fixed,
    linear-gradient(160deg,#DFF7FF 0%,#EAF6FF 50%,#DDEEFF 100%) fixed}
/* dark theme: glossy gradient-black canvas with a soft top sheen */
[data-theme="dark"] body{background:
    radial-gradient(130% 90% at 50% -20%,#1a1c22 0%,#0c0d10 38%,#070708 72%) fixed,
    linear-gradient(180deg,#0a0b0d,#060607) fixed}
a{color:inherit;text-decoration:none}
img{max-width:100%}
h1,h2,h3,h4,h5{font-family:var(--font-display);letter-spacing:-.02em;color:var(--text);font-weight:700}
/* Fraunces earns its keep only at display sizes — a WONKed serif at 15px is mush.
   Big type only: the hero name, section titles, the contact title and the one
   oversized skills h3. Card titles (h4/h5) and small h3s stay Space Grotesk. */
h1,h2,.skill-feature h3{font-family:var(--font-title);font-weight:600;letter-spacing:-.022em;font-variation-settings:'opsz' 144,'SOFT' 16,'WONK' 1;font-synthesis-weight:none}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:6px}
::selection{background:color-mix(in srgb,var(--accent) 32%,transparent);color:var(--text);text-shadow:0 0 12px var(--accent-glow)}

.container{max-width:var(--container)}
.text-grad{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent}
.mono{font-family:var(--font-mono)}

/* Scroll progress */
.scroll-progress{position:fixed;top:0;left:0;height:3px;width:0;z-index:2000;background:var(--grad);box-shadow:0 0 12px var(--accent-glow);transition:width .1s linear}

/* ---------- AI detection-frame cursor — 1:1 dot + eased bracket frame (no circles) ---------- */
html.ai-cursor-on,html.ai-cursor-on body,html.ai-cursor-on *{cursor:none !important}
html.ai-cursor-on input,html.ai-cursor-on textarea,html.ai-cursor-on [contenteditable]{cursor:text !important}
.cursor-dot{position:fixed;top:0;left:0;width:6px;height:6px;margin:-3px 0 0 -3px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 1.5px color-mix(in srgb,var(--bg) 85%,transparent),0 0 10px var(--accent-glow);z-index:10000;pointer-events:none;will-change:transform;transition:opacity .2s ease,width .18s var(--ease),height .18s var(--ease),margin .18s var(--ease)}
/* one-shot detection-lock burst from the exact click point (class restarted by JS on press) */
.cursor-dot::after{content:"";position:absolute;inset:-13px;border-radius:9px;border:1.5px solid var(--accent);opacity:0;transform:scale(.3)}
.cursor-dot.ping::after{animation:cursor-ping .5s var(--ease) forwards}
@keyframes cursor-ping{0%{opacity:.6;transform:scale(.3)}100%{opacity:0;transform:scale(1.35)}}
/* the frame: a bounding box the cursor drags across the page — corners do the talking */
.cursor-ring{--corner:color-mix(in srgb,var(--accent) 62%,transparent);position:fixed;top:0;left:0;width:34px;height:34px;margin:-17px 0 0 -17px;border-radius:9px;z-index:9999;pointer-events:none;will-change:transform;transition:opacity .2s ease,width .25s var(--ease),height .25s var(--ease),margin .25s var(--ease),background .25s ease}
.cursor-ring.is-hover{--corner:var(--accent);width:44px;height:44px;margin:-22px 0 0 -22px;background:color-mix(in srgb,var(--accent) 5%,transparent)}
.cursor-ring.is-curio{--corner:var(--accent);width:42px;height:42px;margin:-21px 0 0 -21px}
.cursor-ring.is-down{width:26px;height:26px;margin:-13px 0 0 -13px}
.cursor-dot.is-down{width:10px;height:10px;margin:-5px 0 0 -5px}
.cursor-dot.is-text,.cursor-ring.is-text{opacity:0}
.cursor-ring.is-disabled{--corner:color-mix(in srgb,var(--text-mute) 50%,transparent)}
/* corner brackets = the whole silhouette; box resizes + corners brighten per state */
.ring-corners{position:absolute;inset:0;pointer-events:none}
.ring-corners::before,.ring-corners::after{content:"";position:absolute;width:10px;height:10px;border:2px solid var(--corner);transition:border-color .2s ease}
.ring-corners::before{top:0;left:0;border-right:0;border-bottom:0;border-top-left-radius:7px}
.ring-corners::after{top:0;right:0;border-left:0;border-bottom:0;border-top-right-radius:7px}
.ring-corners.flip{transform:rotate(180deg)}
/* a scan line sweeps the frame while it targets something clickable — linear, no circles */
.cursor-ring::before{content:"";position:absolute;left:14%;right:14%;top:18%;height:1.5px;border-radius:1px;background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:0;transition:opacity .2s ease}
.cursor-ring.is-hover::before{opacity:.55;animation:cursor-scanline 1.5s ease-in-out infinite}
@keyframes cursor-scanline{0%,100%{top:16%}50%{top:80%}}
/* affordance tag on the frame's NE corner — ↗ opens a new tab, @ opens the mail client */
.cursor-ring::after{content:"↗";position:absolute;top:0;right:0;transform:translate(45%,-45%) scale(.6);font-family:var(--font-mono);font-size:.62rem;font-weight:700;line-height:1;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 90%,transparent);border:1px solid var(--accent-line);border-radius:5px;padding:.1rem .22rem;opacity:0;transition:opacity .2s ease,transform .2s var(--ease)}
.cursor-ring.is-mail::after{content:"@"}
.cursor-ring.is-ext::after,.cursor-ring.is-mail::after{opacity:1;transform:translate(45%,-45%) scale(1)}
/* parked ≥3s → the brackets take a slow breath (JS clears it on any activity) */
.cursor-ring.is-idle:not(.is-hover):not(.is-disabled):not(.is-curio) .ring-corners{animation:cursor-breathe 3.2s ease-in-out infinite}
@keyframes cursor-breathe{0%,100%{opacity:.55}50%{opacity:1}}
/* detection tag: the cursor classifies what it reads — latest AI/ML term + confidence */
.ai-cursor-label{position:absolute;left:50%;top:calc(100% + 8px);transform:translate(-50%,-4px);white-space:nowrap;font-family:var(--font-mono);font-size:.6rem;font-weight:600;letter-spacing:.04em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 88%,transparent);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid var(--accent-line);border-radius:6px;padding:.16rem .46rem;opacity:0;transition:opacity .2s ease,transform .2s ease;pointer-events:none;box-shadow:0 8px 18px -10px var(--accent-glow)}
.cursor-ring.is-curio .ai-cursor-label{opacity:1;transform:translate(-50%,0)}
@media (max-width:991.98px){html.ai-cursor-on,html.ai-cursor-on body,html.ai-cursor-on *{cursor:auto !important}.cursor-dot,.cursor-ring{display:none}}
@media (forced-colors:active){html.ai-cursor-on,html.ai-cursor-on body,html.ai-cursor-on *{cursor:auto !important}.cursor-dot,.cursor-ring{display:none}}

/* ---------- Subtle UI sound toggle (synth sounds, persisted) ---------- */
.sound-toggle{position:fixed;left:20px;bottom:20px;z-index:1060;width:42px;height:42px;border-radius:12px;display:grid;place-items:center;background:color-mix(in srgb,var(--surface) 80%,transparent);-webkit-backdrop-filter:blur(12px) saturate(160%);backdrop-filter:blur(12px) saturate(160%);border:1px solid var(--border-strong);color:var(--accent-ink);font-size:.95rem;cursor:pointer;box-shadow:0 8px 22px -12px var(--accent-glow);transition:color var(--t),border-color var(--t),transform var(--t),background var(--t)}
.sound-toggle:hover{border-color:var(--accent);transform:translateY(-2px)}
.sound-toggle .fa-volume-xmark{display:none}
html.sound-off .sound-toggle{color:var(--text-mute)}
html.sound-off .sound-toggle .fa-volume-high{display:none}
html.sound-off .sound-toggle .fa-volume-xmark{display:inline-block}
@media (max-width:575.98px){.sound-toggle{width:38px;height:38px;left:14px;bottom:14px}}

/* ---------- WebGL neural-constellation background (scene3d.js) ---------- */
/* Fixed full-viewport canvas behind ALL content; sits UNDER .bg-amb (earlier in
   DOM, same z-index) so the engineering grid still reads over the 3D depth.
   Hidden until scene3d.js confirms a live WebGL context and paints (.is-live);
   if WebGL is unavailable the canvas stays invisible and .bg-amb is the fallback. */
.webgl-bg{position:fixed;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;opacity:0;transition:opacity 1s ease}
.webgl-bg.is-live{opacity:1}
@media (prefers-reduced-motion:reduce){.webgl-bg{transition:none}}

/* ---------- Ambient background (clean grid + soft glow) ---------- */
.bg-amb{position:fixed;inset:0;pointer-events:none;z-index:0;overflow:hidden}
/* faint engineering grid, fades out via radial mask */
.bg-amb-grid{position:absolute;inset:0;background-image:linear-gradient(var(--grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);background-size:54px 54px;-webkit-mask-image:radial-gradient(ellipse 95% 75% at 50% 22%,#000 30%,transparent 88%);mask-image:radial-gradient(ellipse 95% 75% at 50% 22%,#000 30%,transparent 88%);opacity:.55}
/* top accent glow that grounds the hero */
.bg-amb-glow{position:absolute;inset:0;background:radial-gradient(70% 46% at 50% -10%,color-mix(in srgb,var(--accent) 10%,transparent),transparent 62%),radial-gradient(48% 38% at 86% 4%,color-mix(in srgb,var(--accent-2) 14%,transparent),transparent 70%),radial-gradient(40% 30% at 10% 60%,color-mix(in srgb,var(--accent) 6%,transparent),transparent 55%)}
[data-theme="dark"] .bg-amb-glow{background:radial-gradient(72% 50% at 50% -12%,color-mix(in srgb,var(--accent) 22%,transparent),transparent 60%),radial-gradient(50% 40% at 86% 2%,color-mix(in srgb,var(--accent-2) 16%,transparent),transparent 68%)}

main,.nav-shell,.footer{position:relative;z-index:1}

/* ---------- Buttons ---------- */
.btn{
    --bs-btn-padding-y:0;--bs-btn-padding-x:0;
    display:inline-flex;align-items:center;justify-content:center;gap:.55em;
    padding:.82em 1.4em;border-radius:12px;border:1px solid transparent;
    font-family:var(--font-display);font-weight:600;font-size:.95rem;line-height:1;
    cursor:pointer;white-space:nowrap;
    transition:transform var(--t) var(--ease),box-shadow var(--t) var(--ease),background var(--t),color var(--t),border-color var(--t);
}
.btn-sm{padding:.58em 1.05em;font-size:.85rem;border-radius:10px}
.btn-xs{padding:.5em .8em;font-size:.76rem;gap:.4em;border-radius:9px}
.btn-lg{padding:1em 1.7em;font-size:1rem}
.btn-primary{background:var(--grad);color:#fff;box-shadow:0 10px 26px -10px var(--accent-glow)}
.btn-primary:hover{color:#fff;transform:translateY(-3px);box-shadow:0 16px 34px -10px var(--accent-glow)}
.btn-primary:active{transform:translateY(-1px)}
.btn-ghost{background:var(--surface);color:var(--text);border-color:var(--border-strong)}
.btn-ghost:hover{border-color:var(--accent);color:var(--accent-ink);transform:translateY(-3px)}

/* ---------- Navbar ---------- */
.nav-shell{position:fixed;top:0;left:0;right:0;z-index:1050;padding:14px 0;transition:padding var(--t),background var(--t),box-shadow var(--t),border-color var(--t);border-bottom:1px solid transparent}
.nav-shell.scrolled{background:var(--nav-bg);border-bottom-color:var(--border);box-shadow:0 4px 24px rgba(15,23,42,.08);padding:8px 0}
/* blur lives on a pseudo, NOT the shell — backdrop-filter on an ancestor makes it the containing
   block for position:fixed descendants, squashing the mobile drawer + overlay into the header box */
.nav-shell::before{content:"";position:absolute;inset:0;z-index:-1;backdrop-filter:saturate(160%) blur(18px);-webkit-backdrop-filter:saturate(160%) blur(18px);opacity:0;transition:opacity var(--t)}
.nav-shell.scrolled::before{opacity:1}
/* Bootstrap's own .nav class zeroes padding-left; restore the container gutter */
.nav{display:flex;align-items:center;justify-content:space-between;gap:16px;padding-left:calc(var(--bs-gutter-x,1.5rem)*.5)}

/* Brand — premium typography */
.nav-brand{display:inline-flex;align-items:center;gap:10px;text-decoration:none}
.brand-mark{position:relative;width:36px;height:36px;display:flex;align-items:center;justify-content:center;border-radius:11px;background:var(--grad);color:#fff;font-weight:700;font-size:.78rem;letter-spacing:.02em;box-shadow:0 6px 18px -4px var(--accent-glow);isolation:isolate;overflow:hidden;gap:0;transition:transform var(--t) var(--ease),box-shadow var(--t)}
.nav-brand:hover .brand-mark{transform:translateY(-2px) scale(1.05);box-shadow:0 10px 24px -4px var(--accent-glow)}
.brand-mark span{position:relative;z-index:1;line-height:1}
.brand-mark::before{content:"";position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,.22) 0%,transparent 50%);pointer-events:none}
.brand-mark::after{content:"";position:absolute;inset:-3px;border-radius:14px;background:var(--grad);opacity:0;filter:blur(10px);z-index:-1;animation:brandBreath 4.5s ease-in-out infinite}
@keyframes brandBreath{0%,100%{opacity:0}50%{opacity:.45}}
.brand-text{font-family:var(--font-display);font-size:1.12rem;font-weight:700;letter-spacing:-.025em;color:var(--text);transition:color var(--t)}
.brand-accent{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent;font-weight:800}
.brand-dot{color:var(--accent);font-weight:800}

/* Nav links container — desktop: floating glass pill bar */
.nav-links{display:flex;align-items:center;gap:2px;list-style:none;margin:0;padding:5px 7px;background:color-mix(in srgb,var(--surface) 84%,transparent);backdrop-filter:blur(12px) saturate(160%);-webkit-backdrop-filter:blur(12px) saturate(160%);border:1px solid var(--border);border-radius:999px;box-shadow:0 8px 24px -10px rgba(15,23,42,.16)}
.nav-shell.scrolled .nav-links{background:color-mix(in srgb,var(--surface) 62%,transparent)}
.nav-link{position:relative;display:inline-flex;align-items:center;gap:7px;padding:.48rem .82rem;border-radius:999px;font-size:.86rem;font-weight:500;color:var(--text-soft);transition:color var(--t),background var(--t)}
.nav-link i{display:inline-flex;align-items:center;font-size:.98rem;color:var(--text-mute);transition:color var(--t),transform var(--t),filter var(--t)}
.nav-link:hover{color:var(--text);background:var(--surface-2)}
.nav-link:hover i{color:var(--accent-ink);transform:translateY(-1px)}
.nav-link.active{color:var(--accent-ink);background:var(--accent-soft);font-weight:600}
.nav-link.active i{color:var(--accent)}

/* ---------- Navbar · console upgrade — bold filled icons + sliding ink (desktop) ---------- */
@media (min-width:992px){
    .nav-links{position:relative;box-shadow:0 8px 24px -10px rgba(15,23,42,.16),inset 0 1px 0 color-mix(in srgb,#fff 45%,transparent)}
    [data-theme="dark"] .nav-links{box-shadow:0 8px 24px -10px rgba(0,0,0,.45),inset 0 1px 0 color-mix(in srgb,#fff 7%,transparent)}
    .nav-link i{font-size:.92rem;transition:color var(--t),transform .3s var(--ease)}
    .nav-link:hover i{transform:translateY(-1px) scale(1.14)}
    .nav-link.active i{color:var(--accent);filter:drop-shadow(0 0 6px var(--accent-glow))}
    .nav-link:hover,.nav-link.active{background:transparent}
    /* one pill that GLIDES between links (JS positions it); links paint above it */
    .nav-ink{position:absolute;top:5px;bottom:5px;left:0;width:0;border-radius:999px;background:var(--accent-soft);border:1px solid var(--accent-line);opacity:0;transition:transform .38s var(--ease),width .38s var(--ease),opacity .18s ease;pointer-events:none}
    .nav-ink.on{opacity:1}
}
@media (max-width:991.98px){.nav-ink{display:none}}
.nav-link:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
/* the CTA carries a live status pulse — "open to talk" */
.nav-cta::before{content:"";width:6px;height:6px;border-radius:50%;background:#fff;flex-shrink:0;box-shadow:0 0 0 0 rgba(255,255,255,.55);animation:ctaPulse 2.4s ease-out infinite}
@keyframes ctaPulse{0%{box-shadow:0 0 0 0 rgba(255,255,255,.55)}70%{box-shadow:0 0 0 6px rgba(255,255,255,0)}100%{box-shadow:0 0 0 0 rgba(255,255,255,0)}}
/* the brand dot blinks like a terminal caret on hover */
.nav-brand:hover .brand-dot{animation:caretBlink 1.1s steps(1) infinite}
@keyframes caretBlink{50%{opacity:0}}
@media (prefers-reduced-motion:reduce){.nav-cta::before{animation:none}.nav-brand:hover .brand-dot{animation:none}.nav-ink{transition:opacity .18s ease}}

/* Drawer head — hidden on desktop */
.nav-drawer-head{display:none}
.nav-drawer-cta{display:none}

/* Nav overlay — hidden on desktop */
.nav-overlay{display:none}

/* Nav actions */
.nav-actions{display:flex;align-items:center;gap:.5rem}
.theme-toggle{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;background:var(--surface);border:1px solid var(--border);color:var(--text-soft);font-size:.95rem;cursor:pointer;transition:transform var(--t),color var(--t),border-color var(--t),background var(--t)}
.theme-toggle:hover{color:var(--accent-ink);border-color:var(--accent-line);background:var(--accent-soft);transform:rotate(-12deg)}
.theme-toggle .bi-sun{display:none}
[data-theme="dark"] .theme-toggle .bi-sun{display:inline-block}
[data-theme="dark"] .theme-toggle .bi-moon-stars{display:none}

/* Hamburger toggle — hidden on desktop */
.nav-toggle{display:none;width:40px;height:40px;border-radius:11px;border:1px solid var(--border);background:var(--surface);color:var(--text);cursor:pointer;place-items:center;position:relative}
.hamburger{width:20px;height:14px;display:flex;flex-direction:column;justify-content:space-between;position:relative}
.hamburger span{display:block;width:100%;height:2px;border-radius:2px;background:var(--text);transition:transform .3s var(--ease),opacity .2s,width .3s var(--ease)}
.hamburger span:nth-child(2){width:14px;margin-left:auto}
/* Hamburger → X morph */
.nav-toggle.active .hamburger span:nth-child(1){transform:translateY(6px) rotate(45deg)}
.nav-toggle.active .hamburger span:nth-child(2){opacity:0;width:0}
.nav-toggle.active .hamburger span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}

/* ===== Compact desktop (992–1199px): icon-only nav so the pill never crowds the CTA ===== */
@media (min-width:992px) and (max-width:1199.98px){
    .nav-link{padding:.5rem .62rem}
    .nav-link span{display:none}
    .nav-link i{font-size:1.08rem}
}

/* ===== Mobile (≤991px) ===== */
@media (max-width:991.98px){
    /* Show hamburger */
    .nav-toggle{display:grid}
    /* Hide desktop CTA */
    .nav-cta{display:none}

    /* Overlay — full-screen scrim (works now that the shell no longer traps fixed descendants) */
    .nav-overlay{display:block;position:fixed;inset:0;z-index:1040;background:rgba(8,12,24,.5);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);opacity:0;pointer-events:none;transition:opacity .35s var(--ease)}
    .nav-overlay.open{opacity:1;pointer-events:auto}

    /* Drawer — full-height glass panel sliding from the right */
    .nav-links{position:fixed;top:0;right:0;bottom:0;z-index:1060;width:min(330px,86vw);flex-direction:column;align-items:stretch;padding:0 14px calc(14px + env(safe-area-inset-bottom,0px));margin:0;gap:4px;border:none;border-left:1px solid var(--border);border-radius:22px 0 0 22px;background:color-mix(in srgb,var(--surface) 92%,transparent);backdrop-filter:blur(24px) saturate(160%);-webkit-backdrop-filter:blur(24px) saturate(160%);box-shadow:-18px 0 60px rgba(0,0,0,.28);transform:translateX(105%);transition:transform .42s cubic-bezier(.22,.9,.3,1);overflow-y:auto;-webkit-overflow-scrolling:touch}
    .nav-links.open{transform:translateX(0)}

    /* Drawer header */
    .nav-drawer-head{display:flex !important;align-items:center;justify-content:space-between;margin:0 -14px 10px;padding:18px 20px 16px;border-bottom:1px solid var(--border);flex-shrink:0}
    .drawer-brand{font-family:var(--font-display);font-size:1.1rem;font-weight:700;letter-spacing:-.02em;color:var(--text)}
    .drawer-brand .brand-accent{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
    .drawer-brand .brand-dot{color:var(--accent);font-weight:800}
    .nav-close{width:38px;height:38px;border-radius:12px;display:grid;place-items:center;border:1px solid var(--border);background:var(--surface-2);color:var(--text-soft);font-size:.95rem;cursor:pointer;transition:color var(--t),background var(--t),border-color var(--t)}
    .nav-close:hover{color:var(--accent-ink);border-color:var(--accent);background:var(--accent-soft)}

    /* Nav links — roomy rounded rows with icon chips, staggered entrance */
    .nav-links li{list-style:none}
    .nav-link{display:flex;align-items:center;gap:13px;padding:10px 12px;border-radius:14px;font-size:1.02rem;font-weight:500;color:var(--text-soft);border-bottom:none;opacity:0;transform:translateX(18px);transition:opacity .35s var(--ease),transform .4s var(--ease),color var(--t),background var(--t)}
    .nav-links.open .nav-link{opacity:1;transform:none}
    .nav-links.open li:nth-child(3) .nav-link{transition-delay:.04s}
    .nav-links.open li:nth-child(4) .nav-link{transition-delay:.08s}
    .nav-links.open li:nth-child(5) .nav-link{transition-delay:.12s}
    .nav-links.open li:nth-child(6) .nav-link{transition-delay:.16s}
    .nav-links.open li:nth-child(7) .nav-link{transition-delay:.2s}
    .nav-links.open li:nth-child(8) .nav-link{transition-delay:.24s}
    .nav-link i{display:inline-flex !important;width:38px;height:38px;border-radius:11px;align-items:center;justify-content:center;font-size:1.02rem;color:var(--text-mute);background:var(--surface-2);border:1px solid var(--border);transition:color var(--t),background var(--t),border-color var(--t);flex-shrink:0}
    .nav-link:hover,.nav-link:focus-visible{color:var(--text);background:var(--surface-2)}
    .nav-link:hover i{color:var(--accent-ink);border-color:var(--accent)}
    .nav-link.active{color:var(--accent-ink);background:var(--accent-soft);font-weight:600}
    .nav-link.active i{color:var(--on-accent);background:var(--accent);border-color:transparent}
    @media (prefers-reduced-motion:reduce){.nav-link{opacity:1;transform:none;transition:none}.nav-links{transition:none}.nav-links.open li .nav-link{transition-delay:0s}}

    /* Drawer CTA */
    .nav-drawer-cta{display:block !important;padding:16px 2px 2px;margin-top:auto;border-top:1px solid var(--border)}
    .nav-cta-mobile{width:100%;text-align:center;justify-content:center}

    /* keep the WALL-E launcher from floating over the open drawer / its CTA */
    .nav-open .chat-launcher{opacity:0;transform:translateY(10px) scale(.9);pointer-events:none;transition:opacity .25s var(--ease),transform .25s var(--ease)}
}

/* ===== Small mobile (≤575px) ===== */
@media (max-width:575.98px){
    .nav-shell{padding:10px 0}
    .nav-shell.scrolled{padding:6px 0}
    .brand-text{font-size:.98rem}
    .brand-mark{width:32px;height:32px;font-size:.72rem}
}


/* ---------- HERO ---------- */
.hero{position:relative;overflow:hidden;padding-top:clamp(132px,16vh,196px);padding-bottom:0}
.hero-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;pointer-events:none;opacity:.85}
.hero-inner{position:relative;z-index:2;display:grid;grid-template-columns:1.04fr 1fr;gap:clamp(28px,4vw,52px);align-items:center}
/* person-first solo hero — the name is the centerpiece */
.hero-solo{display:block;max-width:920px}
.hero-marquee{position:relative;z-index:2}

/* terminal chip */
.hero-terminal{position:relative;display:inline-flex;align-items:center;gap:.7rem;font-family:var(--font-mono);font-size:.82rem;background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:.5rem .85rem;margin-bottom:24px;box-shadow:0 10px 26px -14px rgba(15,23,42,.4);overflow:hidden;isolation:isolate;transition:border-color .25s var(--ease),box-shadow .25s var(--ease),transform .25s var(--ease)}
.hero-terminal::before{content:"";position:absolute;right:12px;top:9px;width:62px;height:15px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--accent) 34%,transparent) 0 1px,transparent 1px 8px),linear-gradient(90deg,transparent,color-mix(in srgb,var(--accent) 16%,transparent));opacity:0;transform:translateY(-4px);transition:opacity .25s var(--ease),transform .25s var(--ease);pointer-events:none}
.hero-terminal::after{content:"";position:absolute;left:0;bottom:0;width:0;height:2px;background:linear-gradient(90deg,var(--accent),var(--accent-2));box-shadow:0 0 12px var(--accent-glow);transition:width .45s var(--ease);pointer-events:none}
.hero-terminal:hover{border-color:var(--accent-line);box-shadow:0 16px 34px -20px var(--accent-glow),0 10px 26px -14px rgba(15,23,42,.4);transform:translateY(-1px)}
.hero-terminal:hover::before{opacity:.72;transform:none}
.hero-terminal:hover::after{width:100%}
.tt-dots{display:inline-flex;gap:5px}
.tt-dots i{width:9px;height:9px;border-radius:50%;display:block}
.tt-dots i:nth-child(1){background:#ff5f57}.tt-dots i:nth-child(2){background:#febc2e}.tt-dots i:nth-child(3){background:#28c840}
.hero-terminal code{color:var(--text-soft)}
.hero-terminal .term-prompt{color:var(--accent-ink)}
.hero-terminal .term-sep,.hero-terminal .term-path{color:var(--text-mute)}
.hero-terminal .term-path{transition:color .25s var(--ease)}
.hero-terminal:hover .term-path{color:var(--accent-ink)}

.hero-eyebrow{font-family:var(--font-mono);font-size:.82rem;margin-bottom:24px;display:flex;flex-direction:column;gap:5px}
.term-prompt{color:var(--accent-ink)}.term-cmd{color:var(--text-soft)}.term-out{color:var(--text-mute);letter-spacing:.03em}
/* big name headline (person-first) */
.hero-hello{font-family:var(--font-mono);font-size:clamp(1rem,1.6vw,1.25rem);font-weight:500;color:var(--text-soft);margin:0 0 .25rem;letter-spacing:.01em}
.hero-name{font-family:var(--font-title);font-weight:700;font-size:clamp(3.2rem,10vw,7.2rem);line-height:.96;letter-spacing:-.04em;margin:0 0 18px}
/* circular portrait set beside the name — orbiting ring + springy pop on hover */
.hn-pic{position:relative;display:inline-grid;place-items:center;width:.62em;height:.62em;min-width:44px;min-height:44px;margin-right:.22em;vertical-align:-.08em}
.hn-pic img{width:84%;height:84%;border-radius:50%;object-fit:cover;object-position:center 12%;background:linear-gradient(160deg,color-mix(in srgb,var(--accent) 18%,var(--surface)),color-mix(in srgb,var(--accent-2) 7%,var(--surface)));box-shadow:0 .12em .34em -.1em var(--accent-glow);position:relative;z-index:2;transition:transform .5s cubic-bezier(.34,1.56,.64,1)}
.hn-orbit{position:absolute;inset:0;border-radius:50%;padding:.045em;background:conic-gradient(from 0deg,var(--accent),var(--accent-2) 35%,transparent 60%,transparent 78%,var(--accent));-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;animation:spinRing 5s linear infinite;z-index:1}
.hn-pic::after{content:"";position:absolute;right:3%;bottom:3%;width:.15em;height:.15em;min-width:11px;min-height:11px;border-radius:50%;background:#22c55e;border:2px solid var(--bg);box-shadow:0 0 .3em rgba(34,197,94,.85);z-index:3;animation:hnDot 2.2s ease-in-out infinite}
.hero-name:hover .hn-pic img{transform:scale(1.14) rotate(-5deg)}
.hero-name:hover .hn-orbit{animation-duration:1.2s}
@keyframes spinRing{to{transform:rotate(360deg)}}
@keyframes hnDot{50%{box-shadow:0 0 .5em rgba(34,197,94,1);transform:scale(1.12)}}
/* hand-drawn stroke under the surname, draws itself in (and redraws on hover) */
/* ⚠ `.hn-line2` (index.html) wraps "Pavan" + `.hero-dot`, and the ≤575.98px
   two-line break lives on IT, not on `.hn-word`. `display:block` on `.hn-word`
   left the accent "." stranded on a THIRD line, one line-height below the name —
   a lone blue dot in the margin. Folding the dot into `.hn-word` instead is the
   other trap: `.hn-scribble` is `width:106%` of `.hn-word`, so the signature
   stroke would stretch out under the period on EVERY breakpoint. */
.hn-word{position:relative;display:inline-block}
.hn-wd{white-space:nowrap}
.hn-scribble{position:absolute;left:-3%;bottom:-.12em;width:106%;height:.22em;overflow:visible;pointer-events:none}
.hn-scribble path{stroke-dasharray:100;stroke-dashoffset:100;animation:hnScribIn .9s .8s var(--ease) forwards}
.hero-name:hover .hn-scribble path{animation:hnScribRe .7s var(--ease) forwards}
@keyframes hnScribIn{to{stroke-dashoffset:0}}
@keyframes hnScribRe{from{stroke-dashoffset:100}to{stroke-dashoffset:0}}
@media (prefers-reduced-motion:reduce){.hn-orbit,.hn-pic::after{animation:none}.hn-pic img{transition:none}.hn-scribble path,.hero-name:hover .hn-scribble path{animation:none;stroke-dashoffset:0}}
.hero-name .text-grad{font-weight:700}
.hero-dot{color:var(--accent)}
.hero-tagline{font-family:var(--font-display);font-weight:600;font-size:clamp(1.3rem,2.8vw,2.1rem);line-height:1.2;letter-spacing:-.02em;color:var(--text);margin:0 0 22px}
.hero-tagline .hl{background:var(--grad);color:#fff;padding:0 .18em;border-radius:8px;-webkit-box-decoration-break:clone;box-decoration-break:clone;display:inline-block;transform:rotate(-1.4deg);box-shadow:0 10px 26px -8px var(--accent-glow);min-width:.5em}
.hero-tagline #typeword{display:inline-block}
.type-caret{display:inline-block;width:.5ch;height:.82em;background:rgba(255,255,255,.92);margin-left:.04em;border-radius:2px;vertical-align:-.02em;animation:caret 1s steps(1) infinite}
@keyframes caret{50%{opacity:0}}
.hero-lead{font-size:clamp(1.02rem,1.2vw,1.18rem);line-height:1.7;color:var(--text-soft);max-width:60ch;margin-bottom:30px}
.hero-lead strong{color:var(--text);font-weight:600}
.lead-key{color:var(--accent-ink);font-weight:600}
.hero-ctas{display:flex;gap:.7rem;flex-wrap:wrap;margin-bottom:22px}
.hero-status{display:inline-flex;align-items:center;gap:.6rem;font-family:var(--font-mono);font-size:.82rem;color:var(--text-mute)}
/* stats as a left-aligned row under the hero copy */
.hero-stats-row{display:inline-grid;max-width:540px;margin-top:30px}
.status-dot{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 0 var(--accent-glow);animation:pulse 2s ease-in-out infinite;flex-shrink:0}
@keyframes pulse{0%,100%{box-shadow:0 0 0 0 var(--accent-glow);opacity:1}50%{box-shadow:0 0 0 7px transparent;opacity:.65}}

.hero-aside{display:flex;flex-direction:column;gap:24px}

/* ---- Portrait stage: free-floating cutout on a luminous, animated stage ---- */
.portrait-stage{position:relative;width:100%;max-width:430px;margin:6px auto 30px;aspect-ratio:1/1.12;display:flex;align-items:flex-end;justify-content:center;transform-style:preserve-3d}

/* rotating conic energy halo */
.stage-halo{position:absolute;z-index:0;left:50%;top:45%;width:120%;aspect-ratio:1;transform:translate(-50%,-50%);border-radius:50%;background:conic-gradient(from 0deg,transparent 0deg,var(--accent) 60deg,transparent 140deg,var(--accent-2) 215deg,transparent 300deg,var(--accent) 360deg);filter:blur(32px);opacity:.42;will-change:transform;animation:spinHalo 16s linear infinite}

/* concentric orbiting rings */
.stage-ring{position:absolute;z-index:0;left:50%;top:45%;width:97%;aspect-ratio:1;transform:translate(-50%,-50%);border-radius:50%;border:1.5px dashed var(--accent-line);-webkit-mask-image:linear-gradient(#000 28%,transparent 82%);mask-image:linear-gradient(#000 28%,transparent 82%);opacity:.75;will-change:transform;animation:spinHalo 38s linear infinite}
.stage-ring-2{width:78%;border-style:dotted;border-color:var(--border-strong);opacity:.55;animation:spinHalo 26s linear infinite reverse}

/* glowing glass platform the figure stands on */
.stage-disc{position:absolute;z-index:0;left:50%;bottom:1%;width:82%;height:26%;transform:translateX(-50%);border-radius:50%;background:radial-gradient(ellipse at 50% 38%,var(--accent-soft),transparent 72%);box-shadow:0 36px 64px -26px var(--accent-glow)}

/* the person — transparent cutout, real drop shadow, no box */
.portrait-figure{position:relative;z-index:2;width:100%;height:100%;display:flex;align-items:flex-end;justify-content:center}
.portrait-figure img{width:auto;height:100%;max-width:100%;object-fit:contain;filter:drop-shadow(0 30px 42px rgba(15,23,42,.32));will-change:transform;animation:floatPortrait 7s ease-in-out infinite}

/* floating glass skill chips */
.float-chip{position:absolute;z-index:5;display:inline-flex;align-items:center;gap:.42rem;padding:.45rem .78rem;border-radius:999px;font-family:var(--font-mono);font-size:.74rem;font-weight:600;color:var(--text);background:var(--surface);background:color-mix(in srgb,var(--surface) 80%,transparent);backdrop-filter:blur(12px) saturate(170%);-webkit-backdrop-filter:blur(12px) saturate(170%);border:1px solid var(--border-strong);box-shadow:0 16px 32px -16px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.4)}
.float-chip img{width:16px;height:16px;object-fit:contain}
.float-chip i{color:var(--accent-ink)}
.fc-py{top:8%;left:-4%;animation:floatY 5s ease-in-out infinite}
.fc-llm{top:27%;right:-7%;animation:floatY 6s ease-in-out infinite .5s}
.fc-rag{bottom:30%;left:-7%;animation:floatY 5.5s ease-in-out infinite .9s}
.fc-agent{bottom:15%;right:-3%;animation:floatY 6.5s ease-in-out infinite .3s}
@keyframes floatY{0%,100%{transform:translateY(0)}50%{transform:translateY(-11px)}}
@keyframes floatPortrait{0%,100%{transform:translateY(0)}50%{transform:translateY(-12px)}}
@keyframes spinHalo{from{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}

/* availability badge (glass) */
.portrait-badge{position:absolute;z-index:6;top:5%;right:1%;display:inline-flex;align-items:center;gap:.42rem;padding:.42rem .8rem;border-radius:999px;font-family:var(--font-mono);font-size:.72rem;font-weight:600;color:var(--text);background:var(--surface);background:color-mix(in srgb,var(--surface) 82%,transparent);backdrop-filter:blur(12px) saturate(170%);-webkit-backdrop-filter:blur(12px) saturate(170%);border:1px solid var(--border-strong);box-shadow:0 14px 30px -14px var(--accent-glow)}

/* floating glass nameplate */
.portrait-plate{position:absolute;z-index:6;left:50%;bottom:2%;transform:translateX(-50%);min-width:78%;display:flex;flex-direction:column;align-items:center;gap:2px;text-align:center;padding:.7rem 1.1rem;border-radius:16px;background:var(--surface);background:color-mix(in srgb,var(--surface) 84%,transparent);backdrop-filter:blur(16px) saturate(170%);-webkit-backdrop-filter:blur(16px) saturate(170%);border:1px solid var(--border-strong);box-shadow:0 22px 44px -22px var(--accent-glow)}
.portrait-name{font-family:var(--font-display);font-weight:700;font-size:1.02rem;color:var(--text);line-height:1.1}
.portrait-loc{font-size:.72rem;color:var(--text-mute);font-family:var(--font-mono);display:inline-flex;align-items:center;gap:.35rem}
.portrait-loc i{color:var(--accent-ink)}
.hero-stats{display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;background:var(--surface)}
.hero-stats div{padding:15px 13px;display:flex;flex-direction:column;gap:3px;border-left:1px solid var(--border)}
.hero-stats div:first-child{border-left:0}
.hero-stats dt{font-family:var(--font-display);font-weight:800;font-size:1.5rem;color:var(--accent-ink);line-height:1;margin:0}
.hero-stats dt span{font-size:.62em;vertical-align:super}
.hero-stats dd{font-size:.72rem;color:var(--text-mute);margin:0;line-height:1.3}

/* ---------- Hero AI workbench (train → fine-tune → deploy) ---------- */
.ai-workbench{position:relative;width:100%;max-width:440px;margin:0 auto;display:flex;flex-direction:column;gap:15px;padding:4px;transform-style:preserve-3d}
.wb-availability{align-self:flex-end;display:inline-flex;align-items:center;gap:.5rem;font-family:var(--font-mono);font-size:.72rem;font-weight:600;color:var(--text);background:color-mix(in srgb,var(--surface) 82%,transparent);backdrop-filter:blur(12px) saturate(160%);-webkit-backdrop-filter:blur(12px) saturate(160%);border:1px solid var(--border-strong);border-radius:999px;padding:.4rem .8rem;box-shadow:0 14px 30px -16px var(--accent-glow)}
.wb-card{position:relative;background:color-mix(in srgb,var(--surface) 90%,transparent);backdrop-filter:blur(16px) saturate(160%);-webkit-backdrop-filter:blur(16px) saturate(160%);border:1px solid var(--border-strong);border-radius:18px;box-shadow:0 26px 52px -30px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.4);overflow:hidden;transition:transform var(--t) var(--ease),box-shadow var(--t)}
.wb-card:hover{transform:translateY(-6px);box-shadow:0 38px 64px -28px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.4)}

/* terminal card */
.wb-terminal{font-family:var(--font-mono)}
.wb-bar{display:flex;align-items:center;gap:.6rem;padding:.55rem .85rem;border-bottom:1px solid var(--border);background:var(--surface-2)}
.wb-bar-title{font-size:.74rem;color:var(--text-soft);font-weight:600}
.wb-tag{margin-left:auto;font-size:.62rem;font-weight:700;letter-spacing:.04em;color:var(--accent-ink);background:var(--accent-soft);border:1px solid var(--accent-line);border-radius:6px;padding:.12rem .42rem}
.wb-term-body{padding:.8rem .95rem;font-size:.75rem;line-height:1.75;color:var(--text-soft)}
.wb-term-body p{margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.wb-term-body .t-p{color:var(--accent-ink);font-weight:700}
.wb-term-body .t-flag{color:var(--accent-2)}
.wb-term-body .t-ok i{color:#22c55e;font-size:.85em}
.wb-term-body .t-run{display:flex;align-items:center;gap:.5rem;color:var(--text);white-space:normal}
.t-spin{width:11px;height:11px;border-radius:50%;border:2px solid var(--accent-line);border-top-color:var(--accent);display:inline-block;flex-shrink:0;animation:wbSpin .8s linear infinite}
@keyframes wbSpin{to{transform:rotate(360deg)}}
.wb-progress{flex:1;min-width:54px;height:5px;border-radius:3px;background:var(--surface-3);overflow:hidden}
.wb-progress-fill{display:block;height:100%;width:30%;border-radius:3px;background:var(--grad);animation:wbProg 3.4s cubic-bezier(.5,.1,.3,1) infinite}
@keyframes wbProg{0%{width:6%}70%{width:94%}100%{width:6%}}

/* metrics card */
.wb-metrics{padding:.9rem 1rem}
.wb-card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:.55rem}
.wb-card-eyebrow{font-family:var(--font-mono);font-size:.72rem;font-weight:600;color:var(--text-soft);display:inline-flex;align-items:center;gap:.4rem}
.wb-card-eyebrow i{color:var(--accent-ink)}
.wb-live{display:inline-flex;align-items:center;gap:.35rem;font-family:var(--font-mono);font-size:.64rem;font-weight:700;color:#22c55e;text-transform:uppercase;letter-spacing:.06em}
.wb-live span{width:7px;height:7px;border-radius:50%;background:#22c55e;animation:pulse 2s ease-in-out infinite}
.wb-metric-row{display:flex;align-items:center;gap:.9rem;padding:.35rem 0}
.wb-metric-row + .wb-metric-row{border-top:1px dashed var(--border)}
.wb-metric{display:flex;flex-direction:column;gap:2px;min-width:84px}
.wb-metric-label{font-size:.7rem;color:var(--text-mute);font-family:var(--font-mono)}
.wb-metric-val{font-family:var(--font-display);font-weight:800;font-size:1.32rem;color:var(--accent-ink);line-height:1;font-variant-numeric:tabular-nums}
.wb-metric-val.down{color:var(--text)}
.wb-spark{flex:1;height:34px;min-width:0}
.spark-path{stroke-dasharray:240;stroke-dashoffset:240;animation:sparkDraw 1.7s ease-out forwards;animation-delay:.8s}
@keyframes sparkDraw{to{stroke-dashoffset:0}}

/* deploy card */
.wb-deploy{display:flex;align-items:center;gap:.8rem;padding:.82rem 1rem}
.wb-deploy-ic{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;background:var(--grad);color:#fff;font-size:1.02rem;box-shadow:0 8px 18px -6px var(--accent-glow);flex-shrink:0}
.wb-deploy-body{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
.wb-deploy-top{display:flex;align-items:center;gap:.5rem;font-size:.9rem;color:var(--text)}
.wb-deploy-top b{font-family:var(--font-display);font-weight:700}
.wb-pill{font-family:var(--font-mono);font-size:.62rem;font-weight:700;color:var(--accent-ink);background:var(--accent-soft);border:1px solid var(--accent-line);border-radius:6px;padding:.1rem .4rem}
.wb-deploy-sub{font-size:.73rem;color:var(--text-mute);font-family:var(--font-mono)}
.wb-deploy-check{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;background:rgba(34,197,94,.16);color:#22c55e;font-size:.95rem;flex-shrink:0;animation:wbCheck .5s var(--ease) backwards;animation-delay:1.1s}
@keyframes wbCheck{from{transform:scale(0)}60%{transform:scale(1.25)}to{transform:scale(1)}}

/* ---------- Workflow section (How I ship) ---------- */
.workflow-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;align-items:stretch}
.workflow-grid .wb-card{display:flex;flex-direction:column;padding-top:0}
.wb-step{display:block;font-family:var(--font-mono);font-size:.68rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--accent-ink);padding:.7rem 1rem .15rem;background:color-mix(in srgb,var(--accent-soft) 60%,transparent)}
.wb-terminal .wb-step{border-bottom:1px solid var(--border)}
.workflow-grid .wb-metrics,.workflow-grid .wb-deploy{padding:0}
.workflow-grid .wb-metrics .wb-card-head{padding:.55rem 1rem 0}
.workflow-grid .wb-metrics .wb-metric-row{padding:.45rem 1rem}
.workflow-grid .wb-metrics .wb-metric-row + .wb-metric-row{border-top:1px dashed var(--border)}
.wb-deploy-row{display:flex;align-items:center;gap:.8rem;padding:.9rem 1rem}
.wb-deploy-chips{display:flex;flex-wrap:wrap;gap:.5rem;padding:0 1rem 1rem;margin-top:auto}
.wb-deploy-chips .float-chip{position:static;top:auto;left:auto;right:auto;bottom:auto;animation:none;z-index:1;font-size:.72rem;padding:.4rem .7rem}
@media (max-width:860px){.workflow-grid{grid-template-columns:1fr;max-width:460px;margin:0 auto}}

/* ---------- Hero ML Pipeline (lifecycle visual inside hero) ---------- */
.hero-aside-ship{align-items:stretch}
.hero-pipeline{position:relative;display:flex;flex-direction:column;align-items:stretch;gap:0;padding:12px 14px 14px;border-radius:22px;background:color-mix(in srgb,var(--surface) 50%,transparent);border:1px solid var(--border);box-shadow:0 30px 64px -34px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.5);overflow:hidden}
.hero-pipeline::before{content:"";position:absolute;inset:0;background:radial-gradient(110% 60% at 50% -10%,var(--accent-soft),transparent 60%);pointer-events:none;opacity:.6;z-index:0}
.hero-pipeline > *{position:relative;z-index:1}

.pipe-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.05rem .25rem .5rem;border-bottom:1px dashed var(--border);margin-bottom:9px}
.pipe-id{font-family:var(--font-mono);font-size:.78rem;font-weight:600;color:var(--text);display:inline-flex;align-items:center;gap:.5rem}
.pipe-id-dot{width:8px;height:8px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 4px color-mix(in srgb,var(--accent) 18%,transparent),0 0 14px 2px var(--accent-glow)}
.pipe-id-mute{color:var(--text-mute);font-weight:500}
.pipe-status{font-family:var(--font-mono);font-size:.62rem;font-weight:700;letter-spacing:.14em;color:#0f7a3c;display:inline-flex;align-items:center;gap:.42rem;padding:.25rem .58rem;border-radius:999px;background:color-mix(in srgb,#22c55e 16%,transparent);border:1px solid color-mix(in srgb,#22c55e 35%,transparent)}
.pipe-status-dot{width:6px;height:6px;border-radius:50%;background:#22c55e;animation:pipePulse 1.4s ease-in-out infinite}
@keyframes pipePulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.8)}}

.pipe-port{display:flex;align-items:center;gap:.65rem;padding:.5rem .75rem;border-radius:14px;background:color-mix(in srgb,var(--surface) 82%,transparent);border:1px solid var(--border-strong);box-shadow:0 14px 28px -22px var(--accent-glow)}
.pipe-port-ic{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;flex-shrink:0;background:linear-gradient(135deg,var(--accent),var(--accent-2));color:#fff;font-size:.92rem;box-shadow:0 8px 18px -8px var(--accent-glow)}
.pipe-port-ic-out{background:linear-gradient(135deg,#22c55e,#10b981)}
.pipe-port-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px;line-height:1.15}
.pipe-port-body b{font-family:var(--font-display);font-size:.88rem;font-weight:700;color:var(--text)}
.pipe-port-body span{font-family:var(--font-mono);font-size:.68rem;color:var(--text-mute);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* live p95 heartbeat on the endpoint line (ticked by telemetry.js heroLive) */
.pipe-port-body .ep-lat{font-family:var(--font-mono);font-size:.68rem;font-weight:600;color:var(--accent-ink)}
.pipe-port-tag{font-family:var(--font-mono);font-size:.58rem;font-weight:700;letter-spacing:.14em;color:var(--accent-ink);padding:.22rem .48rem;border-radius:999px;background:color-mix(in srgb,var(--accent) 14%,transparent);border:1px solid color-mix(in srgb,var(--accent) 35%,transparent);flex-shrink:0}
.pipe-port-tag-out{color:#0f6b3a;background:color-mix(in srgb,#22c55e 14%,transparent);border-color:color-mix(in srgb,#22c55e 35%,transparent)}
.pipe-note{margin:10px 2px 0;font-family:var(--font-mono);font-size:.66rem;letter-spacing:.02em;color:var(--text-mute);text-align:center}

.pipe-link{position:relative;display:block;width:2px;height:13px;margin:0 auto;background:linear-gradient(to bottom,transparent 0%,var(--accent) 35%,var(--accent-2) 70%,transparent 100%);background-size:100% 220%;background-position:50% 100%;animation:pipeFlow 1.6s linear infinite;border-radius:2px;opacity:.9}
.pipe-link::before{content:"";position:absolute;left:50%;top:0;width:7px;height:7px;border-radius:50%;background:var(--accent);transform:translate(-50%,-50%);box-shadow:0 0 12px 1px var(--accent-glow);animation:pipeDot 1.6s linear infinite}
@keyframes pipeFlow{0%{background-position:50% 100%}100%{background-position:50% -100%}}
@keyframes pipeDot{0%{top:0;opacity:0}15%{opacity:1}85%{opacity:1}100%{top:100%;opacity:0}}

.pipe-node{position:relative}
.pipe-node.wb-card{display:flex;flex-direction:column;padding:0 0 18px;border-radius:14px}/* 18px = reserved lane for the .pipe-detail bar so it never covers content */
.pipe-node.wb-metrics,.pipe-node.wb-deploy{padding:0 0 18px}
.pipe-node .wb-card-head{padding:.45rem .9rem 0}
.pipe-node .wb-metric-row{padding:.35rem .9rem}
.pipe-node.wb-metrics .wb-metric-row:last-child{padding-bottom:0}
.pipe-node .wb-metric-row + .wb-metric-row{border-top:1px dashed var(--border)}
.pipe-node .wb-term-body{padding:.45rem .9rem 0;line-height:1.6}
.pipe-node .wb-bar{padding:.3rem .9rem}
.pipe-node.wb-deploy .wb-deploy-row{padding:.55rem .9rem .4rem}
.pipe-node.wb-deploy .wb-deploy-chips{padding:0 .9rem 0}
.pipe-node .wb-step{padding:.45rem .9rem .08rem}

/* Stage-pulse: highlight each stage in sequence to feel "alive" */
.pipe-node::after{content:"";position:absolute;inset:-1px;border-radius:inherit;border:1.5px solid transparent;pointer-events:none;animation:pipeStage 6s ease-in-out infinite}
.pipe-node.wb-terminal::after{animation-delay:0s}
.pipe-node.wb-metrics::after{animation-delay:2s}
.pipe-node.wb-deploy::after{animation-delay:4s}
@keyframes pipeStage{0%,18%,82%,100%{border-color:transparent;box-shadow:0 0 0 transparent}33%,67%{border-color:color-mix(in srgb,var(--accent) 55%,transparent);box-shadow:0 0 26px -6px var(--accent-glow),inset 0 0 22px -8px color-mix(in srgb,var(--accent) 40%,transparent)}}

.hero-marquee{margin-top:clamp(46px,7vw,86px);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.marquee{overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent);mask-image:linear-gradient(90deg,transparent,#000 6%,#000 94%,transparent)}
.marquee-track{display:inline-flex;align-items:center;gap:1.4rem;padding:17px 1.4rem;white-space:nowrap;animation:marquee 36s linear infinite;font-family:var(--font-display);font-weight:600;font-size:1.05rem;color:var(--text-soft)}
.marquee-track .sep{color:var(--accent-ink)}
@keyframes marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ---------- Section heads ---------- */
.section{padding-top:var(--section-y)}
.section-head{margin-bottom:46px;max-width:760px}
.section-head > .eyebrow{display:flex}
.section-head.between{display:flex;align-items:flex-end;justify-content:space-between;max-width:none;gap:24px;flex-wrap:wrap}
.section-head.between > div:first-child{display:flex;flex-direction:column;align-items:flex-start}
.eyebrow{font-family:var(--font-mono);font-size:.76rem;letter-spacing:.14em;color:var(--accent-ink);text-transform:uppercase;font-weight:500;display:inline-flex;align-items:center;gap:.5rem;position:relative}
.eyebrow::before{content:"";width:22px;height:2px;background:var(--grad);border-radius:2px;box-shadow:0 0 8px var(--accent-glow)}
.section-title{font-size:clamp(2.05rem,4.3vw,3.5rem);font-weight:800;letter-spacing:-.03em;margin:.5rem 0 0;position:relative;display:inline-block;text-wrap:balance}
/* margin-top clears the title's underline. That rule is absolutely positioned at
   bottom:-10px and is 3px tall, so it occupies +10..+13px below the title box —
   the old .7rem (11px) put the sub-line straight through it, which is what made
   the head read as clumsy once the punchline went bold and blue underneath. */
.section-sub{color:var(--text-soft);font-size:1.05rem;line-height:1.62;max-width:60ch;margin-top:2rem}
/* The punchline fragment that opens each section-sub — "Verified, not decorative."
   Now on ALL SEVEN section heads, contact included, so no section is left with a
   flat descriptive sub-line while its neighbours have a hook. */
.section-sub .punch,.contact-sub .punch{display:block;color:var(--accent-ink);font-weight:600;font-size:1.12rem;line-height:1.35;letter-spacing:-.005em;margin-bottom:.42rem;transition:transform .32s var(--ease)}

/* The punchline wipes in AFTER the title's underline has drawn, so the head
   resolves in one order: number -> title -> rule -> punchline -> sentence.
   `both` holds the from-state during the delay; crucially the hidden state is
   NOT in the base rule, so if .in-view never fires (no GSAP) it just shows. */
@keyframes punchWipe{from{clip-path:inset(0 100% 0 0);opacity:0}to{clip-path:inset(0 0 0 0);opacity:1}}
.section-head.in-view .punch{animation:punchWipe .68s var(--ease) .52s both}
@keyframes punchRise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.contact-card.in-view .punch{animation:punchRise .6s var(--ease) .45s both}

/* hovering the head nudges the punchline and settles its sentence to full ink */
.section-head:hover .punch{transform:translateX(3px)}
.section-head .section-sub{transition:color .32s var(--ease)}
.section-head:hover .section-sub{color:var(--text)}

@media (prefers-reduced-motion:reduce){
    .section-head.in-view .punch,.contact-card.in-view .punch{animation:none}
    .section-head:hover .punch{transform:none}
    .section-sub .punch,.contact-sub .punch,.section-head .section-sub{transition:none}
}
/* ---- hallmark-style chapter openers: an architectural section index sits on a hairline rule with a "/07" ratio, biasing the top of every section. Numeral is scoped to [data-sec] so shared detail pages stay untouched; it reads attr(data-sec), independent of the JS-rewritten eyebrow text. ---- */
.section-head[data-sec]{position:relative;border-top:1px solid var(--border);padding-top:clamp(54px,6vw,66px)}
.section-head[data-sec]::before{content:attr(data-sec);position:absolute;left:0;top:12px;font-family:var(--font-display);font-weight:800;font-size:clamp(1.6rem,3vw,2.6rem);line-height:1;letter-spacing:-.03em;background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent;opacity:.92}
.section-head[data-sec]::after{content:"/ 07";position:absolute;right:2px;top:20px;font-family:var(--font-mono);font-size:.72rem;font-weight:600;letter-spacing:.12em;color:var(--text-mute)}
.section-head.in-view[data-sec]::before{animation:secIndexIn .7s var(--ease) both}
@keyframes secIndexIn{from{opacity:0;transform:translateY(14px)}to{opacity:.92;transform:none}}
/* contact is a centred CTA card (::before already used for its glow) — mark it as the final chapter with a centred mono ratio */
.contact-card[data-sec]{padding-top:clamp(56px,6vw,88px)}
.contact-card[data-sec]::after{content:attr(data-sec) " / 07";position:absolute;left:50%;top:24px;transform:translateX(-50%);font-family:var(--font-mono);font-size:.74rem;font-weight:600;letter-spacing:.16em;color:var(--text-mute);z-index:2}

/* ---------- Cards (shared) ---------- */
.card-soft{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:0 1px 3px rgba(15,23,42,.06),0 6px 16px -6px rgba(15,23,42,.08)}

/* ---------- About ---------- */
.about-quote{position:relative;font-family:var(--font-display);font-weight:600;font-size:clamp(1.35rem,2.3vw,1.85rem);line-height:1.34;letter-spacing:-.02em;color:var(--text);padding:8px 0 8px 26px;border-left:3px solid var(--accent);margin-bottom:26px;text-wrap:pretty}
.about-quote .q-mark{position:absolute;left:20px;top:-10px;font-size:1.5rem;color:var(--accent);opacity:.4}
.about-quote .q-key{background:linear-gradient(transparent 60%,var(--accent-soft) 60%);color:var(--accent-ink);padding:0 .06em;font-weight:700}
.about-copy p{color:var(--text-soft);margin-bottom:1em;font-size:1.02rem;line-height:1.78;text-wrap:pretty}
.about-copy .lead-p{font-size:1.14rem;color:var(--text);font-weight:500;line-height:1.62}
.about-copy strong{color:var(--text);font-weight:600}
.about-copy em{color:var(--text);font-style:italic}
.about-copy .ink-highlight{color:var(--accent-ink);font-weight:600}
.about-hobby-note{font-size:.96rem!important;color:var(--text-soft)!important;border-left:2px solid var(--accent-line);padding-left:1rem;margin-top:1.4rem}
/* the craft list demos itself — each word does the thing it names on hover */
.about-hobby-note .craft{display:inline-block;text-decoration:underline dotted var(--accent-line);text-decoration-thickness:1.5px;text-underline-offset:3px;transition:transform .25s var(--ease),color var(--t),opacity var(--t),background-color var(--t),box-shadow var(--t) var(--ease),background-size .3s var(--ease)}
/* hovering one craft focuses it — its siblings step back */
.about-hobby-note em:has(.craft:hover) .craft:not(:hover){opacity:.5}
/* design — a design-tool selection frame with corner handles draws around the word */
.craft-design{position:relative}
.craft-design::before{content:"";position:absolute;inset:-3px -6px;border:1px solid var(--accent);border-radius:2px;background:linear-gradient(var(--accent),var(--accent)) no-repeat 0 0/5px 5px,linear-gradient(var(--accent),var(--accent)) no-repeat 100% 0/5px 5px,linear-gradient(var(--accent),var(--accent)) no-repeat 0 100%/5px 5px,linear-gradient(var(--accent),var(--accent)) no-repeat 100% 100%/5px 5px;opacity:0;transform:scale(.8);pointer-events:none;transition:opacity .2s var(--ease),transform .25s var(--ease)}
.craft-design:hover,.craft-design.play{color:var(--accent-ink)}
.craft-design:hover::before,.craft-design.play::before{opacity:1;transform:none}
/* use-case fit — mono brackets slide in and fit the phrase exactly */
.craft-fit{position:relative}
.craft-fit::before,.craft-fit::after{position:absolute;top:50%;font-family:var(--font-mono);font-weight:700;font-style:normal;color:var(--accent);opacity:0;pointer-events:none;transition:opacity .2s var(--ease),transform .28s var(--ease)}
.craft-fit::before{content:"[";left:-.6em;transform:translate(-5px,-52%)}
.craft-fit::after{content:"]";right:-.6em;transform:translate(5px,-52%)}
.craft-fit:hover,.craft-fit.play{color:var(--accent-ink)}
.craft-fit:hover::before,.craft-fit:hover::after,.craft-fit.play::before,.craft-fit.play::after{opacity:1;transform:translate(0,-52%)}
/* UX — the word becomes the interface: lifts into a pressable control */
.craft-ux{padding:.06em .18em;margin:-.06em -.18em;border-radius:6px}
.craft-ux:hover,.craft-ux.play{background-color:var(--accent-soft);color:var(--accent-ink);transform:translateY(-2px);box-shadow:0 8px 16px -8px var(--accent-glow)}
.craft-ux:active{transform:translateY(0) scale(.96)}
/* "attention to detail" demos itself: the phrase recedes, your eye is pulled to the smallest detail on the line — the dot of the i gets circled. No transform, so the line never reflows. */
.craft-detail .ttl,.hc-detail .ttl{position:relative;transition:color var(--t),text-shadow var(--t)}
.craft-detail .ttl::after,.hc-detail .ttl::after{content:"";position:absolute;left:55%;top:.26em;width:.66em;height:.66em;transform:translate(-50%,-50%) scale(.35);border:1.5px solid var(--accent);border-radius:50%;opacity:0;pointer-events:none;transition:transform .3s var(--ease),opacity .2s var(--ease)}
.craft-detail:hover,.craft-detail.play,.hc-detail:hover,.hc-detail.play{color:var(--text-mute)}
.craft-detail:hover .ttl,.craft-detail.play .ttl,.hc-detail:hover .ttl,.hc-detail.play .ttl{color:var(--accent);text-shadow:0 0 10px var(--accent-glow)}
.craft-detail:hover .ttl::after,.craft-detail.play .ttl::after,.hc-detail:hover .ttl::after,.hc-detail.play .ttl::after{opacity:1;transform:translate(-50%,-50%) scale(1)}
/* cost — gets cut: a strike slashes through and the word settles lower */
.craft-cost{background:linear-gradient(var(--text-mute),var(--text-mute)) no-repeat left 54%/0% 1.5px}
.craft-cost:hover,.craft-cost.play{background-size:100% 1.5px;color:var(--text-mute);transform:translateY(1px)}
.about-tags{display:flex;flex-wrap:wrap;gap:10px;margin-top:24px}
.about-tag{display:inline-flex;align-items:center;gap:.5rem;padding:.5rem .95rem;border-radius:999px;background:var(--surface);border:1px solid var(--border);font-size:.82rem;font-weight:600;color:var(--text-soft);transition:border-color var(--t),color var(--t),transform var(--t)}
.about-tag i{color:var(--accent-ink)}
.about-tag:hover{border-color:var(--accent);color:var(--text);transform:translateY(-2px)}
/* ---------- Identity pills — custom micro-interactions (each pill demos itself) ---------- */
.at-swap{display:inline-grid;overflow:hidden;line-height:1.25;vertical-align:middle}
.at-swap>span{grid-area:1/1;white-space:nowrap;transition:transform .3s var(--ease),opacity .2s ease}
.at-swap .at-b{transform:translateY(120%);opacity:0}
.about-tag:hover .at-a{transform:translateY(-120%);opacity:0}
.about-tag:hover .at-b{transform:translateY(0);opacity:1}
/* 01 · M.Tech — the cap gets tossed */
.tag-mtech:hover i{animation:capToss .55s var(--ease)}
@keyframes capToss{40%{transform:translateY(-7px) rotate(-20deg)}100%{transform:translateY(-1px) scale(1.14)}}
/* 02 · NLP — the label re-tokenizes like a BPE playground, tokens land staggered */
.tag-nlp .tok{display:inline-block;padding:0 .24em;margin-right:.16em;border-radius:4px;background:var(--accent-soft);color:var(--accent-ink);font-family:var(--font-mono);font-size:.88em}
.tag-nlp .tok:nth-child(2n){background:color-mix(in srgb,var(--accent-2) 18%,transparent)}
.tag-nlp .tok:last-child{margin-right:0}
.tag-nlp:hover .tok{animation:tokIn .3s var(--ease) backwards}
.tag-nlp:hover .tok:nth-child(2){animation-delay:.07s}
.tag-nlp:hover .tok:nth-child(3){animation-delay:.14s}
.tag-nlp:hover .tok:nth-child(4){animation-delay:.21s}
@keyframes tokIn{from{transform:translateY(5px);opacity:0}}
.tag-nlp:hover i{animation:cpuThink .9s ease-in-out infinite}
@keyframes cpuThink{0%,100%{transform:translateY(-1px) scale(1.14)}50%{transform:translateY(-1px) scale(1.3)}}
/* 03 · Open Source — merge spin */
.tag-oss:hover i{animation:gitSpin .55s var(--ease)}
@keyframes gitSpin{to{transform:rotate(1turn) scale(1.14)}}
/* 04 · F1 — Max Verstappen mode: rev-counter wiggle + a speed streak across the pill */
.tag-f1{position:relative;overflow:hidden}
.tag-f1 .at-b{color:var(--accent-ink);font-weight:700}
.tag-f1:hover i{animation:revWiggle .6s var(--ease)}
@keyframes revWiggle{30%{transform:rotate(-28deg)}65%{transform:rotate(20deg)}100%{transform:translateY(-1px) scale(1.14)}}
.tag-f1::after{content:"";position:absolute;top:50%;left:-45%;width:36%;height:2px;border-radius:2px;background:linear-gradient(90deg,transparent,var(--accent),transparent);opacity:0;transform:translateY(-50%);pointer-events:none}
.tag-f1:hover::after{animation:f1streak .65s var(--ease) 2}
@keyframes f1streak{0%{left:-45%;opacity:0}25%{opacity:.9}100%{left:112%;opacity:0}}
@media (prefers-reduced-motion:reduce){.tag-mtech:hover i,.tag-nlp:hover .tok,.tag-nlp:hover i,.tag-oss:hover i,.tag-f1:hover i,.tag-f1:hover::after{animation:none}.at-swap>span,.about-hobby-note .craft{transition:none}}

/* circular profile portrait (moved out of hero) */
.about-profile{display:flex;align-items:center;gap:18px;padding:20px;background:linear-gradient(180deg,color-mix(in srgb,var(--surface-2) 65%,var(--surface)),var(--surface));border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:16px;position:relative;overflow:hidden;box-shadow:0 22px 50px -32px rgba(0,0,0,.55),inset 0 1px 0 color-mix(in srgb,#fff 7%,transparent);transition:border-color var(--t) var(--ease),transform var(--t) var(--ease),box-shadow var(--t) var(--ease)}
.about-profile::before{content:"";position:absolute;inset:0;background:radial-gradient(480px 200px at 100% 0%,var(--accent-soft),transparent 60%);pointer-events:none}
.about-profile::after{content:"";position:absolute;left:0;right:0;top:0;height:1px;background:linear-gradient(90deg,transparent,var(--accent-line) 30%,var(--accent-line) 70%,transparent)}
.about-profile:hover{transform:translateY(-3px);border-color:color-mix(in srgb,var(--accent) 38%,var(--border));box-shadow:0 34px 64px -34px rgba(0,0,0,.6),0 0 0 1px color-mix(in srgb,var(--accent) 12%,transparent),inset 0 1px 0 color-mix(in srgb,#fff 9%,transparent)}
.ap-portrait{position:relative;width:96px;height:96px;flex-shrink:0;border-radius:50%;display:grid;place-items:center}
.ap-portrait img{width:88px;height:88px;border-radius:50%;object-fit:cover;object-position:center 12%;background:linear-gradient(160deg,var(--surface-2),var(--surface-3));box-shadow:0 12px 28px -10px var(--accent-glow);position:relative;z-index:2}
.ap-ring{position:absolute;inset:0;border-radius:50%;padding:2px;background:conic-gradient(from 140deg,var(--accent),var(--accent-2),transparent 70%,var(--accent));-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;animation:spinRing 9s linear infinite;z-index:1}
.ap-id{display:flex;flex-direction:column;gap:3px;position:relative;z-index:2}
.ap-id strong{font-family:var(--font-display);font-size:1.08rem;font-weight:700;letter-spacing:-.01em}
.ap-role{font-size:.82rem;color:var(--accent-ink);font-weight:600}
.ap-loc{font-size:.76rem;color:var(--text-mute);font-family:var(--font-mono);display:inline-flex;align-items:center;gap:.4rem}
.ap-loc i{color:var(--accent-ink)}
.ap-avail{font-size:.75rem;color:var(--text-soft);display:inline-flex;align-items:center;gap:.45rem;margin-top:2px}
.ap-avail .status-dot{width:8px;height:8px}

.about-card{position:relative;overflow:hidden;background:linear-gradient(180deg,color-mix(in srgb,var(--surface-2) 55%,var(--surface)),var(--surface));border:1px solid var(--border);border-radius:var(--radius-lg);padding:22px 24px;margin-bottom:16px;box-shadow:0 22px 50px -32px rgba(0,0,0,.55),inset 0 1px 0 color-mix(in srgb,#fff 7%,transparent);transition:border-color var(--t) var(--ease),transform var(--t) var(--ease),box-shadow var(--t) var(--ease)}
/* faint accent glow bleeding from the top-right corner */
.about-card::before{content:"";position:absolute;top:-30px;right:-30px;width:200px;height:160px;background:radial-gradient(130px 130px at 100% 0%,var(--accent-soft),transparent 70%);pointer-events:none;opacity:.95}
/* hairline gradient rule across the very top edge */
.about-card::after{content:"";position:absolute;left:0;right:0;top:0;height:1px;background:linear-gradient(90deg,transparent,var(--accent-line) 30%,var(--accent-line) 70%,transparent)}
.about-card:hover{transform:translateY(-3px);border-color:color-mix(in srgb,var(--accent) 38%,var(--border));box-shadow:0 34px 64px -34px rgba(0,0,0,.6),0 0 0 1px color-mix(in srgb,var(--accent) 12%,transparent),inset 0 1px 0 color-mix(in srgb,#fff 9%,transparent)}
.ac-head{display:flex;align-items:center;gap:.7rem;margin-bottom:18px}
.ac-icon{width:38px;height:38px;border-radius:11px;display:grid;place-items:center;color:#fff;background:var(--grad);font-size:1.02rem;box-shadow:0 8px 18px -7px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.3);flex-shrink:0}
.ac-head h3{font-size:1.06rem;font-weight:700;margin:0}
.ac-note{margin-left:auto;font-family:var(--font-mono);font-size:.66rem;letter-spacing:.06em;color:var(--text-mute);display:inline-flex;align-items:center;gap:.38rem}
.ac-note i{color:var(--accent-ink);font-size:.72rem}
.do-list li{display:flex;gap:.8rem;align-items:flex-start;color:var(--text-soft);font-size:.95rem;line-height:1.5;margin-bottom:15px;transition:color var(--t)}
.do-list li:last-child{margin-bottom:0}
.do-list li:hover{color:var(--text)}
.do-ic{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;background:linear-gradient(150deg,color-mix(in srgb,var(--accent) 24%,transparent),color-mix(in srgb,var(--accent-2) 12%,transparent));color:var(--accent-ink);font-size:.95rem;flex-shrink:0;margin-top:1px;border:1px solid color-mix(in srgb,var(--accent) 26%,transparent);box-shadow:0 6px 14px -9px var(--accent-glow);transition:transform var(--t) var(--ease)}
.do-list li:hover .do-ic{transform:translateY(-2px) scale(1.05)}
.do-list strong{color:var(--text);font-weight:600}
.timeline{position:relative;margin:0;padding:0;list-style:none}
.timeline::before{content:"";position:absolute;left:7px;top:6px;bottom:6px;width:2px;background:linear-gradient(180deg,var(--accent),var(--accent-line));border-radius:2px}
.timeline li{display:flex;gap:14px;margin-bottom:18px}
.timeline li:last-child{margin-bottom:0}
.ti-dot{width:16px;height:16px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 4px var(--surface),0 0 12px 1px var(--accent-glow);flex-shrink:0;margin-top:4px}
.timeline li:nth-child(2) .ti-dot{background:var(--surface-3);box-shadow:0 0 0 4px var(--surface),inset 0 0 0 2px var(--accent)}
.ti-body{display:flex;flex-direction:column;gap:1px}
.ti-body strong{font-family:var(--font-display);font-size:.97rem;font-weight:700}
.ti-body span{font-size:.87rem;color:var(--text-soft)}
.ti-body small{font-size:.77rem;color:var(--text-mute);font-family:var(--font-mono)}

/* Education — clean glass tiles, no connecting timeline line */
.edu-list{display:flex;flex-direction:column;gap:12px}
.edu-item{position:relative;display:flex;gap:.85rem;align-items:flex-start;padding:13px 15px;border-radius:13px;background:color-mix(in srgb,var(--surface-2) 55%,transparent);border:1px solid var(--border);transition:border-color var(--t) var(--ease),transform var(--t) var(--ease),background var(--t)}
.edu-item:hover{border-color:color-mix(in srgb,var(--accent) 36%,var(--border));transform:translateX(3px);background:color-mix(in srgb,var(--surface-2) 82%,transparent)}
.edu-badge{width:34px;height:34px;border-radius:10px;flex-shrink:0;display:grid;place-items:center;font-size:.95rem;color:var(--accent-ink);background:linear-gradient(150deg,color-mix(in srgb,var(--accent) 22%,transparent),color-mix(in srgb,var(--accent-2) 10%,transparent));border:1px solid color-mix(in srgb,var(--accent) 26%,transparent);box-shadow:0 6px 14px -9px var(--accent-glow)}
.edu-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.edu-top{display:flex;align-items:center;justify-content:space-between;gap:.6rem}
.edu-top strong{font-family:var(--font-display);font-size:.95rem;font-weight:700;color:var(--text);line-height:1.2}
.edu-grade{flex-shrink:0;font-family:var(--font-mono);font-size:.64rem;font-weight:700;letter-spacing:.03em;color:var(--accent-ink);padding:.18rem .5rem;border-radius:999px;background:var(--accent-soft);border:1px solid color-mix(in srgb,var(--accent) 28%,transparent);white-space:nowrap}
.edu-deg{font-size:.85rem;color:var(--text-soft);line-height:1.35}
.edu-meta{font-family:var(--font-mono);font-size:.72rem;color:var(--text-mute)}

/* Off-the-clock card sits in the left column under the tags */
.about-hobbies{margin-top:20px}
.about-hobbies-card{padding:22px 24px 24px}
.about-hobbies .hobbies-grid{justify-content:flex-start;gap:11px;margin-top:2px}
.about-hobbies .hobby{height:54px;padding:0 16px}
.about-hobbies .hobby i{font-size:1.3rem}

/* ---------- Skills ---------- */
.skill-feature{position:relative;overflow:hidden;border-radius:var(--radius-lg);border:1px solid var(--border-strong);background:var(--surface);padding:clamp(28px,4vw,42px);box-shadow:0 1px 3px rgba(15,23,42,.06),0 8px 24px -8px rgba(15,23,42,.10)}
.skill-feature::before{content:"";position:absolute;inset:0;background:radial-gradient(640px 220px at 88% -20%,var(--accent-soft),transparent 62%);pointer-events:none}
.skill-feature>*{position:relative;z-index:1}
.skill-feature h3{font-size:clamp(1.3rem,2.2vw,1.75rem);margin:.5rem 0 .6rem;font-weight:700}
.skill-feature p{color:var(--text-soft);font-size:1rem;line-height:1.65;margin-bottom:1.2rem;max-width:none;text-wrap:pretty}
.skill-feature .proof-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;margin:4px 0 20px}
.skill-feature .proof-grid div{position:relative;display:grid;grid-template-columns:auto auto 1fr;align-items:center;column-gap:9px;row-gap:2px;min-height:74px;padding:11px 12px;border-radius:12px;border:1px solid var(--border);background:color-mix(in srgb,var(--surface) 70%,transparent);box-shadow:0 12px 24px -24px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 42%,transparent);overflow:hidden;transition:transform .24s var(--ease),border-color .24s var(--ease),background .24s var(--ease),box-shadow .24s var(--ease)}
.skill-feature .proof-grid div::after{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:linear-gradient(90deg,var(--accent),var(--accent-2));transform:scaleX(.18);transform-origin:left;transition:transform .32s var(--ease);opacity:.75}
.skill-feature .proof-grid i{grid-row:1 / span 2;width:30px;height:30px;border-radius:9px;display:grid;place-items:center;color:var(--accent-ink);background:var(--accent-soft);border:1px solid var(--accent-line);font-size:.9rem}
.skill-feature .proof-grid b{font-family:var(--font-display);font-size:1.55rem;line-height:1;color:var(--accent-ink);margin:0}
.skill-feature .proof-grid span{font-family:var(--font-display);font-size:.88rem;font-weight:750;line-height:1.15;color:var(--text)}
.skill-feature .proof-grid small{grid-column:2 / -1;font-size:.72rem;line-height:1.25;color:var(--text-mute)}
.skill-feature .proof-grid div:hover{transform:translateY(-2px);border-color:var(--accent-line);background:color-mix(in srgb,var(--accent) 6%,var(--surface));box-shadow:0 16px 30px -22px var(--accent-glow)}
.skill-feature .proof-grid div:hover::after{transform:scaleX(1)}
.chip{display:inline-block;padding:.42rem .85rem;border-radius:9px;background:var(--surface-2);border:1px solid var(--border);font-size:.82rem;font-weight:500;color:var(--text-soft);margin:.2rem .2rem 0 0}
.focus-pillars{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;margin:22px 0}
.focus-pillar{display:flex;gap:.8rem;align-items:flex-start;padding:14px 16px;background:color-mix(in srgb,var(--surface) 70%,transparent);border:1px solid var(--border);border-radius:var(--radius);transition:transform var(--t) var(--ease),border-color var(--t),box-shadow var(--t)}
.focus-pillar:hover{transform:translateY(-3px);border-color:var(--accent-line);box-shadow:0 18px 38px -22px var(--accent-glow)}
.fp-ic{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;flex-shrink:0;background:var(--accent-soft);color:var(--accent-ink);font-size:1rem}
.focus-pillar strong{display:block;font-family:var(--font-display);font-size:.95rem;font-weight:700;margin-bottom:1px}
.focus-pillar span{font-size:.8rem;color:var(--text-mute);line-height:1.4}
.focus-chips{display:flex;flex-wrap:wrap;gap:.2rem}
@media (max-width:560px){.focus-pillars{grid-template-columns:1fr}}
.chip-accent{color:var(--accent-ink);border-color:var(--accent-line);background:var(--accent-soft)}
.skill-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:26px;height:100%;box-shadow:0 1px 3px rgba(15,23,42,.06),0 8px 24px -8px rgba(15,23,42,.10);transition:transform var(--t) var(--ease),border-color var(--t),box-shadow var(--t)}
.skill-card:hover{transform:translateY(-4px);border-color:var(--border-strong);box-shadow:0 20px 44px -24px var(--accent-glow)}
.sc-head{display:flex;align-items:center;gap:.8rem;margin-bottom:18px}
.sc-icon{width:42px;height:42px;border-radius:12px;display:grid;place-items:center;color:#fff;background:var(--grad);font-size:1.2rem;box-shadow:0 8px 18px -6px var(--accent-glow)}
.sc-head h3{font-size:1.06rem;font-weight:700}
.skill-card>div:not(.sc-head){display:flex;flex-wrap:wrap;gap:14px}
.skill-chip{display:inline-flex;align-items:center;gap:.55rem;padding:.56rem 1rem;border-radius:999px;background:var(--surface-2);border:1px solid var(--border);font-size:.84rem;font-weight:500;color:var(--text-soft);transition:transform var(--t),color var(--t),background var(--t),border-color var(--t);margin:0}
.skill-chip:hover{background:var(--accent-soft);color:var(--accent-ink);border-color:var(--accent-line);transform:translateY(-2px)}
.skill-chip img{width:19px;height:19px;object-fit:contain;flex-shrink:0}

/* ---------- Featured ---------- */
.featured-card{display:flex;flex-direction:column;height:100%;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;box-shadow:0 1px 3px rgba(15,23,42,.06),0 8px 24px -8px rgba(15,23,42,.10);transition:transform var(--t) var(--ease),border-color var(--t),box-shadow var(--t);transform-style:preserve-3d}
.featured-card:hover{transform:translateY(-6px);border-color:var(--border-strong);box-shadow:0 30px 60px -28px var(--accent-glow)}
.featured-card.primary{border-color:var(--accent-line);box-shadow:0 18px 50px -28px var(--accent-glow)}
.fc-media{position:relative;display:block;aspect-ratio:16/10;overflow:hidden;background:var(--surface-3)}
.fc-media img{width:100%;height:100%;object-fit:cover;transition:transform .6s var(--ease)}
.featured-card:hover .fc-media img{transform:scale(1.06)}
.fc-badge{position:absolute;top:13px;left:13px;padding:.3rem .65rem;border-radius:8px;font-size:.68rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;font-family:var(--font-mono);background:rgba(8,12,20,.78);color:#fff;backdrop-filter:blur(6px);border:1px solid rgba(255,255,255,.16)}
.fc-badge.gen-ai{background:var(--grad);border-color:transparent}
.fc-badge.nlp{background:rgba(8,12,20,.82);color:var(--accent-2);border-color:var(--accent-line)}
.fc-body{padding:22px;display:flex;flex-direction:column;gap:.65rem;flex:1}
.fc-body h3{font-size:1.12rem;font-weight:700;line-height:1.3}
.fc-body p{color:var(--text-soft);font-size:.92rem;line-height:1.55;flex:1;margin:0}
.fc-tags{display:flex;flex-wrap:wrap;gap:.35rem}
.fc-tags span{font-size:.68rem;padding:.24rem .52rem;border-radius:6px;background:var(--surface-2);color:var(--text-soft);font-weight:500;border:1px solid var(--border);font-family:var(--font-mono)}
.fc-actions{display:flex;justify-content:space-between;align-items:center;padding-top:.7rem;border-top:1px solid var(--border);font-size:.84rem;font-weight:600;margin-top:.3rem}
.fc-link{color:var(--accent-ink)}.fc-link.muted{color:var(--text-mute)}
.fc-link.muted:hover{color:var(--accent-ink)}

/* ---------- Projects grid + filter + pagination ---------- */
.filter-tabs{display:inline-flex;gap:.3rem;background:var(--surface);border:1px solid var(--border);padding:.3rem;border-radius:12px}
.filter-tab{padding:.55rem 1rem;border-radius:9px;font-size:.85rem;font-weight:600;color:var(--text-soft);border:0;background:transparent;transition:color var(--t),background var(--t);display:inline-flex;align-items:center;gap:.45rem;cursor:pointer}
.filter-tab .count{font-family:var(--font-mono);font-size:.7rem;padding:.05rem .42rem;border-radius:999px;background:var(--surface-3);color:var(--text-mute)}
.filter-tab:hover{color:var(--text)}
.filter-tab.active{background:var(--grad);color:#fff}
.filter-tab.active .count{background:rgba(255,255,255,.22);color:#fff}
.project-card{display:flex;flex-direction:column;height:100%;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:0 1px 3px rgba(15,23,42,.06),0 6px 18px -6px rgba(15,23,42,.09);transition:transform var(--t) var(--ease),border-color var(--t),box-shadow var(--t)}
.project-card:hover{transform:translateY(-5px);border-color:var(--border-strong);box-shadow:0 18px 40px -22px var(--accent-glow)}
.pc-media{aspect-ratio:16/10;overflow:hidden;background:var(--surface-3)}
.pc-media img{width:100%;height:100%;object-fit:cover;transition:transform .5s var(--ease)}
.project-card:hover .pc-media img{transform:scale(1.05)}
.pc-body{padding:16px;display:flex;flex-direction:column;gap:.5rem;flex:1}
.pc-cat{font-family:var(--font-mono);font-size:.68rem;letter-spacing:.07em;color:var(--accent-ink);text-transform:uppercase;font-weight:500}
.pc-body h4{font-size:.97rem;font-weight:700;line-height:1.3}
.pc-tags{display:flex;flex-wrap:wrap;gap:.3rem;position:relative}
.pc-tags span{font-size:.64rem;padding:.2rem .44rem;border-radius:5px;background:var(--surface-2);color:var(--text-soft);font-weight:500;border:1px solid var(--border);font-family:var(--font-mono);transition:opacity .3s var(--ease),transform .3s var(--ease),max-height .3s var(--ease)}
.pc-tags .hidden-tag{display:none;opacity:0;transform:translateY(-4px)}
.pc-tags.expanded .hidden-tag{display:inline-block;opacity:1;transform:translateY(0);animation:tagReveal .35s var(--ease) both}
@keyframes tagReveal{from{opacity:0;transform:translateY(-6px) scale(.92)}to{opacity:1;transform:translateY(0) scale(1)}}
.pc-tags button.more{background:var(--accent-soft);color:var(--accent-ink);border:1px solid var(--accent-line);border-radius:5px;font-size:.64rem;padding:.2rem .52rem;font-weight:600;font-family:var(--font-mono);cursor:pointer;transition:background var(--t),color var(--t),transform var(--t),border-color var(--t)}
.pc-tags button.more:hover{background:var(--accent);color:#fff;border-color:var(--accent);transform:scale(1.05)}
.pc-tags.expanded button.more{background:var(--surface-2);color:var(--text-mute);border-color:var(--border)}
.pc-actions{display:flex;gap:.45rem;margin-top:auto;padding-top:.6rem}
.pc-actions .btn{flex:1}
.pagination-ap{display:flex;align-items:center;justify-content:center;gap:.5rem;margin-top:38px;flex-wrap:wrap}
.pg-btn,.pg-num{min-width:40px;height:40px;padding:0 .55rem;border-radius:10px;display:grid;place-items:center;background:var(--surface);border:1px solid var(--border);color:var(--text);font-family:var(--font-mono);font-weight:600;font-size:.85rem;transition:color var(--t),border-color var(--t),background var(--t);cursor:pointer}
.pg-btn:hover:not(:disabled),.pg-num:hover{color:var(--accent-ink);border-color:var(--accent)}
.pg-btn:disabled{opacity:.4;cursor:not-allowed}
.pg-num.active{background:var(--grad);color:#fff;border-color:transparent}
.pg-pages{display:inline-flex;gap:.4rem}
.pg-info{font-family:var(--font-mono);font-size:.78rem;color:var(--text-mute);margin-left:.8rem}

/* ---------- Certifications ---------- */
.cert-card{display:flex;flex-direction:column;height:100%;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;box-shadow:0 1px 3px rgba(15,23,42,.06),0 6px 18px -6px rgba(15,23,42,.09);transition:transform var(--t) var(--ease),border-color var(--t),box-shadow var(--t)}
.cert-card:hover{transform:translateY(-5px);border-color:var(--accent);box-shadow:0 20px 42px -22px var(--accent-glow)}
.cert-head{padding:18px;display:flex;align-items:center;gap:14px}
.cert-logo{width:44px;height:44px;object-fit:contain;background:#fff;border-radius:9px;padding:6px;flex-shrink:0;box-shadow:0 2px 8px rgba(0,0,0,.08)}
.cert-head h4{font-size:.96rem;font-weight:700;line-height:1.3;margin-bottom:2px}
.cert-head span{font-size:.77rem;color:var(--text-mute);font-family:var(--font-mono)}
.cert-media{background:linear-gradient(180deg,#ffffff,#eef1f6);padding:22px;height:172px;display:grid;place-items:center;overflow:hidden;border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.cert-media img{max-width:86%;max-height:128px;object-fit:contain;filter:drop-shadow(0 8px 16px rgba(15,23,42,.16))}
.cert-foot{padding:12px 18px;display:flex;align-items:center;justify-content:space-between;font-size:.82rem;font-weight:600;color:var(--accent-ink);border-top:1px solid var(--border)}
.cert-cta{text-align:center;margin-top:30px}

/* ---------- Learning platforms + Hobbies (cool 3D extruded tiles) ---------- */
.panel{position:relative;overflow:hidden;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-lg);padding:26px 26px 26px 30px;box-shadow:0 1px 3px rgba(15,23,42,.06),0 8px 24px -8px rgba(15,23,42,.10)}
.panel::before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:linear-gradient(180deg,var(--accent),var(--accent-2))}
.panel h4{font-size:.78rem;font-family:var(--font-mono);letter-spacing:.1em;text-transform:uppercase;color:var(--text-mute);margin:0 0 16px;font-weight:500}
.panel h4:not(:first-child){margin-top:26px}
.platforms-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,150px));justify-content:center;gap:14px}
.hobbies-grid{display:flex;flex-wrap:wrap;gap:12px;justify-content:center}

/* platform = 3D extruded tile */
.platform{position:relative;display:flex;flex-direction:column;align-items:center;gap:.5rem;padding:16px 8px;border-radius:14px;border:1px solid var(--border);transition:transform var(--t-fast) var(--ease),box-shadow var(--t-fast) var(--ease),border-color var(--t)}
.platform img{width:34px;height:34px;object-fit:contain}
.platform span{font-size:.76rem;font-weight:600;color:var(--text-soft);text-align:center}

/* hobby = expanding pill: icon-only, slides open to reveal the name on hover */
.hobby{position:relative;display:inline-flex;align-items:center;height:60px;padding:0 18px;border-radius:15px;border:1px solid var(--border);overflow:hidden;cursor:default;transition:transform var(--t) var(--ease),box-shadow var(--t) var(--ease),border-color var(--t)}
.hobby i{font-size:1.45rem;line-height:1;flex-shrink:0;color:var(--accent-ink);transition:color var(--t)}
.hobby span{max-width:0;opacity:0;overflow:hidden;white-space:nowrap;margin-left:0;font-size:.84rem;font-weight:600;color:var(--text);transition:max-width .38s var(--ease),opacity .25s ease,margin-left .38s var(--ease)}
.hobby:hover span{max-width:150px;opacity:1;margin-left:11px}
.hobby:hover i{color:var(--accent)}

[data-theme="light"] .platform,[data-theme="light"] .hobby{background:linear-gradient(145deg,#ffffff,#e7edf6);box-shadow:5px 5px 0 rgba(15,23,42,.07),0 14px 26px -14px rgba(15,23,42,.28),inset 0 1px 0 #fff}
[data-theme="dark"] .platform,[data-theme="dark"] .hobby{background:linear-gradient(145deg,#1d1f25,#101115);box-shadow:5px 5px 0 rgba(0,0,0,.45),0 16px 30px -14px rgba(0,0,0,.72),inset 0 1px 0 rgba(255,255,255,.045)}
.platform:hover{transform:translate(-3px,-3px);border-color:var(--accent-line)}
.hobby:hover{transform:translateY(-3px);border-color:var(--accent)}
[data-theme="light"] .platform:hover,[data-theme="light"] .hobby:hover{box-shadow:5px 5px 18px rgba(37,99,235,.2),0 22px 36px -14px rgba(15,23,42,.32)}
[data-theme="dark"] .platform:hover,[data-theme="dark"] .hobby:hover{box-shadow:5px 5px 20px rgba(59,130,246,.36),0 24px 40px -14px rgba(0,0,0,.82)}
.platform:active{transform:translate(0,0);box-shadow:2px 2px 0 rgba(0,0,0,.2),0 6px 14px -8px rgba(0,0,0,.4)}
.hobby:hover i{color:var(--accent)}

/* ---------- Contact ---------- */
.section-contact{padding-bottom:0}
.contact-card{position:relative;overflow:hidden;border-radius:var(--radius-xl);border:1px solid var(--border-strong);background:var(--surface);padding:clamp(36px,5vw,76px);text-align:center;box-shadow:0 1px 3px rgba(15,23,42,.06),0 12px 32px -10px rgba(15,23,42,.12)}
.contact-card::before{content:"";position:absolute;inset:0;background:radial-gradient(720px 300px at 50% -16%,var(--accent-soft),transparent 60%);pointer-events:none}
.contact-card>*{position:relative;z-index:1}
.contact-title{font-size:clamp(1.9rem,4.4vw,3.3rem);font-weight:800;letter-spacing:-.025em;line-height:1.1;margin:.5rem 0 .7rem}
.contact-sub{color:var(--text-soft);font-size:1.05rem;max-width:55ch;margin:0 auto 28px}
.contact-sub strong{color:var(--text)}
.contact-actions{display:flex;justify-content:center;gap:.7rem;flex-wrap:wrap;margin-bottom:34px}
.contact-meta{display:grid;grid-template-columns:repeat(2,1fr);gap:18px;max-width:520px;margin:0 auto;padding-top:28px;border-top:1px solid var(--border)}
.contact-meta div{display:flex;flex-direction:column;gap:4px}
.contact-meta strong{font-family:var(--font-mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--text-mute);font-weight:500}
.contact-meta span{color:var(--text);font-weight:500;font-size:.92rem}

/* ---------- Footer (clean) ---------- */
.footer{padding:36px 0 30px;margin-top:80px;border-top:1px solid var(--border)}
.footer-top{display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap;padding-bottom:24px;border-bottom:1px solid var(--border)}
.footer-links{display:flex;flex-wrap:wrap;gap:.4rem 1.6rem;justify-content:center}
.footer-links a{color:var(--text-soft);font-size:.9rem;font-weight:500;transition:color var(--t)}
.footer-links a:hover{color:var(--accent-ink)}
.footer-social{display:flex;gap:.5rem}
.footer-social a{width:40px;height:40px;border-radius:11px;display:grid;place-items:center;background:var(--surface);border:1px solid var(--border);color:var(--text-soft);font-size:1.05rem;transition:color var(--t),border-color var(--t),transform var(--t)}
.footer-social a:hover{color:var(--accent-ink);border-color:var(--accent);transform:translateY(-3px)}
.footer-bottom{text-align:center;padding-top:22px;font-family:var(--font-mono);font-size:.82rem;color:var(--text-mute)}

/* =====================================================================
   PREMIUM FOOTER (.site-footer) — project detail pages
   ===================================================================== */
.site-footer{position:relative;z-index:1;margin-top:clamp(48px,6vw,80px);padding-bottom:24px;overflow:hidden}
.site-footer .container{position:relative;z-index:1}
.site-footer::before{content:"";position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,var(--accent-line),transparent)}
.site-footer::after{content:"";position:absolute;left:50%;top:-30px;transform:translateX(-50%);width:min(820px,92%);height:300px;background:radial-gradient(ellipse 62% 60% at 50% 0%,var(--accent-glow),transparent 72%);opacity:.4;pointer-events:none;z-index:0}
.sf-cta{position:relative;margin-top:clamp(28px,4vw,48px);display:flex;align-items:center;justify-content:space-between;gap:28px;flex-wrap:wrap;padding:clamp(22px,3vw,36px);border-radius:var(--radius-lg);background:linear-gradient(135deg,color-mix(in srgb,var(--surface) 88%,transparent),color-mix(in srgb,var(--accent-soft) 85%,transparent));border:1px solid var(--border-strong);box-shadow:0 30px 64px -36px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.4);backdrop-filter:blur(16px) saturate(160%);-webkit-backdrop-filter:blur(16px) saturate(160%);overflow:hidden}
.sf-cta::before{content:"";position:absolute;top:-55%;right:-8%;width:360px;height:360px;background:radial-gradient(circle,var(--accent-soft),transparent 70%);pointer-events:none}
.sf-cta-text{position:relative;z-index:1}
.sf-eyebrow{display:inline-flex;align-items:center;gap:.5rem;font-family:var(--font-mono);font-size:.72rem;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--accent-ink);margin-bottom:14px}
.sf-dot{width:8px;height:8px;border-radius:50%;background:#22C55E;animation:sfPulse 2.2s ease-in-out infinite}
@keyframes sfPulse{0%{box-shadow:0 0 0 0 rgba(34,197,94,.5)}70%{box-shadow:0 0 0 7px rgba(34,197,94,0)}100%{box-shadow:0 0 0 0 rgba(34,197,94,0)}}
.sf-cta-title{font-family:var(--font-display);font-weight:700;font-size:clamp(1.4rem,2.8vw,2rem);line-height:1.12;letter-spacing:-.02em;color:var(--text);margin:0}
.sf-cta-title span{background:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.sf-cta-sub{margin:.7rem 0 0;color:var(--text-soft);font-size:1rem;line-height:1.55;max-width:44ch}
.sf-cta-actions{position:relative;z-index:1;display:flex;gap:.7rem;flex-wrap:wrap}
.sf-btn{display:inline-flex;align-items:center;gap:.5rem;padding:.82rem 1.35rem;border-radius:var(--radius-sm);font-weight:600;font-size:.92rem;border:1px solid transparent;cursor:pointer;transition:transform var(--t) var(--ease),box-shadow var(--t),border-color var(--t),color var(--t)}
.sf-btn i{font-size:1.05rem}
.sf-btn-primary{background:var(--grad);color:#fff;box-shadow:0 12px 28px -10px var(--accent-glow)}
.sf-btn-primary:hover{color:#fff;transform:translateY(-3px);box-shadow:0 18px 38px -10px var(--accent-glow)}
.sf-btn-ghost{background:color-mix(in srgb,var(--surface) 72%,transparent);color:var(--text);border-color:var(--border-strong)}
.sf-btn-ghost:hover{color:var(--accent-ink);border-color:var(--accent);transform:translateY(-3px)}
.sf-main{display:grid;grid-template-columns:1.7fr 1fr 1fr;gap:36px;padding:clamp(28px,3.6vw,44px) 0 26px}
.sf-brand{display:inline-flex;align-items:center;gap:.6rem}
.sf-brand-text{font-family:var(--font-display);font-weight:700;font-size:1.05rem;color:var(--text)}
.sf-brand-dot{color:var(--accent);font-weight:800}
.sf-tagline{margin:1.05rem 0 1.3rem;color:var(--text-soft);font-size:.92rem;line-height:1.65;max-width:40ch}
.sf-social{display:flex;gap:.5rem}
.sf-social a{width:42px;height:42px;border-radius:12px;display:grid;place-items:center;background:color-mix(in srgb,var(--surface) 72%,transparent);border:1px solid var(--border);color:var(--text-soft);font-size:1.1rem;transition:color var(--t),border-color var(--t),transform var(--t),box-shadow var(--t)}
.sf-social a:hover{color:var(--accent-ink);border-color:var(--accent);transform:translateY(-3px);box-shadow:0 12px 24px -12px var(--accent-glow)}
.sf-col{display:flex;flex-direction:column;align-items:flex-start}
.sf-col-title{font-family:var(--font-mono);font-size:.72rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--text-mute);margin:0 0 1rem}
.sf-col a{color:var(--text-soft);font-size:.94rem;font-weight:500;padding:.4rem 0;transition:color var(--t),transform var(--t)}
.sf-col a:hover{color:var(--accent-ink);transform:translateX(4px)}
.sf-bottom{display:flex;align-items:center;justify-content:center;gap:14px;flex-wrap:wrap;padding-top:24px;border-top:1px solid var(--border);text-align:center}
.sf-made{font-family:var(--font-mono);font-size:.82rem;letter-spacing:.01em;color:var(--text-soft)}
@media (max-width:991.98px){
    .site-footer .sf-main{grid-template-columns:1fr 1fr;gap:30px 24px}
    .sf-brand-col{grid-column:1 / -1}
    .sf-cta{flex-direction:column;align-items:flex-start;gap:22px}
    .sf-cta-actions{width:100%}
}
@media (max-width:560px){
    .site-footer{margin-top:clamp(56px,12vw,80px)}
    .sf-cta{padding:22px}
    .sf-cta-title{font-size:1.4rem}
    .sf-cta-actions{flex-direction:column}
    .sf-btn{width:100%;justify-content:center}
    .site-footer .sf-main{grid-template-columns:1fr 1fr;gap:24px 16px;padding:34px 0 28px}
    .sf-tagline{max-width:none}
    .sf-bottom{text-align:center}
}

/* =====================================================================
   DETAIL PAGE
   ===================================================================== */
.detail-main{padding-top:120px}
/* breadcrumb — a blurred telemetry chip */
.breadcrumb-ap{display:inline-flex;align-items:center;gap:.5rem;flex-wrap:wrap;font-family:var(--font-mono);font-size:.78rem;color:var(--text-mute);background:color-mix(in srgb,var(--surface) 80%,transparent);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid var(--border);padding:.5rem .95rem;border-radius:999px;margin-bottom:30px;box-shadow:0 10px 26px -20px var(--accent-glow)}
.breadcrumb-ap a{color:var(--text-soft);display:inline-flex;align-items:center;gap:.35rem}
.breadcrumb-ap .sep{opacity:.4}
.breadcrumb-ap .current{color:var(--accent-ink);font-weight:600}
/* hero — editorial kicker + oversized balanced title */
.detail-hero{margin-bottom:46px}
.detail-cat{display:inline-flex;align-items:center;gap:.55rem;font-family:var(--font-mono);font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;color:var(--accent-ink);font-weight:600}
.detail-cat::before{content:"";width:22px;height:2px;border-radius:2px;background:var(--grad);box-shadow:0 0 8px var(--accent-glow)}
.detail-title{font-size:clamp(2.1rem,5vw,3.6rem);font-weight:800;letter-spacing:-.035em;line-height:1.03;margin:.65rem 0 .85rem;text-wrap:balance}
.detail-tagline{color:var(--text-soft);font-size:clamp(1.02rem,1.5vw,1.18rem);max-width:66ch;line-height:1.65}
.detail-cta-row{display:flex;gap:.7rem;flex-wrap:wrap;margin-top:26px}
/* Gallery — premium framed viewer */
.gallery{border-radius:var(--radius-lg);padding:16px;height:100%}
.gallery-stage{position:relative;border-radius:var(--radius);overflow:hidden;background:var(--surface-3);aspect-ratio:16/10;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--border-strong) 65%,transparent)}
.gallery-stage img{width:100%;height:100%;object-fit:contain;background:#fff}
.gallery-nav{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;border-radius:50%;display:grid;place-items:center;background:color-mix(in srgb,#0b1120 60%,transparent);color:#fff;border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);font-size:1.3rem;cursor:pointer;opacity:.85;transition:background var(--t),transform var(--t) var(--ease),box-shadow var(--t),opacity var(--t);z-index:2}
.gallery:hover .gallery-nav{opacity:1}
.gallery-nav:hover{background:var(--accent);transform:translateY(-50%) scale(1.09);box-shadow:0 10px 24px -8px var(--accent-glow)}
.gallery-nav:active{transform:translateY(-50%) scale(.95)}
.gallery-nav.prev{left:12px}.gallery-nav.next{right:12px}
.gallery-counter{position:absolute;bottom:12px;right:12px;z-index:2;font-family:var(--font-mono);font-size:.72rem;font-weight:600;letter-spacing:.04em;background:color-mix(in srgb,#0b1120 66%,transparent);color:#fff;padding:.28rem .62rem;border-radius:999px;border:1px solid rgba(255,255,255,.2);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.gallery-caption{margin-top:14px;font-size:.8rem;color:var(--text-mute);text-align:center;min-height:1.2em;font-family:var(--font-mono);letter-spacing:.02em}
.gallery-thumbs{display:flex;gap:.55rem;overflow-x:auto;padding:14px 2px 4px;scrollbar-width:thin}
.gallery-thumbs img{width:88px;height:56px;object-fit:cover;border-radius:9px;border:1px solid var(--border);cursor:pointer;flex-shrink:0;opacity:.5;background:#fff;transition:opacity var(--t),border-color var(--t),transform var(--t) var(--ease),box-shadow var(--t)}
.gallery-thumbs img:hover{opacity:1;transform:translateY(-2px)}
.gallery-thumbs img.active{opacity:1;border-color:var(--accent);box-shadow:0 0 0 1px var(--accent),0 8px 18px -10px var(--accent-glow)}
/* Info / summary cards */
.info-card{border-radius:var(--radius-lg);padding:28px;height:100%}
.info-card h3,.detail-block h3{font-size:1.14rem;font-weight:700;letter-spacing:-.01em;margin-bottom:20px;display:flex;align-items:center;gap:.7rem}
.info-card h3 .bi,.detail-block h3 .bi{flex-shrink:0;width:34px;height:34px;display:grid;place-items:center;background:var(--accent-soft);border:1px solid var(--accent-line);border-radius:10px;color:var(--accent-ink);font-size:1.02rem;box-shadow:inset 0 1px 0 color-mix(in srgb,#fff 28%,transparent)}
.info-row{display:flex;flex-direction:column;gap:10px;padding:16px 0;border-bottom:1px dashed var(--border)}
.info-row:last-child{border-bottom:0;padding-bottom:0}
.info-row:first-of-type{padding-top:0}
.info-label{font-family:var(--font-mono);font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;color:var(--text-mute);display:flex;align-items:center;gap:.5rem;font-weight:600}
.info-label .bi{color:var(--accent-ink);font-size:.9rem}
.info-value{color:var(--text);font-size:.95rem;font-weight:500;display:flex;flex-wrap:wrap;gap:.42rem;align-items:center}
/* concept pills (with a telemetry dot) vs mono tech tags — both gain hover craft */
.pill{display:inline-flex;align-items:center;gap:.42rem;padding:.34rem .72rem;border-radius:8px;background:var(--accent-soft);color:var(--accent-ink);border:1px solid var(--accent-line);font-size:.75rem;font-weight:600;transition:transform var(--t) var(--ease),border-color var(--t),box-shadow var(--t)}
.pill::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent-glow);flex-shrink:0}
.pill:hover{transform:translateY(-2px);border-color:var(--accent);box-shadow:0 9px 18px -10px var(--accent-glow)}
.tag{display:inline-flex;align-items:center;padding:.34rem .72rem;border-radius:8px;background:var(--surface-2);color:var(--text-soft);border:1px solid var(--border);font-size:.76rem;font-weight:500;font-family:var(--font-mono);transition:transform var(--t) var(--ease),border-color var(--t),color var(--t),box-shadow var(--t)}
.tag:hover{transform:translateY(-2px);border-color:var(--accent);color:var(--accent-ink);box-shadow:0 9px 18px -12px var(--accent-glow)}
/* detail blocks */
.detail-block{border-radius:var(--radius-lg);padding:28px;margin-bottom:20px}
.detail-block p{color:var(--text-soft);line-height:1.75;font-size:1.02rem;margin:0}
.detail-block > div{display:flex;flex-wrap:wrap;gap:.5rem}
/* approach — a numbered timeline with a connecting rail */
.approach-steps{counter-reset:step;list-style:none;margin:0;padding:0}
.approach-steps li{position:relative;padding:0 0 22px 52px;color:var(--text-soft);line-height:1.62;font-size:.98rem}
.approach-steps li:last-child{padding-bottom:0}
.approach-steps li::before{counter-increment:step;content:counter(step,decimal-leading-zero);position:absolute;left:0;top:-3px;width:34px;height:34px;border-radius:10px;background:var(--grad);color:#fff;display:grid;place-items:center;font-family:var(--font-display);font-weight:700;font-size:.78rem;box-shadow:0 8px 18px -8px var(--accent-glow);z-index:1}
.approach-steps li::after{content:"";position:absolute;left:16px;top:34px;bottom:2px;width:2px;background:linear-gradient(var(--accent-line),transparent);z-index:0}
.approach-steps li:last-child::after{display:none}
.approach-steps li strong{color:var(--text);font-weight:600}
.detail-links{display:flex;flex-wrap:wrap;gap:.7rem}

/* Lightbox */
.lightbox{position:fixed;inset:0;z-index:3000;background:rgba(6,9,15,.92);display:none;align-items:center;justify-content:center;padding:30px}
.lightbox.open{display:flex}
.lightbox img{max-width:94vw;max-height:90vh;object-fit:contain;border-radius:10px;background:#fff}
.lightbox-close{position:absolute;top:20px;right:24px;color:#fff;font-size:2rem;cursor:pointer;line-height:1}

/* ---------- Reveal fallback (AOS handles most) ---------- */
@media (prefers-reduced-motion: reduce){
    *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
    .marquee-track{animation:none}
    [data-aos]{opacity:1!important;transform:none!important}
}

/* =====================================================================
   RESPONSIVE
   ===================================================================== */
@media (max-width:991.98px){
    .hero-inner{grid-template-columns:1fr;gap:34px}
    .hero-main,.hero-aside{min-width:0}   /* prevent grid min-content blowout → hero text stops clipping on mobile */
    .hero-aside{align-items:center}
    .portrait-stage{max-width:380px;margin-bottom:18px}
    .hero-stats{width:100%}
    .footer-top{flex-direction:column;align-items:flex-start;gap:18px}
    .footer-links{justify-content:flex-start}
    .section-head.between{flex-direction:column;align-items:flex-start}
}
@media (max-width:767.98px){
    /* (legacy dropdown nav removed — the ≤991.98px slide-in drawer handles all mobile) */
    .hero{padding-top:104px}
    .portrait-stage{max-width:330px}
    .float-chip{font-size:.68rem;padding:.36rem .62rem}
    .hero-stats{grid-template-columns:repeat(3,1fr)}
    .hero-stats div{border-top:0;border-left:1px solid var(--border)}
    .hero-stats div:first-child{border-left:0}
    .hero-stats dt{font-size:1.25rem}
    .platforms-row{grid-template-columns:repeat(4,1fr);gap:.5rem}
    .platform img{width:28px;height:28px}
    .contact-meta{grid-template-columns:1fr;gap:14px;text-align:left}
    .section-head{margin-bottom:32px}
    .pg-info{display:block;width:100%;text-align:center;margin:8px 0 0}
    .detail-main{padding-top:96px}
}
@media (max-width:480px){
    .hobbies-grid{grid-template-columns:repeat(2,1fr)}
    .portrait-stage{max-width:280px}
    .fc-py,.fc-rag{left:0}
    .fc-llm,.fc-agent{right:0}
    .hero-stats{grid-template-columns:1fr}
    .hero-stats div{border-left:0;border-top:1px solid var(--border)}
    .hero-stats div:first-child{border-top:0}
    .platforms-row{grid-template-columns:repeat(2,1fr)}
}

/* =====================================================================
   WALL-E — Chat assistant widget
   ===================================================================== */
.chat-launcher{position:fixed;bottom:24px;right:24px;z-index:1060;width:58px;height:58px;border-radius:18px;border:1px solid transparent;cursor:pointer;padding:5px;background:color-mix(in srgb,var(--surface) 80%,transparent);backdrop-filter:blur(18px) saturate(175%);-webkit-backdrop-filter:blur(18px) saturate(175%);box-shadow:0 12px 30px -10px rgba(15,23,42,.28),0 2px 8px rgba(15,23,42,.08),inset 0 1px 0 color-mix(in srgb,#fff 45%,transparent);display:grid;place-items:center;transition:transform .4s cubic-bezier(.34,1.56,.64,1),box-shadow .35s,border-color .35s,opacity var(--t),visibility var(--t);animation:walleFloat 3.6s ease-in-out infinite}
@keyframes walleFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
.chat-launcher::before{content:"";position:absolute;inset:-2px;border-radius:20px;padding:2px;background:conic-gradient(from 0deg,var(--accent),var(--accent-2),color-mix(in srgb,var(--accent) 35%,transparent) 55%,var(--accent));-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:.75;animation:chatSpin 6s linear infinite;pointer-events:none}
@keyframes chatSpin{to{transform:rotate(360deg)}}
.chat-launcher:hover{transform:translateY(-4px) scale(1.06);animation:none;box-shadow:0 18px 40px -10px var(--accent-glow),0 3px 10px rgba(15,23,42,.12)}
.chat-launcher:hover::before{opacity:1}
.chat-launcher>img{width:44px;height:44px;border-radius:12px;object-fit:contain;position:relative;z-index:1;filter:drop-shadow(0 2px 6px rgba(15,23,42,.12));transition:filter .3s,transform .3s}
.chat-launcher:hover>img{filter:drop-shadow(0 5px 14px var(--accent-glow));transform:scale(1.05)}
[data-theme="dark"] .chat-launcher{background:color-mix(in srgb,var(--surface) 66%,transparent);box-shadow:0 14px 34px -10px rgba(0,0,0,.5),inset 0 1px 0 rgba(255,255,255,.05)}
.chat-launcher-ring{position:absolute;inset:-4px;border-radius:20px;border:1.5px solid var(--accent-line);opacity:0;animation:chatPulse 3.2s ease-out infinite;pointer-events:none}
@keyframes chatPulse{0%{transform:scale(1);opacity:.4}70%{transform:scale(1.35);opacity:0}100%{opacity:0}}
.chat-launcher-badge{position:absolute;top:-5px;right:-5px;z-index:2;background:linear-gradient(135deg,#22C55E,#16A34A);color:#fff;font-family:var(--font-mono);font-size:.5rem;font-weight:700;line-height:1;padding:.17rem .34rem;border-radius:999px;border:2px solid var(--bg);letter-spacing:.03em;box-shadow:0 2px 6px rgba(34,197,94,.4)}
.chat-launcher::after{content:"Ask me about Aradhya";position:absolute;right:70px;top:50%;transform:translateY(-50%) translateX(6px);white-space:nowrap;background:var(--surface);color:var(--text);font-size:.76rem;font-weight:600;padding:.5rem .8rem;border-radius:11px;border:1px solid var(--border-strong);box-shadow:0 10px 24px -12px rgba(15,23,42,.35);opacity:0;visibility:hidden;transition:opacity var(--t),transform var(--t);pointer-events:none}
.chat-launcher:hover::after{opacity:1;visibility:visible;transform:translateY(-50%) translateX(0)}
.chat-launcher.open{opacity:0;visibility:hidden;transform:scale(.6);pointer-events:none}

.chat-panel{position:fixed;bottom:24px;right:24px;z-index:1061;width:clamp(360px,94vw,420px);height:min(76vh,640px);display:flex;flex-direction:column;overflow:hidden;background:color-mix(in srgb,var(--surface) 90%,transparent);backdrop-filter:blur(30px) saturate(185%);-webkit-backdrop-filter:blur(30px) saturate(185%);border:1px solid var(--border-strong);border-radius:24px;box-shadow:0 60px 120px -30px rgba(15,23,42,.5),0 20px 44px -24px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 55%,transparent);transform-origin:bottom right;transform:translateY(24px) scale(.94);opacity:0;transition:transform .38s var(--ease),opacity .28s var(--ease)}
.chat-panel.open{transform:translateY(0) scale(1);opacity:1}
.chat-panel[hidden]{display:none}
[data-theme="dark"] .chat-panel{background:color-mix(in srgb,var(--surface) 78%,transparent);box-shadow:0 60px 120px -30px rgba(0,0,0,.75),0 20px 44px -24px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.05)}

/* ---- Header ---- */
.chat-head{position:relative;display:flex;align-items:center;gap:.75rem;padding:1rem 1.05rem;border-bottom:1px solid var(--border);overflow:hidden}
.chat-head::before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(120% 120% at 0% 0%,color-mix(in srgb,var(--accent) 22%,transparent),transparent 58%),radial-gradient(100% 120% at 100% 0%,color-mix(in srgb,var(--accent-2) 18%,transparent),transparent 60%)}
.chat-head>*{position:relative;z-index:1}
.chat-head-avatar{position:relative;flex-shrink:0}
.chat-head-avatar::before{content:"";position:absolute;inset:-3px;border-radius:50%;padding:2px;background:conic-gradient(from 140deg,var(--accent),var(--accent-2),transparent 72%,var(--accent));-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;animation:chatSpin 7s linear infinite}
.chat-head-avatar img{width:42px;height:42px;border-radius:50%;background:#fff;padding:2px;object-fit:cover;position:relative;z-index:1}
.chat-head-avatar::after{content:"";position:absolute;bottom:1px;right:1px;z-index:2;width:11px;height:11px;border-radius:50%;background:#22C55E;border:2px solid var(--surface);box-shadow:0 0 0 0 rgba(34,197,94,.5);animation:chatOnline 2s ease-in-out infinite}
@keyframes chatOnline{0%,100%{box-shadow:0 0 0 0 rgba(34,197,94,.5)}50%{box-shadow:0 0 0 6px rgba(34,197,94,0)}}
.chat-head-meta{display:flex;flex-direction:column;line-height:1.2;margin-right:auto;min-width:0}
.chat-head-title{font-family:var(--font-display);font-weight:800;font-size:1.06rem;color:var(--text);letter-spacing:-.01em}
.chat-head-sub{font-size:.72rem;color:var(--text-soft);font-family:var(--font-mono)}
.chat-head-sub .status-dot{display:none}
.chat-head-close{width:34px;height:34px;border-radius:11px;border:1px solid transparent;background:color-mix(in srgb,var(--surface) 55%,transparent);color:var(--text-soft);display:grid;place-items:center;cursor:pointer;font-size:.9rem;transition:color var(--t),background var(--t),border-color var(--t),transform var(--t)}
.chat-head-close:hover{color:var(--text);background:var(--surface-2);border-color:var(--border-strong);transform:rotate(90deg)}

/* ---- Body ---- */
.chat-body{flex:1;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;padding:1.1rem 1rem;display:flex;flex-direction:column;gap:.85rem;scroll-behavior:smooth}
.chat-body::-webkit-scrollbar{width:6px}
.chat-body::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:99px}
.chat-body::-webkit-scrollbar-thumb:hover{background:var(--text-mute)}

/* ---- Messages ---- */
.cm{display:flex;align-items:flex-end;gap:.5rem;max-width:100%;animation:cmIn .32s var(--ease) both}
@keyframes cmIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.cm.user{flex-direction:row-reverse}
.cm-avatar{flex-shrink:0}
.cm-avatar img{width:30px;height:30px;border-radius:50%;background:#fff;padding:1px;object-fit:cover;border:1px solid var(--border);box-shadow:0 3px 9px -3px var(--accent-glow)}
.cm-bubble{padding:.72rem .95rem;border-radius:18px;font-size:.92rem;line-height:1.55;max-width:80%;overflow-wrap:anywhere;box-shadow:0 3px 12px -7px rgba(15,23,42,.28)}
.cm.bot .cm-bubble{background:color-mix(in srgb,var(--surface-2) 82%,var(--surface));color:var(--text);border:1px solid var(--border);border-bottom-left-radius:6px}
.cm.user .cm-bubble{background:var(--grad);color:#fff;border-bottom-right-radius:6px;box-shadow:0 10px 24px -10px var(--accent-glow)}
.cm-bubble a{color:var(--accent-ink);text-decoration:underline;font-weight:600}
.cm.user .cm-bubble a{color:#fff}
.cm-bubble p{margin:0 0 .5em}.cm-bubble p:last-child{margin-bottom:0}
.cm-bubble ul,.cm-bubble ol{margin:.35em 0;padding-left:1.15em}
.cm-bubble li{margin:.2em 0}
.cm-bubble code{font-family:var(--font-mono);font-size:.85em;background:var(--code-bg);padding:.08em .38em;border-radius:6px}
.cm-bubble strong{font-weight:700}

.cm-typing{display:inline-flex;gap:5px;align-items:center;padding:.25rem .1rem}
.cm-typing span{width:7px;height:7px;border-radius:50%;background:var(--accent);opacity:.55;animation:cmDot 1.3s infinite ease-in-out}
.cm-typing span:nth-child(2){animation-delay:.16s}
.cm-typing span:nth-child(3){animation-delay:.32s}
@keyframes cmDot{0%,60%,100%{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}

/* ---- Option chips ---- */
.cm-options{display:flex;flex-wrap:wrap;gap:.45rem;padding:.2rem 0 .2rem 38px}
.cm-option{font-family:var(--font-body);font-size:.79rem;font-weight:600;padding:.5rem .9rem;border-radius:999px;cursor:pointer;background:color-mix(in srgb,var(--surface) 70%,transparent);color:var(--accent-ink);border:1px solid var(--accent-line);transition:background var(--t),color var(--t),border-color var(--t),transform var(--t),box-shadow var(--t)}
.cm-option:hover{background:var(--grad);color:#fff;border-color:transparent;transform:translateY(-2px);box-shadow:0 10px 22px -10px var(--accent-glow)}
.cm-showopts{align-self:flex-start;margin-left:38px;font-family:var(--font-body);font-size:.77rem;font-weight:600;color:var(--text-mute);background:transparent;border:1px dashed var(--border-strong);border-radius:999px;padding:.4rem .85rem;cursor:pointer;transition:color var(--t),border-color var(--t),background var(--t)}
.cm-showopts:hover{color:var(--accent-ink);border-color:var(--accent);border-style:solid;background:var(--accent-soft)}

/* ---- Composer (modern in-field send) ---- */
.chat-input{display:flex;align-items:flex-end;gap:.4rem;margin:.35rem .85rem calc(.85rem + env(safe-area-inset-bottom,0px));padding:.35rem .35rem .35rem .95rem;border:1px solid var(--border-strong);border-radius:18px;background:color-mix(in srgb,var(--surface) 92%,transparent);box-shadow:inset 0 1px 0 color-mix(in srgb,#fff 40%,transparent);transition:border-color var(--t),box-shadow var(--t)}
.chat-input:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft),0 10px 24px -16px var(--accent-glow)}
.chat-input textarea{flex:1;min-width:0;resize:none;border:0;background:transparent;padding:.5rem 0;font-family:var(--font-body);font-size:.92rem;line-height:1.5;color:var(--text);max-height:120px}
.chat-input textarea::placeholder{color:var(--text-mute)}
.chat-input textarea:focus{outline:none}
.chat-send,.chat-stop{flex-shrink:0;width:40px;height:40px;border-radius:13px;border:0;cursor:pointer;display:grid;place-items:center;font-size:1rem;transition:transform var(--t),box-shadow var(--t),opacity var(--t),background var(--t),color var(--t)}
.chat-send{background:var(--grad);color:#fff;box-shadow:0 8px 20px -10px var(--accent-glow)}
.chat-send:hover{transform:translateY(-2px) scale(1.04);box-shadow:0 12px 26px -8px var(--accent-glow)}
.chat-send:disabled{opacity:.4;cursor:not-allowed;transform:none;box-shadow:none}
.chat-stop{background:var(--surface-2);color:var(--text);border:1px solid var(--border-strong)}
.chat-stop:hover{color:var(--accent-ink);border-color:var(--accent)}
.chat-stop[hidden]{display:none}
.chat-foot{margin:0;padding:.15rem .9rem .7rem;text-align:center;font-family:var(--font-mono);font-size:.62rem;color:var(--text-mute);letter-spacing:.02em}

@media (max-width:767.98px){
    .chat-panel{width:auto;left:12px;right:12px;bottom:12px;height:min(84vh,640px)}
    .chat-launcher{bottom:16px;right:16px;width:52px;height:52px}
    .chat-launcher>img{width:40px;height:40px}
    .chat-launcher::after{display:none}
}

/* =====================================================================
   UNIFIED CARD SURFACE — the footer-CTA "blue glass" look on every card.
   Excludes the hero (.wb-card) and the 3D .platform / .hobby tiles.
   ===================================================================== */
.card-soft,.about-card,.about-hobbies-card,.skill-feature,.skill-card,.featured-card,.project-card,.cert-card,.contact-card,.panel,.info-card,.gallery,.detail-block,.credly-card{background:linear-gradient(135deg,var(--surface) 38%,color-mix(in srgb,var(--accent) 13%,var(--surface)));border:1px solid var(--border-strong);box-shadow:0 18px 40px -28px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 55%,transparent)}
[data-theme="dark"] .card-soft,[data-theme="dark"] .about-card,[data-theme="dark"] .about-hobbies-card,[data-theme="dark"] .skill-feature,[data-theme="dark"] .skill-card,[data-theme="dark"] .featured-card,[data-theme="dark"] .project-card,[data-theme="dark"] .cert-card,[data-theme="dark"] .contact-card,[data-theme="dark"] .panel,[data-theme="dark"] .info-card,[data-theme="dark"] .gallery,[data-theme="dark"] .detail-block,[data-theme="dark"] .credly-card{box-shadow:0 18px 40px -28px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.06)}

/* =====================================================================
   PER-SECTION SPARK — subtle, creative signature interactions
   (decode = JS in spark.js; the rest are pure CSS)
   ===================================================================== */
/* Decode reveal: a faint accent glow while a title resolves into place */
.section-title.is-decoding,.detail-title.is-decoding{text-shadow:0 0 20px color-mix(in srgb,var(--accent) 32%,transparent)}

/* Projects + Skills cards: a soft light tracks the cursor across the card */
.project-card,.skill-card{position:relative}
.project-card::after,.skill-card::after{content:"";position:absolute;inset:0;border-radius:inherit;z-index:2;pointer-events:none;opacity:0;transition:opacity .35s ease;background:radial-gradient(210px 210px at var(--mx,50%) var(--my,50%),color-mix(in srgb,var(--accent) 15%,transparent),transparent 60%)}
.project-card:hover::after,.skill-card:hover::after{opacity:1}

/* Featured work: a scan-line sweeps down the card on hover (AI "reading" it) */
.featured-card{position:relative}
.featured-card::before{content:"";position:absolute;left:0;right:0;top:0;height:40%;z-index:3;pointer-events:none;opacity:0;background:linear-gradient(180deg,color-mix(in srgb,var(--accent) 32%,transparent),transparent);transform:translateY(-120%)}
.featured-card:hover::before{animation:spkScan 1.1s ease-in-out}
@keyframes spkScan{0%{opacity:0;transform:translateY(-120%)}16%{opacity:.85}100%{opacity:0;transform:translateY(300%)}}

/* Certifications: a holographic sheen sweeps across the credential on hover */
.cert-card,.credly-card{position:relative;overflow:hidden}
.cert-card::before,.credly-card::before{content:"";position:absolute;inset:-1px;z-index:3;pointer-events:none;opacity:0;background:linear-gradient(115deg,transparent 32%,color-mix(in srgb,var(--accent-2) 32%,transparent) 44%,color-mix(in srgb,#fff 42%,transparent) 50%,color-mix(in srgb,var(--accent) 30%,transparent) 56%,transparent 68%);background-size:230% 100%;background-position:120% 0}
.cert-card:hover::before,.credly-card:hover::before{animation:spkHolo 1.15s ease}
@keyframes spkHolo{0%{opacity:0;background-position:120% 0}22%{opacity:.8}100%{opacity:0;background-position:-50% 0}}

/* Skills focus pillars: "energize" — accent wash + icon lift on hover */
.focus-pillar{position:relative;overflow:hidden}
.focus-pillar::before{content:"";position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .3s ease;background:radial-gradient(140px 90px at 16% 0%,color-mix(in srgb,var(--accent) 18%,transparent),transparent 70%)}
.focus-pillar:hover::before{opacity:1}
.fp-ic{transition:transform var(--t) var(--ease),box-shadow var(--t)}
.focus-pillar:hover .fp-ic{transform:translateY(-2px) scale(1.07);box-shadow:0 10px 20px -6px var(--accent-glow)}

/* =====================================================================
   WHOLE-SITE DETAIL POLISH — the small things
   ===================================================================== */
/* Branded slim scrollbar */
html{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--accent) 45%,var(--border-strong)) transparent}
::-webkit-scrollbar{width:12px;height:12px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--accent) 42%,var(--border-strong));border:3px solid var(--bg);border-radius:99px}
::-webkit-scrollbar-thumb:hover{background:var(--accent)}

/* Primary buttons: a soft light sweeps across on hover */
.btn-primary,.sf-btn-primary{position:relative;overflow:hidden}
.btn-primary::after,.sf-btn-primary::after{content:"";position:absolute;top:0;left:-65%;width:45%;height:100%;pointer-events:none;background:linear-gradient(100deg,transparent,rgba(255,255,255,.4),transparent);transform:skewX(-18deg);opacity:0}
.btn-primary:hover::after,.sf-btn-primary:hover::after{animation:btnSheen .85s ease}
/* ===== CURSORLIGHT — every control catches the one light the telemetry-cursor emits. Reuses cursor.js's --mx/--my spotlight (extended to .btn/.sf-btn/social links). All new light lives on the FREE ::before (::after is the sheen); text stays above via isolation + z-index:-1, so no markup change and no overflow needed on ghost. ===== */
.btn,.sf-btn{position:relative;isolation:isolate}
.btn::before,.sf-btn::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;opacity:0;transition:opacity var(--t) var(--ease);background:radial-gradient(var(--btn-r,70px) var(--btn-r,70px) at var(--mx,50%) var(--my,50%),var(--btn-light,color-mix(in srgb,var(--accent) 14%,transparent)),transparent 64%)}
.btn:hover::before,.btn:focus-visible::before,.sf-btn:hover::before,.sf-btn:focus-visible::before{opacity:1}
.btn:active::before,.sf-btn:active::before{opacity:1;--btn-r:52px}
.btn-primary,.sf-btn-primary{--btn-light:color-mix(in srgb,#fff 20%,transparent)}
.btn-primary::before,.sf-btn-primary::before{mix-blend-mode:screen}
/* directional glyph "launch" + accent-glow comet — scoped to arrow glyphs so it never touches the contact send-icon or nav nudge */
.btn i.bi-arrow-right,.btn i.bi-box-arrow-up-right,.sf-btn i.bi-arrow-right{transition:transform .3s var(--ease),filter .3s var(--ease)}
.btn:hover i.bi-arrow-right,.sf-btn:hover i.bi-arrow-right{transform:translateX(4px);filter:drop-shadow(-6px 0 6px var(--accent-glow))}
.btn:hover i.bi-box-arrow-up-right{transform:translate(3px,-3px);filter:drop-shadow(-4px 4px 6px var(--accent-glow))}
/* social-icon chips join the same light family */
.footer-social a,.sf-social a{position:relative;isolation:isolate}
.footer-social a::before,.sf-social a::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;opacity:0;transition:opacity var(--t) var(--ease);background:radial-gradient(56px 56px at var(--mx,50%) var(--my,50%),color-mix(in srgb,var(--accent) 16%,transparent),transparent 64%)}
.footer-social a:hover::before,.footer-social a:focus-visible::before,.sf-social a:hover::before,.sf-social a:focus-visible::before{opacity:1}
/* glowing gradient "filament" underline for plain text links (footer + breadcrumb) — same var(--grad) as the button specular, so links and buttons are lit by one gradient */
.footer-links a,.breadcrumb-ap a{position:relative}
.footer-links a::after,.breadcrumb-ap a::after{content:"";position:absolute;left:0;right:0;bottom:-2px;height:1.5px;background:var(--grad);border-radius:2px;box-shadow:0 0 8px var(--accent-glow);transform:scaleX(0);transform-origin:right;transition:transform .3s var(--ease)}
.footer-links a:hover::after,.breadcrumb-ap a:hover::after{transform:scaleX(1);transform-origin:left}
/* nav icon glows on hover with the identical light token (keeps its existing bg + nudge) */
.nav-link:hover i{filter:drop-shadow(0 0 6px var(--accent-glow))}
@media (prefers-reduced-motion:reduce){.btn::before,.sf-btn::before,.footer-social a::before,.sf-social a::before,.footer-links a::after,.breadcrumb-ap a::after{transition:none}.btn i.bi-arrow-right,.btn i.bi-box-arrow-up-right,.sf-btn i.bi-arrow-right{transition:none}.btn:hover i.bi-arrow-right,.sf-btn:hover i.bi-arrow-right,.btn:hover i.bi-box-arrow-up-right{transform:none}}
@keyframes btnSheen{0%{left:-65%;opacity:0}14%{opacity:1}100%{left:135%;opacity:0}}

/* Hero marquee — NO-JS FALLBACK ONLY. Every rule here is scoped away the
   moment tape.js measures successfully and adds `.is-live`; the live tape
   crawls instead of hard-pausing and dims by distance instead of on/off. */
.marquee:not(.is-live):hover .marquee-track{animation-play-state:paused}
.marquee:not(.is-live) .marquee-track span:not(.sep){transition:color var(--t),opacity var(--t)}
.marquee:not(.is-live):hover .marquee-track span:not(.sep){opacity:.42}
.marquee:not(.is-live):hover .marquee-track span:not(.sep):hover{opacity:1;color:var(--accent-ink)}

/* ---------------------------------------------------------------------
   Hero tape — attention falloff  (`req/js/tape.js`, gated on `.is-live`)

   Two numbers drive the whole effect, and JS writes nothing else:
     `--f`  on the track — how engaged the tape is (0 at rest, 1 hovered)
     `--w`  on each term — a Gaussian of its distance to the pointer
   Opacity, colour and the lift are all derived from those, so the bright
   spot SLIDES along the tape and its neighbours fall off, instead of the
   old all-or-nothing dim. At `--f:0` every term resolves to exactly its
   resting style, so the tape is fully readable when you are not pointing
   at it. Colours go through `color-mix` on existing vars — it themes
   itself, nothing is hardcoded.

   ⚠ NOTHING IN HERE MAY CHANGE THE TRACK'S WIDTH. tape.js measures the
   wrap period once; a width change mid-run tears the seam. That is why
   the speed cue is a skew (painted, never laid out) and emphatically not
   letter-spacing, and why the lift is translateY and not margin.
   --------------------------------------------------------------------- */
.tape-unit{display:inline-flex;align-items:center;gap:inherit}
.marquee.is-live .marquee-track{--f:0;animation:none;will-change:transform}
.marquee.is-live .tape-unit > span{--w:0}
.marquee.is-live .tape-unit > span:not(.sep){display:inline-block;transition:none;opacity:calc(1 - var(--f)*(.58 - .58*var(--w)));color:color-mix(in srgb,var(--accent-ink) calc(var(--w)*var(--f)*100%),var(--text-soft));transform:translateY(calc(var(--w)*var(--f)*-2px))}
.marquee.is-live .tape-unit > .sep{opacity:calc(1 - var(--f)*(.5 - .5*var(--w)))}
@media (prefers-reduced-motion:reduce){.marquee.is-live .tape-unit > span:not(.sep){transform:none}}

/* Live "my time" line in the contact card */
.my-time{display:inline-flex;align-items:center;gap:.45rem;margin-top:6px;font-family:var(--font-mono);font-size:.78rem;color:var(--text-mute);font-variant-numeric:tabular-nums}
.my-time i{color:var(--accent-ink);font-size:.82rem}
.my-time b{color:var(--text-soft);font-weight:600}

/* Click-to-copy toast */
.copy-toast{position:fixed;left:50%;bottom:26px;transform:translate(-50%,16px);z-index:3000;display:inline-flex;align-items:center;gap:.5rem;padding:.6rem 1rem;border-radius:999px;font-family:var(--font-mono);font-size:.8rem;font-weight:600;color:var(--text);background:color-mix(in srgb,var(--surface) 92%,transparent);-webkit-backdrop-filter:blur(14px) saturate(160%);backdrop-filter:blur(14px) saturate(160%);border:1px solid var(--border-strong);box-shadow:0 18px 40px -18px var(--accent-glow);opacity:0;pointer-events:none;transition:opacity .25s ease,transform .25s var(--ease)}
.copy-toast.show{opacity:1;transform:translate(-50%,0)}
.copy-toast i{color:#22C55E}

/* =====================================================================
   HERO keywords + ABOUT terms — clean blue hover
   (Removed a non-blue #ff3d71/#21d4fd .lead-key glitch + its glitchTop/
    glitchBot keyframes — off-palette and a scramble effect. The lead-keys
    now get a meaningful blue hover-readout instead; see HERO · Wave 7.)
   ===================================================================== */
.lead-key{position:relative;display:inline-block;cursor:default;transition:color var(--t),text-shadow var(--t)}
.lead-key:hover{color:var(--accent);text-shadow:0 0 16px var(--accent-glow)}
.about-copy .ink-highlight{cursor:default;transition:color var(--t),text-shadow var(--t)}
.about-copy .ink-highlight:hover{color:var(--accent);text-shadow:0 0 14px var(--accent-glow)}

/* =====================================================================
   HERO PIPELINE — comes alive on hover (flow boost · stage focus · reveal · 3D)
   ===================================================================== */
.hero-pipeline:hover .pipe-link{animation-duration:.8s}
.hero-pipeline:hover .pipe-link::before{animation-duration:.8s;box-shadow:0 0 18px 2px var(--accent-glow)}
.hero-pipeline:hover .pipe-status-dot{animation-duration:.7s}

.pipe-node,.pipe-port{transition:transform .35s var(--ease),opacity .35s ease,filter .35s ease,box-shadow .3s ease,border-color .25s ease}
.pipe-node:hover,.pipe-port:hover{transform:translateX(5px) scale(1.02);border-color:var(--accent);box-shadow:0 22px 46px -20px var(--accent-glow);z-index:4}
/* focus the hovered stage — dim & desaturate the rest */
.hero-pipeline:has(.pipe-node:hover) .pipe-node:not(:hover),
.hero-pipeline:has(.pipe-node:hover) .pipe-port,
.hero-pipeline:has(.pipe-port:hover) .pipe-node,
.hero-pipeline:has(.pipe-port:hover) .pipe-port:not(:hover){opacity:.5;filter:saturate(.7)}
/* light up the active path either side of the hovered stage */
.pipe-link{transition:width .25s ease,filter .25s ease}
.pipe-node:hover + .pipe-link,.pipe-port:hover + .pipe-link,
.pipe-link:has(+ .pipe-node:hover),.pipe-link:has(+ .pipe-port:hover){width:3px;animation-duration:.6s;filter:drop-shadow(0 0 6px var(--accent-glow))}

/* extra pipeline metadata that slides up on node hover */
.pipe-detail{position:absolute;left:0;right:0;bottom:0;z-index:5;display:flex;align-items:center;gap:.4rem;height:18px;padding:0 .85rem;font-family:var(--font-mono);font-size:.575rem;font-weight:600;letter-spacing:.02em;line-height:1;white-space:nowrap;overflow:hidden;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 95%,transparent);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border-top:1px solid var(--accent-line);transform:translateY(110%);transition:transform .28s var(--ease);pointer-events:none}
.pipe-detail i{color:var(--accent)}
.pipe-node:hover .pipe-detail{transform:translateY(0)}
@media (prefers-reduced-motion:reduce){.pipe-detail{transition:none}}

/* =====================================================================
   TELEMETRY MICRO-INTERACTIONS · Wave 1
   F1 instrument-cluster motion — all on the blue --accent tokens,
   transform/opacity only, every animation guarded by reduced-motion.
   Paired with req/js/telemetry.js. Bump styles.css?v= when edited.
   ===================================================================== */

/* ---- 1. HUD viewport corner brackets (the instrument frame) ---- */
.hud-frame{position:fixed;inset:0;z-index:1900;pointer-events:none;display:none}
.hud-corner{position:absolute;width:18px;height:18px;border:1.5px solid var(--accent-line);opacity:0;transition:opacity .9s var(--ease) .35s}
.hud-frame.lit .hud-corner{opacity:.5}
.hud-corner.tl{top:14px;left:14px;border-right:0;border-bottom:0}
.hud-corner.tr{top:14px;right:14px;border-left:0;border-bottom:0}
.hud-corner.bl{bottom:14px;left:14px;border-right:0;border-top:0}
.hud-corner.br{bottom:14px;right:14px;border-left:0;border-top:0}
@media (min-width:992px){.hud-frame{display:block}}

/* ---- 2. Boot / init sequence (once per session, skippable) ---- */
.boot{position:fixed;inset:0;z-index:4000;background:var(--bg);display:grid;place-items:center;padding:24px;font-family:var(--font-mono);color:var(--text-soft)}
.boot-card{width:min(420px,92vw)}
.boot-top{display:flex;align-items:center;justify-content:space-between;font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;color:var(--text-mute);margin-bottom:14px}
.boot-top b{color:var(--accent-ink);font-weight:600;letter-spacing:.06em}
.boot-dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent-glow);margin-right:.5rem;vertical-align:middle;animation:pulse 2s ease-in-out infinite}
.boot-lines{display:flex;flex-direction:column;gap:6px;font-size:.74rem}
.boot-line{display:flex;justify-content:space-between;gap:1rem;opacity:0;transform:translateY(6px);transition:opacity .25s ease,transform .25s var(--ease)}
.boot-line.in{opacity:1;transform:none}
.boot-line .nm{color:var(--text-soft)}
.boot-line .ok{color:var(--accent-ink);font-weight:600}
.boot-bar{margin-top:16px;height:3px;border-radius:3px;background:var(--surface-3);overflow:hidden}
.boot-bar i{display:block;height:100%;width:0;background:var(--grad);box-shadow:0 0 12px var(--accent-glow);transition:width .85s linear}
.boot-foot{margin-top:10px;font-size:.62rem;letter-spacing:.12em;color:var(--text-mute);text-transform:uppercase}
.boot.is-done{opacity:0;transform:translateY(-14px);pointer-events:none;transition:opacity .55s var(--ease),transform .55s var(--ease)}

/* ---- 3. Channel-ID eyebrows (every section = a telemetry channel) ---- */
.eyebrow--ch::before{display:none}
.eyebrow--ch .eb-id{font-family:var(--font-mono);font-weight:600;font-size:.72rem;letter-spacing:.08em;color:var(--accent-ink);border:1px solid var(--accent-line);border-radius:5px;padding:.1rem .4rem;display:inline-flex;align-items:center;gap:.34rem;background:var(--accent-soft)}
.eyebrow--ch .eb-id::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--accent);box-shadow:0 0 8px var(--accent-glow);animation:pulse 2s ease-in-out infinite}
.eyebrow--ch .eb-label{letter-spacing:.14em}

/* ---- 4. "Live demo →" arrow-slide + underline draw (featured cards) ---- */
.fc-link{position:relative;display:inline-flex;align-items:center;gap:.3rem}
.fc-link .fc-arr{display:inline-block;transition:transform .3s var(--ease)}
.fc-link::after{content:"";position:absolute;left:0;right:0;bottom:-3px;height:1.5px;background:var(--grad);border-radius:2px;transform:scaleX(0);transform-origin:right;transition:transform .35s var(--ease)}
.fc-link:hover::after{transform:scaleX(1);transform-origin:left}
.fc-link:hover .fc-arr{transform:translateX(5px)}
.fc-link:active .fc-arr{transform:translateX(9px)}

/* ---- 5. Tag pill-fill from the left (project + featured tags) ---- */
.fc-tags span,.pc-tags span{position:relative;overflow:hidden;z-index:0}
.fc-tags span{transition:color .3s var(--ease),border-color .3s var(--ease)}
.fc-tags span::before,.pc-tags span::before{content:"";position:absolute;inset:0;z-index:-1;background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform .34s var(--ease)}
.fc-tags span:hover,.pc-tags span:hover{color:var(--on-accent);border-color:var(--accent)}
.fc-tags span:hover::before,.pc-tags span:hover::before{transform:scaleX(1)}

/* ---- reduced motion: drop the moving parts, keep the static frame ---- */
@media (prefers-reduced-motion:reduce){
  .hud-corner{transition:none;opacity:.5}
  .boot{display:none!important}
  .boot-dot,.eyebrow--ch .eb-id::before{animation:none}
  .fc-link .fc-arr,.fc-link::after,.fc-tags span::before,.pc-tags span::before{transition:none}
}

/* =====================================================================
   TELEMETRY MICRO-INTERACTIONS · Wave 2  (quiet polish, pure CSS)
   HUD corner brackets that snap onto scanned cards · card-head icon wiggle.
   Brackets reuse each card's FREE pseudo (project/featured = ::before,
   since ::after is the spotlight). transform/opacity only.
   ===================================================================== */

/* ---- 6. HUD corner brackets snap onto project + featured cards ---- */
.project-card::before,.featured-card::before{content:"";position:absolute;inset:7px;z-index:4;pointer-events:none;opacity:0;transform:scale(1.04);transition:opacity .3s var(--ease),transform .3s var(--ease);background-repeat:no-repeat;background-image:linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent));background-size:12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px,12px 1.5px,1.5px 12px;background-position:left top,left top,right top,right top,left bottom,left bottom,right bottom,right bottom}
.project-card:hover::before,.featured-card:hover::before{opacity:.72;transform:scale(1)}

/* ---- 7. Card-head icons wiggle on card hover (fp-ic already animates) ---- */
.sc-icon,.ac-icon{transition:transform .4s var(--ease),box-shadow .4s var(--ease)}
.skill-card:hover .sc-icon,.about-card:hover .ac-icon{transform:rotate(-6deg) scale(1.08)}

@media (prefers-reduced-motion:reduce){
  .project-card::before,.featured-card::before,.sc-icon,.ac-icon{transition:none}
  .skill-card:hover .sc-icon,.about-card:hover .ac-icon{transform:none}
}

/* =====================================================================
   ABOUT SECTION · Wave 3 — editorial polish + intelligent annotation
   Clean resting state; key phrases get marker-highlighted as the section
   reveals (staggered via .lit / .in from telemetry.js) and replay on hover.
   REPL greeting · tag-icon wiggle · hobby focus-dim · quote-mark lift.
   ===================================================================== */

/* ---- 8. Kinetic keywords — the words do what they say ----
   (Drop-cap + REPL prompt retired — both broke the greeting.)
   "Hi" waves hello · "build" assembles letter by letter · "scalable"
   scales up · "distributed" fans out replicas · "responsive" flexes ·
   the craft list demos itself · "Formula 1" takes the apex with a
   speed blur. Each plays once as the section reveals (.play staggered
   in reading order by telemetry.js) and replays on hover. Dotted
   underlines mark the live words at rest. */
.about-copy .kw{display:inline-block}
.about-copy .kw-build,.about-copy .kw-resp{color:var(--text);font-weight:600;text-decoration:underline dotted var(--accent-line);text-decoration-thickness:1.5px;text-underline-offset:3px}
.about-copy .hi-wave{display:inline-block;font-weight:700;color:var(--accent-ink);transform-origin:72% 78%}
.lead-p:hover .hi-wave,.hi-wave.play{animation:kwWave .7s var(--ease)}
@keyframes kwWave{22%{transform:rotate(-13deg)}48%{transform:rotate(9deg)}74%{transform:rotate(-4deg)}}
.kw-build .kb-ch{display:inline-block}
.kw-build:hover .kb-ch,.kw-build.play .kb-ch{animation:kbRise .5s var(--ease) backwards}
.kw-build .kb-ch:nth-child(2){animation-delay:.06s}
.kw-build .kb-ch:nth-child(3){animation-delay:.12s}
.kw-build .kb-ch:nth-child(4){animation-delay:.18s}
.kw-build .kb-ch:nth-child(5){animation-delay:.24s}
@keyframes kbRise{from{transform:translateY(.45em);opacity:0}60%{transform:translateY(-.09em);opacity:1}}
.kw-scale:hover,.kw-scale.play{animation:kwScale .65s var(--ease)}
@keyframes kwScale{45%{transform:scale(1.18)}}
.kw-dist:hover,.kw-dist.play{animation:kwDist .8s var(--ease)}
@keyframes kwDist{38%{text-shadow:-9px 0 color-mix(in srgb,var(--accent) 38%,transparent),9px 0 color-mix(in srgb,var(--accent) 38%,transparent)}}
.kw-resp:hover,.kw-resp.play{animation:kwResp .7s var(--ease)}
@keyframes kwResp{30%{transform:scaleX(.84)}64%{transform:scaleX(1.08)}}
.kw-f1:hover,.kw-f1.play{animation:kwF1 .6s var(--ease)}
@keyframes kwF1{35%{transform:skewX(-10deg) translateX(2px);text-shadow:-9px 0 12px var(--accent-glow)}}
/* hero — "messy data" is literally messy at rest; hover (or the one-shot auto-demo
   after the hero settles) straightens every letter. His whole job, in one word. */
.hero-lead .kw-messy{display:inline-block;color:var(--text);font-weight:600;cursor:default}
.kw-messy .md-ch{display:inline-block;transition:transform .38s var(--ease)}
.kw-messy .md-ch:nth-child(1){transform:rotate(-7deg) translateY(1.5px)}
.kw-messy .md-ch:nth-child(2){transform:rotate(5deg) translateY(-1.5px)}
.kw-messy .md-ch:nth-child(3){transform:rotate(-4deg) translateY(2px)}
.kw-messy .md-ch:nth-child(4){transform:rotate(8deg) translateY(-1px)}
.kw-messy .md-ch:nth-child(5){transform:rotate(-6deg) translateY(1px)}
.kw-messy .md-ch:nth-child(6){transform:rotate(7deg) translateY(-2px)}
.kw-messy .md-ch:nth-child(7){transform:rotate(-8deg) translateY(1.5px)}
.kw-messy .md-ch:nth-child(8){transform:rotate(4deg) translateY(-1.5px)}
.kw-messy .md-ch:nth-child(9){transform:rotate(-5deg) translateY(1px)}
.kw-messy .md-ch:nth-child(2n){transition-delay:.045s}
.kw-messy .md-ch:nth-child(3n){transition-delay:.09s}
.kw-messy:hover .md-ch,.kw-messy.tidy .md-ch{transform:none}
/* hero — the deploy phrase draws its pipeline underline through to prod */
.hero-lead .kw-ship{color:var(--text);font-weight:600;background:linear-gradient(var(--accent),var(--accent)) no-repeat left 100%/0% 2px;transition:background-size .55s var(--ease),color var(--t);cursor:default}
.hero-lead .kw-ship:hover,.hero-lead .kw-ship.play{background-size:100% 2px;color:var(--accent-ink)}
/* hero — the promise line: every claim performs itself. "attention to detail"
   circles the dot of its own i · "subtle" all but vanishes when you look at
   it · "stands out" physically leaves the line. */
.hero-craft{margin:-14px 0 26px;font-size:clamp(.95rem,1.02vw,1.04rem);line-height:1.8;color:var(--text-soft);max-width:62ch;font-weight:500}
.hero-craft .hc{display:inline-block;font-weight:700;color:var(--text);cursor:default;transition:color var(--t),opacity .4s var(--ease),transform .3s var(--ease),text-shadow var(--t),background var(--t),box-shadow var(--t)}
/* attention to detail — accent-inked, with a permanently visible dot circling its own i (gentle idle pulse draws the eye), full on hover */
.hc-detail{position:relative;color:var(--accent-ink);text-decoration:underline dotted var(--accent-line);text-decoration-thickness:1.5px;text-underline-offset:3px}
.hc-detail .ttl::after{opacity:.6;transform:translate(-50%,-50%) scale(1);animation:hcDotPulse 2.6s var(--ease) infinite}
.hc-detail:hover .ttl::after,.hc-detail.play .ttl::after{animation:none}
@keyframes hcDotPulse{0%,100%{opacity:.38;transform:translate(-50%,-50%) scale(.88)}50%{opacity:.9;transform:translate(-50%,-50%) scale(1.1)}}
/* attention to detail — hover puts the phrase "under inspection": a dashed selection box snaps snug (1.12→1, focusing in) and a mono spec tag confirms it's pixel-perfect, i-dot brightens to full */
.hc-detail::before{content:"";position:absolute;inset:-3px -6px;border:1px dashed var(--accent-line);border-radius:6px;opacity:0;transform:scale(1.12);transform-origin:center;pointer-events:none;transition:opacity .22s var(--ease),transform .32s var(--ease),border-color var(--t)}
.hc-detail::after{content:"✓ pixel-perfect";position:absolute;left:-4px;bottom:calc(100% + 6px);font-family:var(--font-mono);font-size:.56rem;font-weight:600;letter-spacing:.03em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 88%,transparent);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border:1px solid var(--accent-line);border-radius:5px;padding:.14em .4em;white-space:nowrap;opacity:0;transform:translateY(4px) scale(.96);transform-origin:left bottom;pointer-events:none;box-shadow:0 8px 18px -10px var(--accent-glow);transition:opacity .22s var(--ease),transform .3s var(--ease)}
.hc-detail:hover,.hc-detail.play{color:var(--accent)}
.hc-detail:hover::before,.hc-detail.play::before{opacity:1;transform:scale(1)}
.hc-detail:hover::after,.hc-detail.play::after{opacity:1;transform:translateY(0) scale(1)}
/* subtle — a quiet accent chip: clearly a marked word, yet understated; recedes when you look straight at it */
.hc-subtle{font-style:italic;font-weight:600;color:var(--accent-ink);background:var(--accent-soft);border-radius:6px;padding:.04em .38em;text-decoration:none}
.hc-subtle:hover,.hc-subtle.play{opacity:.5;color:var(--text-mute);background:transparent}
/* stands out — lifted off the baseline in a solid gradient pill; lifts further on hover */
.hc-standout{font-weight:800;color:#fff;background:var(--grad);border-radius:7px;padding:.06em .44em;text-decoration:none;transform:translateY(-1px);box-shadow:0 7px 18px -8px var(--accent-glow)}
.hc-standout:hover,.hc-standout.play{transform:translateY(-4px);color:#fff;box-shadow:0 13px 28px -8px var(--accent-glow);text-shadow:0 0 18px var(--accent-glow)}
.about-quote .q-cite{display:block;width:fit-content;margin-top:10px;font-family:var(--font-mono);font-style:normal;font-weight:600;font-size:.63rem;letter-spacing:.14em;text-transform:uppercase;color:var(--text-mute);opacity:0;transform:translateY(4px);transition:opacity .5s var(--ease) .5s,transform .5s var(--ease) .5s}
.about-quote.in .q-cite,.about-quote:hover .q-cite{opacity:1;transform:none}
#about .about-quote .q-cite{transition:opacity .5s var(--ease) .5s,transform .5s var(--ease) .5s,background-color var(--t) var(--ease),color var(--t)}

/* ---- 9. Highlighter annotation on key phrases (sweeps in, replays on hover) ---- */
.about-quote .q-key{background-image:linear-gradient(transparent 58%,var(--accent-soft) 58%);background-repeat:no-repeat;background-position:0 0;background-size:0% 100%;transition:background-size .6s var(--ease),color .3s var(--ease)}
.about-quote.in .q-key,.about-quote:hover .q-key{background-size:100% 100%}
.about-copy .ink-highlight{background-image:linear-gradient(var(--accent-soft),var(--accent-soft));background-repeat:no-repeat;background-position:0 100%;background-size:0% .26em;transition:background-size .55s var(--ease),color var(--t),text-shadow var(--t)}
.about-copy .ink-highlight.lit,.about-copy .ink-highlight:hover{background-size:100% .26em}
.about-copy .ink-highlight:hover{color:var(--accent);text-shadow:0 0 14px var(--accent-glow)}

/* ---- 10. Identity pills → live telemetry credential chips ----
   Hover reveals a mono data-readout (set by telemetry.js via data-readout),
   floated above the pill so the row never reflows; the icon lights up.
   Resting state stays clean — no tooltip, no underline. */
.about-tag{position:relative;transition:border-color var(--t),color var(--t),transform var(--t),box-shadow var(--t) var(--ease)}
.about-tag i{transition:transform var(--t) var(--ease),color var(--t)}
.about-tag:hover i{transform:translateY(-1px) scale(1.14);color:var(--accent)}
.about-tag:hover{box-shadow:0 12px 26px -16px var(--accent-glow)}
.about-tag[data-readout]::after{content:attr(data-readout);position:absolute;left:50%;bottom:calc(100% + 9px);transform:translate(-50%,7px) scale(.96);transform-origin:bottom center;white-space:nowrap;font-family:var(--font-mono);font-size:.66rem;font-weight:600;letter-spacing:.01em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 90%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:1px solid var(--accent-line);border-radius:8px;padding:.34rem .6rem;opacity:0;pointer-events:none;z-index:6;box-shadow:0 14px 30px -16px var(--accent-glow);transition:opacity var(--t) var(--ease),transform var(--t) var(--ease)}
.about-tag[data-readout]:hover::after{opacity:1;transform:translate(-50%,0) scale(1)}

/* ---- 11. Quote-mark lifts + brightens on blockquote hover ---- */
.about-quote .q-mark{transition:opacity var(--t) var(--ease),transform var(--t) var(--ease)}
.about-quote:hover .q-mark{opacity:.72;transform:translateY(-2px)}

/* ---- 12. Hobbies grid — focus the hovered tile, settle the rest ---- */
.hobbies-grid-about .hobby{transition:transform var(--t) var(--ease),box-shadow var(--t) var(--ease),border-color var(--t),opacity var(--t) var(--ease),filter var(--t) var(--ease)}
.hobbies-grid-about .hobby i{transition:color var(--t),transform var(--t) var(--ease)}
.hobbies-grid-about:has(.hobby:hover) .hobby:not(:hover){opacity:.5;filter:saturate(.75)}
.hobbies-grid-about .hobby:hover i{transform:translateY(-2px) scale(1.1)}

/* ---- 13. "Your bio, parsed" — named entities reveal an NER type tag on hover ----
   Entities tagged by telemetry.js (data-ner). The prose reads like his own
   spaCy/displaCy entity output — clean at rest, lights into an entity box +
   a solid mono type label on hover. Demonstrates his NLP craft, not decoration. */
#about [data-ner]{position:relative;border-radius:5px;cursor:default;transition:background-color var(--t) var(--ease),color var(--t),box-shadow var(--t) var(--ease)}
#about [data-ner]:hover{background-color:var(--accent-soft);color:var(--accent-ink);box-shadow:0 0 0 1px var(--accent-line),0 8px 20px -12px var(--accent-glow)}
#about [data-ner]::after{content:attr(data-ner);position:absolute;left:0;bottom:calc(100% + 6px);font-family:var(--font-mono);font-size:.56rem;font-weight:700;letter-spacing:.08em;line-height:1;color:var(--on-accent);background:var(--accent);border-radius:4px;padding:.22rem .34rem;opacity:0;transform:translateY(5px) scale(.9);transform-origin:bottom left;pointer-events:none;white-space:nowrap;z-index:7;box-shadow:0 8px 18px -8px var(--accent-glow);transition:opacity var(--t) var(--ease),transform var(--t) var(--ease)}
#about [data-ner]:hover::after{opacity:1;transform:translateY(0) scale(1)}
/* identity-card TEXT entities live in a flex COLUMN (.ap-id) — without this they stretch to full card width, so the hover box + type label balloon across the row. Hug the text instead. (Scoped to .ap-id so the portrait, a flex item of the outer row, keeps its centered position.) */
#about .ap-id [data-ner]{align-self:flex-start;max-width:max-content}
/* PERSON = the profile photo: circular, brackets + label pinned to the photo's LOWER-LEFT corner (the look you wanted) */
#about .ap-portrait[data-ner]{border-radius:50%;cursor:pointer}
#about .ap-portrait[data-ner]::after{left:-4px;right:auto;top:auto;bottom:-2px;transform:translateY(4px) scale(.9);transform-origin:bottom left}
#about .ap-portrait[data-ner]:hover::after{transform:translateY(0) scale(1)}
/* Identity-card tags sit BESIDE their line, never above or below it.
   `.ap-id` is a flex column with `gap:3px`, but a tag is ~16px tall and is
   offset 6px clear of its element, so it needs ~22px of vertical room. It
   never had it: every tag landed on the NEIGHBOURING line. Hovering the role
   painted `ROLE` across the name, hovering the location painted `GPE` across
   the role, and the name's own tag (previously dropped below, to dodge the
   card's clipped top edge) painted `PERSON` across the role. Nothing was
   clipped and nothing was missing, which is exactly why it read as "hover
   isn't working" rather than as a layout bug — the tag was there, just on top
   of the wrong words.
   Sideways is the only direction with room: the widest row needs 393px of a
   491px well, so there is ~98px of slack. Re-measure that slack before making
   any `data-ner` string longer, and note `.ap-avail` keeps its own
   `[data-readout]` chip (higher specificity) — it sits below and clears. */
#about .ap-id [data-ner]::after{left:calc(100% + 8px);right:auto;top:50%;bottom:auto;transform:translate(-4px,-50%) scale(.92);transform-origin:left center}
#about .ap-id [data-ner]:hover::after{transform:translate(0,-50%) scale(1)}
/* Phone: the rows already fill the card (343px of 351px), so a tag beside them
   lands outside and `.about-profile{overflow:hidden}` eats it. There is no hover
   on touch anyway, so hide it — the same call already made on this breakpoint
   for the .ap-avail readout, the do-list proof chips and the edu-item chips. */
@media (max-width:575.98px){#about .ap-id [data-ner]::after{display:none}}

/* ---- 13b. Education entities — hug the text, and put the tag where there is room
   Two separate reports, one root cause each.

   (1) *"the education date box and stream name box is length on hover is too
   much"*. `.edu-body` is a flex COLUMN, so `.edu-deg` / `.edu-meta` stretch to the
   full well and the hover box balloons far past the words — measured at 1440, the
   DATE box was **381.2px for 131.3px of text**, i.e. 2.9x the text and 250px of
   empty highlight; DEGREE was 381.2 for 235.7. This is the same bug as the
   `.ap-id` one above and takes the same fix. `align-self:flex-start` sizes them to
   fit-content, so they still wrap normally when the well is narrower than the text.
   ⚠ The `>` is load-bearing. `.edu-top` is also a child of `.edu-body`, and its
     `strong` is a flex item of a ROW — there `align-self` is the VERTICAL axis and
     would break the org name's centring against the CGPA chip. */
#about .edu-body > [data-ner]{align-self:flex-start;max-width:max-content}

/* (2) *"some of the ner are overflowing and cutoff"*. The tags had nowhere to go
   UP: the three lines sit **1.9–4.3px** apart (`.edu-body{gap:2px}`) and a tag is
   16px tall + 6px clear = **22px**, so each one landed on the line ABOVE — DATE
   painted across the degree, DEGREE across the org name. Precisely the failure
   already documented for `.ap-id`, and the same answer: sideways. Slack after
   hugging is 145px (DEGREE) / 250px (DATE) at 1440 against 47px / 35px of tag. */
#about .edu-body > [data-ner]::after{left:calc(100% + 8px);right:auto;top:50%;bottom:auto;transform:translate(-4px,-50%) scale(.92);transform-origin:left center}
#about .edu-body > [data-ner]:hover::after{transform:translate(0,-50%) scale(1)}

/* ⚠ ORG IS THE EXCEPTION AND MUST STAY ABOVE — do not "make it consistent".
   Its line is `.edu-top`, a `space-between` row whose right end is the CGPA chip,
   so beside the LONG university name there are only **13.6px** at 1440 against a
   37px need; it never fits at any two-column width. Above was the only direction
   left, and above did not fit either: the card gave the tag **18px** where it
   needs **26**, so `.edu-item{overflow:hidden}` (which exists for the ::before
   bar's rounded corners, §edu) sliced 4px off its top. That slice is the "cut off"
   in the report — see the padding-top note at `#about .edu-item`. */

/* Phone: same call already made for `.ap-id` — no hover on touch, the well is
   full-width so nothing fits beside, and `overflow:hidden` would eat it anyway. */
@media (max-width:575.98px){#about .edu-body > [data-ner]::after{display:none}}

/* ⚠ Bootstrap `lg` ONLY. Between `lg` and `xl` the about section is two columns
   and the education well collapses to **254px** — the degree line's own text is
   236–245px of that, leaving **9–19px** beside it for a 47px tag, which the card
   would clip. It returns at `xl`, where the well reaches 313px (68px of slack),
   and below `lg` the section is one column with a 603px well where everything
   fits. DATE keeps its tag throughout (123px of slack even at the squeeze).
   Re-measure this band before lengthening the degree copy. */
@media (min-width:992px) and (max-width:1199.98px){#about .edu-deg[data-ner]::after{display:none}}

/* ---- 14. One-shot "parse" scan-line sweeps down the bio on reveal ---- */
.about-copy{position:relative}
.about-copy::before{content:"";position:absolute;left:-10px;right:-10px;top:0;height:2px;background:linear-gradient(90deg,transparent,var(--accent),transparent);box-shadow:0 0 16px 2px var(--accent-glow);opacity:0;pointer-events:none;z-index:5}
.about-copy.parsing::before{animation:nerParse 1.5s var(--ease) .15s}
@keyframes nerParse{0%{opacity:0;top:0}10%{opacity:.9}88%{opacity:.9}100%{opacity:0;top:100%}}

@media (prefers-reduced-motion:reduce){
  .about-quote .q-key{transition:none;background-size:100% 100%}
  .about-quote .q-cite,#about .about-quote .q-cite{transition:none;opacity:1;transform:none}
  .craft-detail .ttl,.craft-detail .ttl::after{transition:none}
  .craft-design::before,.craft-fit::before,.craft-fit::after{transition:none}
  .about-hobby-note em:has(.craft:hover) .craft:not(:hover){opacity:1}
  .kw-messy .md-ch:nth-child(n){transform:none;transition:none}
  .hero-lead .kw-ship{transition:none}
  .hero-craft .hc,.hc-detail .ttl{transition:none}
  .hc-detail .ttl::after{transition:none;animation:none;opacity:.6;transform:translate(-50%,-50%) scale(1)}
  .hc-detail::before,.hc-detail::after{transition:none}
  .section-head.in-view[data-sec]::before{animation:none}
  .lead-p:hover .hi-wave,.hi-wave.play,.kw-build:hover .kb-ch,.kw-build.play .kb-ch,.kw-scale:hover,.kw-scale.play,.kw-dist:hover,.kw-dist.play,.kw-resp:hover,.kw-resp.play,.kw-f1:hover,.kw-f1.play{animation:none}
  .about-copy .ink-highlight{transition:none;background-size:100% .26em}
  #about [data-ner],#about [data-ner]::after{transition:none}
  .about-copy.parsing::before{animation:none}
  .about-tag i,.about-tag[data-readout]::after,.about-quote .q-mark,.hobbies-grid-about .hobby,.hobbies-grid-about .hobby i{transition:none}
  .hobbies-grid-about:has(.hobby:hover) .hobby:not(:hover){opacity:1;filter:none}
}

/* =====================================================================
   ABOUT · Wave 3.2 — right column (same "parsed profile" idea)
   Portrait gets CV subject-detection; the do-list capabilities + education
   records are NER-tagged in telemetry.js and reuse #about [data-ner] above.
   ===================================================================== */

/* ---- 15. Profile portrait → subject detection (brackets + PERSON tag) ---- */
.ap-portrait::before{content:"";position:absolute;inset:-5px;z-index:3;pointer-events:none;opacity:0;transform:scale(1.07);transition:opacity .35s var(--ease),transform .35s var(--ease);background-repeat:no-repeat;background-image:linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent)),linear-gradient(var(--accent),var(--accent));background-size:11px 1.5px,1.5px 11px,11px 1.5px,1.5px 11px,11px 1.5px,1.5px 11px,11px 1.5px,1.5px 11px;background-position:left top,left top,right top,right top,left bottom,left bottom,right bottom,right bottom}
.ap-portrait::after{content:"PERSON";position:absolute;left:50%;bottom:1px;transform:translate(-50%,4px);z-index:4;font-family:var(--font-mono);font-size:.5rem;font-weight:700;letter-spacing:.1em;color:var(--on-accent);background:var(--accent);border-radius:3px;padding:.12rem .32rem;opacity:0;pointer-events:none;box-shadow:0 6px 14px -6px var(--accent-glow);transition:opacity .3s var(--ease),transform .3s var(--ease)}
/* SUBJECT DETECTION FIRES ON THE PORTRAIT, NOT ON THE CARD.
   These three were keyed to `.about-profile:hover`, so hovering the NAME or the
   ROLE — anywhere in the card — snapped the detection brackets and the PERSON
   tag onto the photo. The card is a parsed record: hovering a field should
   annotate THAT field, and pointing at text that says "AI/ML Engineer" while
   the photo lights up reads as the page mis-detecting what you are pointing at.
   The card keeps its own lift/border on hover (`.about-profile:hover` above);
   only the per-field annotations moved. */
.ap-portrait:hover::before{opacity:.85;transform:scale(1)}
.ap-portrait:hover::after{opacity:1;transform:translate(-50%,0)}
.ap-portrait:hover .ap-ring{animation-duration:3s}

@media (prefers-reduced-motion:reduce){
  .ap-portrait::before,.ap-portrait::after{transition:none}
  .ap-portrait:hover .ap-ring{animation-duration:9s}
}

/* =====================================================================
   SKILLS · Wave 4 — the stack is a SYSTEM, not a list
   Hover any tool / concept / focus area and its whole ecosystem lights up
   across the section while the rest recede — a live dependency graph of his
   stack. telemetry.js skillsGraph tags nodes + toggles .graph-act/.graph-rel
   on #skills. Demonstrates the systems thinking the section is about. */
#skills .skill-chip,#skills .chip,#skills .focus-pillar{transition:opacity var(--t) var(--ease),filter var(--t) var(--ease),transform var(--t) var(--ease),box-shadow var(--t) var(--ease),border-color var(--t)}
#skills.graph-active .skill-chip:not(.graph-rel):not(.graph-act),#skills.graph-active .chip:not(.graph-rel):not(.graph-act),#skills.graph-active .focus-pillar:not(.graph-rel):not(.graph-act){opacity:.34;filter:saturate(.7)}
#skills .graph-rel{border-color:var(--accent)!important;transform:translateY(-2px);box-shadow:0 10px 22px -14px var(--accent-glow)}
#skills .graph-act{border-color:var(--accent)!important;transform:translateY(-3px);box-shadow:0 0 0 1px var(--accent-line),0 14px 28px -14px var(--accent-glow)}
@media (prefers-reduced-motion:reduce){
  #skills .skill-chip,#skills .chip,#skills .focus-pillar{transition:none}
  #skills .graph-rel,#skills .graph-act{transform:none}
}

/* ---- Bold per-tool payoff (no lines): hover energizes the tool + reveals its spec ----
   Each chip pops with an accent glow + a logo kick, and floats a mono one-liner of
   what the tool actually is (data-spec set by telemetry.js skillsSpecs). Layers on
   the cluster-brighten above. The hover-loved "readout" pattern, made bolder. */
#skills .skill-chip,#skills .chip{position:relative}
#skills .skill-chip img{transition:transform var(--t) var(--ease)}
#skills .skill-chip:hover,#skills .chip:hover{transform:translateY(-4px) scale(1.07);border-color:var(--accent);background:color-mix(in srgb,var(--accent) 10%,var(--surface-2));box-shadow:0 0 0 1px var(--accent-line),0 16px 32px -14px var(--accent-glow);z-index:6}
#skills .skill-chip:hover img{transform:scale(1.2) rotate(-6deg)}
#skills [data-spec]::after{content:attr(data-spec);position:absolute;left:50%;bottom:calc(100% + 8px);transform:translate(-50%,7px) scale(.95);transform-origin:bottom center;font-family:var(--font-mono);font-size:.62rem;font-weight:600;letter-spacing:.01em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 92%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:1px solid var(--accent-line);border-radius:7px;padding:.3rem .52rem;white-space:nowrap;opacity:0;pointer-events:none;z-index:9;box-shadow:0 14px 30px -16px var(--accent-glow);transition:opacity var(--t) var(--ease),transform var(--t) var(--ease)}
#skills [data-spec]:hover::after{opacity:1;transform:translate(-50%,0) scale(1)}
@media (prefers-reduced-motion:reduce){
  #skills .skill-chip:hover,#skills .chip:hover{transform:none}
  #skills .skill-chip:hover img{transform:none}
  #skills .skill-chip img,#skills [data-spec]::after{transition:none}
}

/* =====================================================================
   HERO · Wave 5 — the pipeline actually RUNS (live ML telemetry)
   The LIVE Evaluate card was frozen; now the metrics breathe around the
   trained value, each sparkline keeps a live read-head at its latest point,
   and the terminal answers `whoami`. Subtle. Pairs with telemetry.js heroLive. */

/* live metric — soft glow pulse each time the value refreshes */
.wb-metric-val.tick{animation:wbTick .55s var(--ease)}
@keyframes wbTick{30%{text-shadow:0 0 13px var(--accent-glow)}100%{text-shadow:0 0 0 transparent}}

/* sparkline live read-head — a pulsing dot parked at the latest value (SVG circle, injected) */
.wb-head{fill:var(--accent);opacity:0;filter:drop-shadow(0 0 3px var(--accent));animation:wbHead 1.8s ease-in-out 2.4s infinite}
@keyframes wbHead{0%,100%{opacity:.45}50%{opacity:1}}

/* terminal answers `whoami` (typed by JS) */
.hero-terminal{flex-wrap:wrap}
.term-answer{flex-basis:100%;display:flex;align-items:center;gap:2px;margin-top:.28rem;font-family:var(--font-mono);font-size:.82rem;color:var(--accent-ink);font-weight:600}
.term-answer::before{content:"";width:7px;height:7px;margin-right:.35rem;border-radius:2px;background:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);transform:rotate(45deg);flex-shrink:0}
.hero-terminal:hover .term-answer::before{animation:termPixel .7s var(--ease) both}
.term-caret{display:inline-block;width:6px;height:.95em;border-radius:1px;background:var(--accent);animation:termBlink 1s steps(1) infinite}
@keyframes termBlink{50%{opacity:0}}
@keyframes termPixel{50%{transform:rotate(45deg) scale(1.35);box-shadow:0 0 0 5px transparent,0 0 10px var(--accent-glow)}}

@media (prefers-reduced-motion:reduce){
  .wb-metric-val.tick{animation:none}
  .wb-head{animation:none;opacity:0}
  .hero-terminal,.hero-terminal::before,.hero-terminal::after,.hero-terminal .term-path{transition:none}
  .hero-terminal:hover{transform:none}
  .hero-terminal:hover .term-answer::before{animation:none}
  .term-caret{animation:none}
}

/* =====================================================================
   HERO · Wave 6 — "Attention" : the name responds to the cursor
   The giant name is the hero's centerpiece but sat static. Each letter is a
   token, the cursor is the query: letters bloom to full weight where you point
   and ease back to light elsewhere — a live attention distribution over his own
   name (he's an NLP / Transformers engineer; the headline does the thing the
   page is about). Variable-weight only (Plus Jakarta Sans 200..800), no colour,
   no transform, no layout jump beyond the natural type breathing. JS splits the
   letters + drives the weight (telemetry.js heroName); the accent dot keeps a
   soft "inference" pulse. */
.hero-name{font-synthesis-weight:none}
.hn-ch{display:inline-block;font-variation-settings:'wght' 700,'opsz' 144,'SOFT' 12,'WONK' 1;transition:font-variation-settings .2s var(--ease),transform .25s var(--ease)}
.text-grad .hn-ch{background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
.hero-dot{animation:heroDotPulse 2.4s ease-in-out infinite}
@keyframes heroDotPulse{0%,100%{opacity:.82;text-shadow:0 0 0 transparent}50%{opacity:1;text-shadow:0 0 15px var(--accent-glow)}}
@media (prefers-reduced-motion:reduce){.hn-ch{transition:none;font-variation-settings:'wght' 700,'opsz' 144,'SOFT' 12,'WONK' 1;transform:none}.hero-dot{animation:none}}

/* =====================================================================
   HERO · Wave 7 — the hero is an inspectable production console
   Hover any signal in the hero and it reveals the REAL record behind it,
   reusing the loved .about-tag[data-readout] chip. The pipeline stages
   already drill into their eval/SLO strip (spark.js .pipe-detail); this
   wave completes the LEFT column: lead-keys → the real stack behind each
   buzzword, the stat tiles → their provenance (the bold moment: the KPI
   lights up + the rest recede), the status line → an uplink fact. All blue
   tokens, transform/opacity only, fully reduced-motion guarded. data-readout
   is set by telemetry.js heroConsole; cursor.js already suppresses its own
   curio term on [data-readout]. Pairs with the removed .lead-key glitch. */

/* shared readout chip — centered above the element (clone of .about-tag[data-readout]) */
.hero-lead .lead-key[data-readout]::after,.hero-stats div[data-readout]::after{content:attr(data-readout);position:absolute;left:50%;bottom:calc(100% + 9px);transform:translate(-50%,7px) scale(.96);transform-origin:bottom center;white-space:nowrap;font-family:var(--font-mono);font-size:.66rem;font-weight:600;letter-spacing:.01em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 92%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:1px solid var(--accent-line);border-radius:8px;padding:.34rem .6rem;opacity:0;pointer-events:none;z-index:9;box-shadow:0 14px 30px -16px var(--accent-glow);transition:opacity var(--t) var(--ease),transform var(--t) var(--ease)}
.hero-lead .lead-key[data-readout]:hover::after{opacity:1;transform:translate(-50%,0) scale(1)}
@media (hover:hover) and (pointer:fine){.hero-stats div[data-readout]:hover::after{opacity:1;transform:translate(-50%,0) scale(1)}}
.hero-lead .lead-key:hover{z-index:9}

/* stat tiles — the bold moment: the KPI lights up + reveals its source, the rest recede.
   :has lifts the dl's resting overflow:hidden only on hover so the chip can escape upward.
   ⚠ THE WHOLE SET IS GATED BEHIND A FINE POINTER. On a touch screen :hover
     STICKS after a tap, so tapping a tile left the rail permanently dimmed with
     a nowrap readout chip parked over the row above it and no way to dismiss it.
     A hover affordance on a device that cannot hover is not a decoration, it is
     a stuck state. Keep every hover-dependent declaration inside this block. */
.hero-stats > div{position:relative;transition:box-shadow var(--t) var(--ease),opacity var(--t) var(--ease)}
.hero-stats dt{transition:transform var(--t) var(--ease)}
@media (hover:hover) and (pointer:fine){
.hero-stats:has(div:hover){overflow:visible}
.hero-stats > div:hover{box-shadow:inset 0 0 0 1px var(--accent-line),inset 0 0 26px -12px var(--accent-glow);z-index:8}
.hero-stats:has(div:hover) > div:not(:hover){opacity:.5}
.hero-stats > div:hover dt{transform:scale(1.08)}
.hero-stats > div:hover dt:not([data-text]){background:var(--grad);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent}
}

/* status line — quiet uplink readout (the blue .status-dot pulse is untouched; no new green) */
.hero-status{position:relative}
.hero-status:hover{color:var(--text-soft)}
.hero-status[data-readout]::after{content:attr(data-readout);position:absolute;left:0;bottom:calc(100% + 9px);transform:translate(0,7px) scale(.97);transform-origin:bottom left;white-space:nowrap;font-family:var(--font-mono);font-size:.66rem;font-weight:600;letter-spacing:.01em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 92%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:1px solid var(--accent-line);border-radius:8px;padding:.34rem .6rem;opacity:0;pointer-events:none;z-index:9;box-shadow:0 14px 30px -16px var(--accent-glow);transition:opacity var(--t) var(--ease),transform var(--t) var(--ease)}
.hero-status[data-readout]:hover::after{opacity:1;transform:translate(0,0) scale(1)}

@media (prefers-reduced-motion:reduce){
  .hero-lead .lead-key[data-readout]::after,.hero-stats div[data-readout]::after,.hero-stats > div,.hero-stats dt,.hero-status[data-readout]::after{transition:none}
  .hero-stats:has(div:hover) > div:not(:hover){opacity:1}
  .hero-stats > div:hover dt{transform:none}
}

/* =====================================================================
   FEATURED · Wave 8 — each featured card is a DEPLOYED SYSTEM
   The three cards are real, live products. On hover a thin mono deployment
   strip slides up over the bottom of the screenshot — the hero pipeline's
   "Deployed to prod" language, carrying each app's real stack + host.
   Reuses the .fc-badge dark-glass (cohesive image overlay) + a blue accent
   "live" dot + accent hairline. Injected child .fc-deploy by telemetry.js
   (#13 featuredDeploy); the card's ::before scan / ::after spotlight are
   left untouched. transform/opacity only · reduced-motion guarded. */
.fc-deploy{position:absolute;left:0;right:0;bottom:0;z-index:4;display:flex;align-items:center;gap:.45rem;height:25px;padding:0 .72rem;font-family:var(--font-mono);font-size:.645rem;font-weight:600;letter-spacing:.015em;line-height:1;white-space:nowrap;overflow:hidden;color:#fff;background:rgba(8,12,20,.82);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);border-top:1px solid color-mix(in srgb,var(--accent) 60%,transparent);transform:translateY(101%);transition:transform .34s var(--ease);pointer-events:none}
.fc-deploy .fc-dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 7px var(--accent-glow);flex-shrink:0;animation:fcPulse 2s ease-in-out infinite}
.fc-deploy b{font-weight:700;letter-spacing:.05em;color:color-mix(in srgb,var(--accent) 30%,#fff)}
.fc-deploy .fc-stack{min-width:0;overflow:hidden;text-overflow:ellipsis;font-weight:500;color:rgba(255,255,255,.78)}
.featured-card:hover .fc-deploy{transform:translateY(0)}
@keyframes fcPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.8)}}
@media (prefers-reduced-motion:reduce){.fc-deploy{transition:none}.fc-deploy .fc-dot{animation:none}}

/* =====================================================================
   ALL PROJECTS · Wave 9 — every card is a repo/deployment record
   Subtle sibling of Featured · Wave 8. On hover a thin light-glass mono
   strip slides up over the card's screenshot with the REAL record:
   status · host · stack depth — derived from each card's own primary
   action link + tags by telemetry.js (#14 projectMeta). Reuses the loved
   readout aesthetic (surface-glass + accent-line + accent-ink), lighter
   than the featured dark strip so the grid stays calm. Injected child
   .pc-meta; ::before (brackets/scan) + ::after (spotlight) untouched.
   transform/opacity only · reduced-motion guarded. */
.pc-media{position:relative}
.pc-meta{position:absolute;left:0;right:0;bottom:0;z-index:3;display:flex;align-items:center;gap:.4rem;height:22px;padding:0 .6rem;font-family:var(--font-mono);font-size:.6rem;font-weight:600;letter-spacing:.01em;line-height:1;white-space:nowrap;overflow:hidden;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 90%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border-top:1px solid var(--accent-line);transform:translateY(101%);transition:transform .3s var(--ease);pointer-events:none}
.pc-meta .pc-dot{width:5px;height:5px;border-radius:50%;background:var(--accent);box-shadow:0 0 6px var(--accent-glow);flex-shrink:0;animation:pcMetaPulse 2s ease-in-out infinite}
.pc-meta b{font-weight:700;letter-spacing:.04em;color:var(--accent-ink)}
.pc-meta .pc-host{min-width:0;overflow:hidden;text-overflow:ellipsis;font-weight:500;color:var(--text-soft)}
.project-card:hover .pc-meta{transform:translateY(0)}
@keyframes pcMetaPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.8)}}
@media (prefers-reduced-motion:reduce){.pc-meta{transition:none}.pc-meta .pc-dot{animation:none}}

/* =====================================================================
   PROJECTS · Wave 10 — the archive knows itself
   The grid behaves as a live recommender + classifier whose training set
   IS the 13 cards on the page. Three payoffs, all reading the cards'
   OWN content (no fake data, no extra copy):
     a) .pc-path — every card gets a mono parsed-path readout under the
        title (ml.nlp · transformers · spaCy), derived from .pc-cat + 2
        most distinctive tags. Brightens with the card.
     b) k-NN siblings on hover — weighted Jaccard over each card's own
        tags picks top-3 nearest projects; siblings brighten + their
        SHARED tag pills fill blue, everything else dims to .42. The
        constellation reveals the engineer's discipline on the engineer's
        own portfolio. NO LINES — opacity only.
     c) Classifier-pass on filter click — every card flashes a corner
        predicted-class badge (.proj-class : "ml ✓" / "ml ✗") for ~420ms
        before main.js's display:none hides the non-matches; .proj-flash
        keeps all cards visible for the duration.
   .project-card::before (HUD brackets) and ::after (spotlight) are both
   already taken, so .pc-path and .proj-class are real DOM children
   (injected by telemetry.js #15 projectsAware). transform/opacity only
   · reduced-motion guarded. */

/* a) the parsed self-classified path — sits under .pc-cat + h4 */
.pc-path{font-family:var(--font-mono);font-size:.6rem;font-weight:600;letter-spacing:.045em;color:var(--accent-ink);opacity:.55;line-height:1.3;margin-top:-.18rem;transition:opacity .26s var(--ease),color .26s var(--ease),letter-spacing .26s var(--ease)}
.project-card:hover .pc-path,.project-col.proj-act .pc-path,.project-col.proj-rel .pc-path{opacity:1;letter-spacing:.06em}

/* b) hover-aware grid — dim non-siblings, brighten me + my k-NN siblings */
#projectsGrid .project-col{transition:opacity .28s var(--ease),transform .32s var(--ease)}
#projectsGrid.proj-aware .project-col{opacity:.42}
#projectsGrid.proj-aware .project-col.proj-act,#projectsGrid.proj-aware .project-col.proj-rel{opacity:1}
#projectsGrid.proj-aware .project-col.proj-act{z-index:6}
#projectsGrid.proj-aware .project-col.proj-rel{z-index:5}

.project-col.proj-act .project-card{border-color:var(--accent)!important;box-shadow:0 22px 44px -18px var(--accent-glow),0 0 0 1px var(--accent)}
.project-col.proj-rel .project-card{border-color:var(--accent)!important;box-shadow:0 14px 30px -16px var(--accent-glow),0 0 0 1px var(--accent-line);transform:translateY(-3px)!important}

/* siblings reveal their pc-meta strip too — mirrors the existing :hover behavior, so the kinship reads */
.project-col.proj-rel .pc-meta,.project-col.proj-act .pc-meta{transform:translateY(0)}

/* the constellation payoff: SHARED tags pill-fill accent blue inside every sibling */
.pc-tags > span.proj-shared{background:var(--accent)!important;color:#fff!important;border-color:var(--accent)!important;font-weight:600;transform:translateY(-1px);box-shadow:0 6px 14px -8px var(--accent-glow);transition:background .25s var(--ease),color .25s var(--ease),border-color .25s var(--ease),transform .25s var(--ease)}

/* c) classifier-pass badge: keep ALL cards visible during the ~420ms flash so user can SEE the prediction on every card */
#projectsGrid.proj-flash .project-col{display:block!important;opacity:1!important}
#projectsGrid.proj-flash .project-col.proj-act,#projectsGrid.proj-flash .project-col.proj-rel{opacity:1!important}
.proj-class{position:absolute;top:8px;right:8px;z-index:9;padding:.22rem .42rem;font-family:var(--font-mono);font-size:.6rem;font-weight:700;letter-spacing:.07em;border-radius:5px;text-transform:uppercase;pointer-events:none;opacity:0;transform:translateY(-3px) scale(.95);transition:opacity .18s var(--ease),transform .22s var(--ease),background .22s var(--ease),color .22s var(--ease),border-color .22s var(--ease);background:color-mix(in srgb,var(--surface) 92%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:1px solid var(--accent-line);color:var(--accent-ink);white-space:nowrap}
#projectsGrid.proj-flash .proj-class{opacity:1;transform:translateY(0) scale(1)}
#projectsGrid.proj-flash .proj-class[data-match="y"]{background:var(--accent);color:#fff;border-color:var(--accent);box-shadow:0 6px 14px -8px var(--accent-glow)}
#projectsGrid.proj-flash .proj-class[data-match="n"]{opacity:.42}

@media (prefers-reduced-motion:reduce){
  #projectsGrid .project-col,.pc-path,.pc-tags > span.proj-shared,.proj-class{transition:none}
  .project-col.proj-rel .project-card,.pc-tags > span.proj-shared{transform:none}
}

/* =====================================================================
   CERTIFICATIONS · Wave 11 — verifiable credentials wallet
   Each .cert-card behaves as a W3C-style verifiable credential being
   inspected. ALL VALUES ARE REAL — issuer is parsed from the logo alt,
   the credential ID is extracted from the cert's actual href path
   (learn.microsoft.com/.../F6278D004A6EA9BF → CRED-F6278D00,
   credly.com/badges/1F090AAE-... → CRED-1F090AAE,
   datacamp.com/certificate/AIEDA001... → CRED-AIEDA001). On hover:
     a) issuer constellation — cards sharing data-issuer brighten
        (Microsoft × 2 light up together, GitHub × 2 together, etc.);
        everyone else dims to .45. NO LINES — opacity + lift only.
     b) the .cert-vc strip slides down between .cert-head and .cert-media
        showing "◢ vc · ISSUER · CRED-{real-id} · ✓ AUTH".
     c) the View-certificate footer gains "↗ verify.{host}" prefix
        (learn.microsoft.com / credly.com / datacamp.com — real hosts).
     d) one-shot blue scan sweep across .cert-media (the badge image).
   Pseudo map: .cert-card::before = holo sheen (taken, untouched).
   .cert-vc and .vc-host are real DOM children injected by JS;
   .cert-media::after is free, used for the scan. transform/opacity
   only · reduced-motion guarded. JS in telemetry.js #16 certVerify. */

/* a) the vc strip — accordions open below cert-head on hover or cred-rel */
.cert-vc{display:flex;align-items:center;gap:.4rem;height:0;overflow:hidden;padding:0 18px;font-family:var(--font-mono);font-size:.62rem;font-weight:600;letter-spacing:.05em;color:var(--accent-ink);background:color-mix(in srgb,var(--accent) 7%,var(--surface));border-top:1px solid transparent;transition:height .28s var(--ease),padding .28s var(--ease),border-color .28s var(--ease);white-space:nowrap}
.cert-card:hover .cert-vc,.cert-card.cred-rel .cert-vc,.cert-card.cred-act .cert-vc{height:26px;padding:6px 18px;border-top-color:var(--accent-line)}
.cert-vc .vc-icon{color:var(--accent);font-size:.7rem;line-height:1}
.cert-vc .vc-key{color:var(--text-mute);text-transform:lowercase;font-weight:500}
.cert-vc .vc-sep{color:var(--text-mute);opacity:.45}
.cert-vc .vc-issuer{color:var(--accent-ink);font-weight:700;letter-spacing:.07em}
.cert-vc .vc-cred{color:var(--text-soft);font-weight:600}
.cert-vc .vc-auth{margin-left:auto;color:var(--accent);font-weight:700;letter-spacing:.08em;opacity:0;transform:translateX(6px);transition:opacity .25s var(--ease) .15s,transform .25s var(--ease) .15s}
.cert-card:hover .cert-vc .vc-auth,.cert-card.cred-rel .cert-vc .vc-auth,.cert-card.cred-act .cert-vc .vc-auth{opacity:1;transform:translateX(0)}

/* b) the verify.{host} prefix in the footer — slides in left of the existing label */
.cert-foot > span{display:inline-flex;align-items:center;gap:.5rem;font-family:inherit}
.cert-foot .vc-host{font-family:var(--font-mono);font-size:.65rem;font-weight:600;letter-spacing:.04em;color:var(--accent);background:color-mix(in srgb,var(--accent) 12%,transparent);padding:.18rem .42rem;border-radius:5px;border:1px solid var(--accent-line);opacity:0;max-width:0;overflow:hidden;white-space:nowrap;transform:translateX(-4px);transition:opacity .25s var(--ease),max-width .3s var(--ease),transform .25s var(--ease)}
.cert-card:hover .vc-host,.cert-card.cred-rel .vc-host,.cert-card.cred-act .vc-host{opacity:1;max-width:240px;transform:translateX(0)}

/* c) issuer constellation — dim non-issuer-siblings; brighten me + same-issuer kin */
#certGrid .col-lg-4{transition:opacity .28s var(--ease)}
#certGrid.cred-aware .col-lg-4{opacity:.45}
#certGrid.cred-aware .col-lg-4:has(.cred-act),#certGrid.cred-aware .col-lg-4:has(.cred-rel){opacity:1}
.cert-card.cred-act{border-color:var(--accent)!important;box-shadow:0 22px 44px -18px var(--accent-glow),0 0 0 1px var(--accent)!important}
.cert-card.cred-rel{border-color:var(--accent)!important;box-shadow:0 16px 32px -16px var(--accent-glow),0 0 0 1px var(--accent-line)!important;transform:translateY(-3px)!important}

/* d) one-shot scan across the cert badge image on hover entry */
.cert-media{position:relative}
.cert-media::after{content:"";position:absolute;inset:0;pointer-events:none;background:linear-gradient(105deg,transparent 30%,color-mix(in srgb,var(--accent) 28%,transparent) 48%,color-mix(in srgb,#fff 50%,transparent) 52%,color-mix(in srgb,var(--accent) 28%,transparent) 56%,transparent 72%);background-size:240% 100%;background-position:140% 0;opacity:0;mix-blend-mode:overlay}
.cert-card:hover .cert-media::after{opacity:1;animation:vcScan .85s ease}
@keyframes vcScan{from{background-position:140% 0}to{background-position:-60% 0}}

@media (prefers-reduced-motion:reduce){
  .cert-vc,.cert-vc .vc-auth,.cert-foot .vc-host,#certGrid .col-lg-4,.cert-card.cred-rel,.cert-media::after{transition:none;animation:none}
  .cert-card.cred-rel{transform:none!important}
  .cert-media::after{opacity:0}
}

/* =====================================================================
   LEARNING · Wave 12 — tune in to the channel
   Each .platform tile in #extras broadcasts a "frequency": at rest a
   quiet accent LED pulses in its top-right corner — competitive-prog
   tiles pulse faster (1.5s) than online-learning ones (2.4s), giving
   the panel a subtle living signal. On hover any tile:
     a) the LED locks to steady (tuned in) + slightly scales up
     b) a soft accent scan sweeps the tile face once (.platform::before)
     c) a mono one-liner floats just below the tile, anchored to
        attr(data-spec) via .platform::after (e.g. LeetCode →
        "daily problems · DSA · interview prep" — same readout
        pattern that Skills · Wave 4 uses, so the site feels native)
     d) the OTHER row dims to .55 via :has() — the kinship reads
        without lines (Competitive vs Online Learning), but
        sister tiles in the same row stay full-bright.
   .panel{overflow:hidden} is conditionally lifted to visible on
   any tile hover so the readout that floats below the tile can
   escape the panel. cursor.js already suppresses its curio AI/ML
   term on [data-spec] so the new readout never double-labels.
   transform/opacity only · reduced-motion guarded. JS in
   telemetry.js #17 platformsTune. */

/* lift the panel's overflow only when a tile is hovered, so the floating readout escapes */
#extras .panel:has(.platform:hover){overflow:visible}

/* row kinship — hover competitive dims learning, and vice versa */
.platform{transition:opacity .28s var(--ease),transform var(--t-fast) var(--ease),box-shadow var(--t-fast) var(--ease),border-color var(--t)}
#extras .panel:has(.platform[data-track="competitive"]:hover) .platform[data-track="learning"]{opacity:.55}
#extras .panel:has(.platform[data-track="learning"]:hover) .platform[data-track="competitive"]{opacity:.55}

/* status LED — quiet pulsing accent dot, top-right corner of every tile */
.platform .pt-led{position:absolute;top:10px;right:10px;width:6px;height:6px;border-radius:50%;background:var(--accent);opacity:.4;box-shadow:0 0 5px var(--accent-glow);animation:ptPulse 2.4s ease-in-out infinite;pointer-events:none;z-index:2;transition:opacity .25s var(--ease),transform .25s var(--ease)}
.platform[data-track="competitive"] .pt-led{animation-duration:1.5s}
.platform:hover .pt-led{animation:none;opacity:1;transform:scale(1.2)}
@keyframes ptPulse{0%,100%{opacity:.22;transform:scale(.9)}50%{opacity:.72;transform:scale(1.08)}}

/* one-shot accent scan sweep across the tile face on hover entry */
.platform::before{content:"";position:absolute;inset:0;pointer-events:none;background:linear-gradient(105deg,transparent 32%,color-mix(in srgb,var(--accent) 22%,transparent) 50%,transparent 68%);background-size:230% 100%;background-position:130% 0;opacity:0;border-radius:inherit;mix-blend-mode:overlay;z-index:1}
.platform:hover::before{opacity:1;animation:ptScan .85s ease}
@keyframes ptScan{from{background-position:130% 0}to{background-position:-55% 0}}

/* mono readout — floats below the tile on hover (doesn't push layout) */
.platform[data-spec]::after{content:attr(data-spec);position:absolute;left:50%;top:calc(100% + 8px);transform:translate(-50%,6px) scale(.95);transform-origin:top center;font-family:var(--font-mono);font-size:.6rem;font-weight:600;letter-spacing:.035em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 92%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:1px solid var(--accent-line);border-radius:6px;padding:.28rem .5rem;white-space:nowrap;opacity:0;pointer-events:none;z-index:9;box-shadow:0 12px 24px -14px var(--accent-glow);transition:opacity .22s var(--ease),transform .22s var(--ease)}
.platform[data-spec]:hover::after{opacity:1;transform:translate(-50%,0) scale(1)}

@media (prefers-reduced-motion:reduce){
  .platform .pt-led,.platform::before,.platform[data-spec]::after,.platform{transition:none;animation:none}
  .platform .pt-led{animation:none}
  .platform:hover .pt-led{transform:none}
}

/* ---- Wave 12.1 — Learning panel layout polish ----
   The default .panel laid the h4 row labels stacked above tiny centered
   tiles (auto-fit minmax(120px,150px)), leaving big empty bands on a
   wide viewport. Restructure #extras .panel to a clean 2-column grid:
   row label sits in col 1 (vertically centred), tiles span 4-across in
   col 2 (justify:stretch, equal gutters). Collapses to single-column
   below the 768px breakpoint where the existing media query already
   sets .platforms-row to repeat(4,1fr). Heading "Platforms I learn from."
   no longer breaks with a <br>.
   Also: the panel's accent ::before bar used to sit top:0/bottom:0 and
   look "rounded" only because overflow:hidden clipped it to the panel's
   22px corner curve. Wave 12 flips overflow→visible on hover (so the
   tile readout can escape downward), which exposed the bar's square
   top/bottom corners — looked like a flat line jumping in.

   ⚠ INSETTING THE BAR IS NOT THE FIX — IT TRADES ONE WRONG SHAPE FOR
     ANOTHER. Wave 12 tried 10px, Wave 12.1 tried the honest
     `calc(var(--radius-lg) + 1px)` (the first value at which a left:0 bar
     is actually flush, since at depth d the card's edge sits at
     `R − √(R² − (R−d)²)` and that only reaches 0 at d ≥ R). It IS flush —
     and it reads as a short blue stub floating on the edge with 23px of
     bare corner above and below it. Reported again after that landed.

   ⚠ THE BAR MUST CARRY THE CARD'S OWN CORNER, NOT AVOID IT, AND IT MUST
     DO SO WITHOUT `overflow`. So the ::before is no longer a 4px bar: it
     is a corner-width box carrying the panel's INNER radius (R − border,
     because the pseudo is positioned against the PADDING box), masked
     down to the leftmost 4px. The background is clipped by the radius
     first, the mask second, so the visible stripe tapers along the exact
     same arc the card does — in BOTH overflow states, at every width.
     ⚠ Width must stay ≥ the radius or CSS's overlapping-radii rule scales
       the curve down (top edge sums 21 + 0 against the box width) and the
       stripe stops matching the card. It is also ≤ the mobile
       padding-left (26px), so the masked-out part never reaches text.
     ⚠ Do not "simplify" this to `border-radius:22px 0 0 22px` on the 4px
       bar — same rule scales those 22s to 4 and you get a pill again. */
#extras .panel{display:grid;grid-template-columns:minmax(180px,210px) 1fr;column-gap:34px;row-gap:24px;align-items:center;padding:30px 32px 30px 38px}
#extras .panel::before{top:0;bottom:0;width:calc(var(--radius-lg) + 4px);border-radius:calc(var(--radius-lg) - 1px) 0 0 calc(var(--radius-lg) - 1px);-webkit-mask:linear-gradient(90deg,#000 0 4px,transparent 4px);mask:linear-gradient(90deg,#000 0 4px,transparent 4px);pointer-events:none}
#extras .panel h4{margin:0;line-height:1.55}
#extras .panel h4:not(:first-child){margin-top:0}
#extras .platforms-row{grid-template-columns:repeat(4,1fr);justify-content:stretch;gap:16px}
@media (max-width:767.98px){
  #extras .panel{grid-template-columns:1fr;row-gap:12px;padding:24px 22px 24px 26px}
  #extras .panel h4{margin-bottom:6px}
  #extras .panel h4:not(:first-child){margin-top:14px}
}
/* ⚠ FOUR TILES ACROSS DO NOT FIT A PHONE, AND THE PANEL'S OWN `overflow:hidden`
     WAS HIDING IT. `repeat(4,1fr)` is really `minmax(auto,1fr)`, so the tracks
     refuse to go below their min-content and the row simply overflows instead
     of shrinking. Measured at 390px: row `scrollWidth` **440** against a 376
     client — Codeforces and GfG sliced off by 37px each, with no page-level
     scrollbar to give it away because the panel clips. A tile's min-content is
     ~96px, so four of them plus three 16px gaps need 432px of row; the panel
     only has that from ~576px of viewport up. Two-up below it. */
@media (max-width:575.98px){
  #extras .platforms-row{grid-template-columns:repeat(2,1fr)}
}

/* ---- Wave 12.2 — per-platform logo signatures + LED tune-in ----
   Eight platforms, eight distinct micro-motions on the logo image, each
   matched to that platform's vibe: CodeChef's chef-hat tilts and lifts;
   HackerRank pops; LeetCode "types" (skew flick); Codeforces' bars
   stretch upward; Coursera rises (course-card lift); DataCamp spins a
   touch; Codecademy steps forward (path progress); GfG bounces. 500-
   650ms one-shots on hover entry, resolve to a slightly-elevated rest
   state so the tile reads as "activated" while you linger. Plus the
   pt-led now TUNES IN on hover entry (a fast-pulse burst before
   locking to steady) — like a radio dial finding the channel. Per-
   platform craft, calm in aggregate. Reduced-motion fully bypasses. */
.platform img{transition:transform .3s var(--ease);transform-origin:center;will-change:transform}
.platform[data-tag="codechef"]:hover img   {animation:plHat .65s var(--ease) both}
.platform[data-tag="hackerrank"]:hover img {animation:plPop .55s var(--ease) both}
.platform[data-tag="leetcode"]:hover img   {animation:plType .55s var(--ease) both}
.platform[data-tag="codeforces"]:hover img {animation:plBars .55s var(--ease) both}
.platform[data-tag="coursera"]:hover img   {animation:plRise .55s var(--ease) both}
.platform[data-tag="datacamp"]:hover img   {animation:plTilt .55s var(--ease) both}
.platform[data-tag="codecademy"]:hover img {animation:plStep .55s var(--ease) both}
.platform[data-tag="gfg"]:hover img        {animation:plBounce .6s var(--ease) both}

@keyframes plHat   {0%{transform:rotate(0) translateY(0)}30%{transform:rotate(-12deg) translateY(-3px)}60%{transform:rotate(5deg) translateY(-1px)}100%{transform:rotate(-2deg) translateY(-2px)}}
@keyframes plPop   {0%{transform:scale(1)}45%{transform:scale(1.16)}100%{transform:scale(1.08)}}
@keyframes plType  {0%{transform:skewX(0) translateX(0)}25%{transform:skewX(8deg) translateX(2px)}50%{transform:skewX(-4deg) translateX(-1px)}75%{transform:skewX(2deg)}100%{transform:skewX(0) scale(1.05)}}
@keyframes plBars  {0%{transform:scaleY(1) translateY(0)}45%{transform:scaleY(1.18) translateY(-2px)}100%{transform:scaleY(1.06) translateY(-1px)}}
@keyframes plRise  {0%{transform:translateY(0)}50%{transform:translateY(-5px) scale(1.06)}100%{transform:translateY(-2px) scale(1.04)}}
@keyframes plTilt  {0%{transform:rotate(0)}35%{transform:rotate(10deg) scale(1.08)}65%{transform:rotate(-3deg) scale(1.06)}100%{transform:rotate(3deg) scale(1.05)}}
@keyframes plStep  {0%{transform:translateX(0)}35%{transform:translateX(4px) scale(1.05)}65%{transform:translateX(-2px) scale(1.06)}100%{transform:translateX(1px) scale(1.04)}}
@keyframes plBounce{0%{transform:scale(1) translateY(0)}35%{transform:scale(1.12) translateY(-4px)}70%{transform:scale(.98) translateY(0)}100%{transform:scale(1.06) translateY(-1px)}}

/* LED tune-in on hover entry — fast-pulse burst, then lock to steady + slightly larger glow.
   Overrides the Wave 12 ".platform:hover .pt-led" (defined earlier) by being a later same-
   specificity rule. */
.platform:hover .pt-led{animation:plTune .58s var(--ease) both}
@keyframes plTune{0%{transform:scale(.9);opacity:.35}20%,60%{transform:scale(1.45);opacity:1}40%,80%{transform:scale(.92);opacity:.55}100%{transform:scale(1.2);opacity:1;box-shadow:0 0 10px var(--accent-glow)}}

@media (prefers-reduced-motion:reduce){
  .platform img,.platform:hover .pt-led,.platform[data-tag]:hover img{animation:none!important;transition:none}
}

/* =====================================================================
   CONTACT · Wave 16 — realistic local-time clock + MEANINGFUL routing
   Fixes two complaints: (1) the mini clock was flat + floated detached
   beside the wrapping time text — now it's a small DIMENSIONAL clock
   (domed glass face, drop shadow, glass glare) sitting inline, the time
   forced to one centered line so nothing floats. (2) the intent underline
   read as meaningless decoration — now hovering an opportunity wipes an
   accent HIGHLIGHT across it (the About "marker" pattern = selected) AND
   a live compose line under the buttons updates to "✎ subject: …" while
   the email CTA glows, so the routing's meaning is legible: point at what
   you need, watch the email pre-fill. telemetry.js #18 contactConsole. */

/* a) presence = live signal equalizer */
.contact-meta .presence{display:inline-flex;align-items:center;justify-content:center;gap:.6rem;font-weight:600;font-size:.95rem;color:var(--text);line-height:1.2}
.contact-meta .presence-beacon{display:inline-flex;align-items:flex-end;gap:2.5px;height:13px}
.contact-meta .presence-beacon .pb-bar{width:2.5px;height:100%;border-radius:2px;background:var(--accent);transform-origin:bottom;transform:scaleY(.3);animation:eqBar 1.15s ease-in-out infinite}
.contact-meta .presence-beacon .pb-bar:nth-child(2){animation-delay:.16s}
.contact-meta .presence-beacon .pb-bar:nth-child(3){animation-delay:.32s}
.contact-meta .presence-beacon .pb-bar:nth-child(4){animation-delay:.48s}
@keyframes eqBar{0%,100%{transform:scaleY(.26)}50%{transform:scaleY(1)}}
.contact-meta .presence-sub{font-size:.82rem!important;color:var(--text-soft)!important;font-weight:500!important}
.contact-meta .presence.is-rest .presence-beacon .pb-bar{animation:eqRest 3.8s ease-in-out infinite;background:color-mix(in srgb,var(--accent) 48%,var(--text-mute))}
.contact-meta .presence.is-rest .presence-beacon .pb-bar:nth-child(2){animation-delay:.5s}
.contact-meta .presence.is-rest .presence-beacon .pb-bar:nth-child(3){animation-delay:1s}
.contact-meta .presence.is-rest .presence-beacon .pb-bar:nth-child(4){animation-delay:1.5s}
@keyframes eqRest{0%,100%{transform:scaleY(.2)}50%{transform:scaleY(.42)}}

/* b) realistic mini clock + a deliberate two-line local-time widget (clock spans
   both lines, centered as a group — never wraps/floats, never blows the column). */
.contact-meta .my-time{display:grid;grid-template-columns:auto auto;column-gap:.55rem;row-gap:0;align-items:center;justify-content:center;width:max-content;margin:6px auto 0;text-align:left}
.contact-meta .my-time .mini-clock{grid-row:1 / span 2;align-self:center}
.contact-meta .my-time .mt-time{grid-column:2;font-family:var(--font-mono);font-weight:700;font-size:.9rem;color:var(--text);line-height:1.2}
.contact-meta .my-time .mt-zone{grid-column:2;font-family:var(--font-mono);font-size:.68rem;color:var(--text-mute);line-height:1.2}
.mini-clock{position:relative;width:22px;height:22px;flex:0 0 auto;border-radius:50%;background:radial-gradient(circle at 38% 30%,#ffffff,#eaf0f8 60%,#d3ddec);box-shadow:0 3px 6px -1px rgba(15,23,42,.3),0 1px 2px rgba(15,23,42,.2),inset 0 1.5px 1px rgba(255,255,255,.95),inset 0 -2px 3px rgba(15,23,42,.16),0 0 0 1px color-mix(in srgb,var(--accent) 28%,transparent)}
.mini-clock::before{content:"";position:absolute;top:2px;left:3.5px;width:9px;height:5px;border-radius:50%;background:linear-gradient(rgba(255,255,255,.95),rgba(255,255,255,0));transform:rotate(-18deg);opacity:.85;pointer-events:none}
.mini-clock .mc-hand{position:absolute;left:50%;bottom:50%;transform-origin:bottom center;transform:translateX(-50%);border-radius:2px}
.mini-clock .mc-hour{width:1.8px;height:5px;background:var(--accent);box-shadow:0 0 1px rgba(0,0,0,.25)}
.mini-clock .mc-min{width:1.5px;height:7px;background:#1f2a44;box-shadow:0 0 1px rgba(0,0,0,.25)}
.mini-clock .mc-pin{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:3px;height:3px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 1px rgba(255,255,255,.6)}
[data-theme="dark"] .mini-clock{background:radial-gradient(circle at 38% 30%,#30343d,#191c22 60%,#0f1116);box-shadow:0 3px 7px -1px rgba(0,0,0,.6),inset 0 1.5px 1px rgba(255,255,255,.12),inset 0 -2px 3px rgba(0,0,0,.55),0 0 0 1px color-mix(in srgb,var(--accent) 34%,transparent)}
[data-theme="dark"] .mini-clock::before{background:linear-gradient(rgba(255,255,255,.3),rgba(255,255,255,0));opacity:.5}
[data-theme="dark"] .mini-clock .mc-min{background:#cdd6e6}

/* c) routable intents — accent highlight WIPE (marker = "selected"), reusing the About pattern */
.contact-card .intent{background-image:linear-gradient(color-mix(in srgb,var(--accent) 16%,transparent),color-mix(in srgb,var(--accent) 16%,transparent));background-repeat:no-repeat;background-position:0 100%;background-size:0% 100%;border-radius:4px;padding:0 .14em;cursor:pointer;transition:background-size .32s var(--ease),color .25s var(--ease),box-shadow .25s var(--ease)}
.contact-card .intent:hover{background-size:100% 100%;color:var(--accent-ink)}
.contact-card .intent:hover strong{color:var(--accent-ink)}
/* click-locked intent — the wipe stays + an underline seals the choice until toggled off */
.contact-card .intent.is-locked{background-size:100% 100%;color:var(--accent-ink);box-shadow:0 2px 0 var(--accent)}
.contact-card .intent.is-locked strong{color:var(--accent-ink)}
.contact-card .intent:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* the meaningful payoff: a live compose line under the buttons shows the email pre-filling */
.compose-line{margin:16px 0 0;min-height:1.3em;text-align:center;font-family:var(--font-mono);font-size:.72rem;letter-spacing:.02em;color:var(--text-mute);transition:color .25s var(--ease)}
.compose-line.is-active{color:var(--accent-ink)}
.compose-line .cl-text{display:inline-block}
.compose-line.is-active .cl-text{animation:clPop .3s var(--ease)}
@keyframes clPop{0%{transform:translateY(3px);opacity:.35}100%{transform:translateY(0);opacity:1}}

/* email CTA glow while an intent is routed + envelope "send" nudge on hover */
.contact-actions .btn-primary{position:relative}
.contact-actions .btn-primary.is-routed{box-shadow:0 0 0 1.5px var(--accent),0 16px 38px -14px var(--accent-glow)}
.contact-actions .btn-primary i.bi{transition:transform .35s var(--ease)}
.contact-actions .btn-primary:hover i.bi{transform:translateY(-2px) rotate(-8deg)}

@media (prefers-reduced-motion:reduce){
  .contact-meta .presence-beacon .pb-bar{animation:none!important;transform:scaleY(.55)}
  .contact-card .intent,.compose-line,.contact-actions .btn-primary i.bi{transition:none}
  .compose-line.is-active .cl-text{animation:none}
  .contact-actions .btn-primary:hover i.bi{transform:none}
}

/* =====================================================================
   FOOTER · Wave 17 — the nav is a channel tuner (telemetry callback)
   The Explore links jump to the same sections whose eyebrows read
   "CH_01 ABOUT" … (Wave 1 channel-IDs). On hover each link reveals its
   real channel-ID sliding in from the left while the label shifts right
   — the footer literally tunes you to a channel, tying the sign-off back
   to the telemetry theme. Connect links reveal a directional glyph
   instead (↗ external · ✉ email · ↑ back to top). Plus subtle life on
   the brand mark + social icons. data-ch / data-go set by telemetry.js
   #19 footerNav. Blue tokens · transform/opacity · reduced-motion safe. */

/* nav links — a leading marker fades in at the left while the LABEL slides right
   (transform on the wrapped .sf-lbl, so no reflow and long labels never wrap) */
.sf-col a{position:relative;transition:color var(--t) var(--ease)}
.sf-col a .sf-lbl{display:inline-block;transition:transform .28s var(--ease)}
.sf-col a[data-ch]::before,.sf-col a[data-go]::before{position:absolute;left:0;top:50%;font-family:var(--font-mono);font-weight:600;color:var(--accent);opacity:0;transform:translate(-5px,-50%);transition:opacity .25s var(--ease),transform .25s var(--ease);pointer-events:none;white-space:nowrap}
.sf-col a[data-ch]::before{content:attr(data-ch);font-size:.6rem;letter-spacing:.06em}
.sf-col a[data-go]::before{content:attr(data-go);font-size:.82rem;line-height:1}
.sf-col a[data-ch]:hover,.sf-col a[data-go]:hover{color:var(--accent-ink);transform:none}
.sf-col a[data-ch]:hover .sf-lbl{transform:translateX(42px)}
.sf-col a[data-go]:hover .sf-lbl{transform:translateX(22px)}
.sf-col a[data-ch]:hover::before,.sf-col a[data-go]:hover::before{opacity:1;transform:translate(0,-50%)}

/* social icon buttons — keep the lift, add a crisp icon pop */
.sf-social a i{transition:transform .3s var(--ease)}
.sf-social a:hover i{transform:scale(1.18)}

/* brand mark — a touch of life on hover (the signature dot glows, mark tips) */
.sf-brand .brand-mark{transition:transform .35s var(--ease)}
.sf-brand:hover .brand-mark{transform:scale(1.06) rotate(-3deg)}
.sf-brand .sf-brand-dot{display:inline-block}
.sf-brand:hover .sf-brand-dot{animation:sfDot 1.5s ease-in-out infinite}
@keyframes sfDot{0%,100%{opacity:.85;text-shadow:0 0 0 transparent}50%{opacity:1;text-shadow:0 0 11px var(--accent-glow)}}

@media (prefers-reduced-motion:reduce){
  .sf-col a,.sf-col a .sf-lbl,.sf-col a[data-ch]::before,.sf-col a[data-go]::before,.sf-social a i,.sf-brand .brand-mark{transition:none}
  .sf-col a[data-ch]:hover .sf-lbl,.sf-col a[data-go]:hover .sf-lbl{transform:none}
  .sf-brand:hover .sf-brand-dot,.sf-brand:hover .brand-mark{animation:none;transform:none}
}

/* ---- Wave 17b — footer signature: the maker's name reads as a gradient
   "signature" (was muted gray + barely visible). The "Designed and developed
   by" prefix stays quiet mono; the NAME becomes a brand-gradient display-font
   signature with a trailing dot + a soft glow + sheen sweep on hover. polish.js
   #5 signature wraps the name in .sf-sign. Works on every page. */
.sf-made .sf-sign{position:relative;font-family:var(--font-display);font-weight:700;font-size:1rem;letter-spacing:.005em;background:linear-gradient(100deg,var(--accent) 0%,var(--accent) 42%,color-mix(in srgb,#fff 78%,var(--accent)) 50%,var(--accent-2,var(--accent)) 60%,var(--accent) 100%);background-size:230% 100%;background-position:115% 0;-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent;transition:background-position .9s var(--ease),filter .3s var(--ease)}
.sf-made .sf-sign-dot{-webkit-text-fill-color:var(--accent);color:var(--accent);font-weight:800}
.sf-made:hover .sf-sign{background-position:-15% 0;filter:drop-shadow(0 2px 9px var(--accent-glow))}
.sf-made:hover .sf-sign-dot{animation:sfDot 1.5s ease-in-out infinite}
@media (prefers-reduced-motion:reduce){
  .sf-made .sf-sign{transition:none;background-position:0 0}
  .sf-made:hover .sf-sign{background-position:0 0}
  .sf-made:hover .sf-sign-dot{animation:none}
}

/* =====================================================================
   FEEDBACK PASS — make the portfolio feel more personal, less SaaS
   ===================================================================== */
.hero-pipeline{background:color-mix(in srgb,var(--surface) 44%,transparent);box-shadow:0 24px 52px -38px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.4)}
.pipe-status{letter-spacing:.1em}
.proof-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:0 0 24px}
.proof-grid div{padding:16px;border-radius:14px;border:1px solid var(--border);background:color-mix(in srgb,var(--surface) 80%,transparent)}
.proof-grid b{display:block;font-family:var(--font-display);font-size:clamp(1.45rem,2.4vw,2rem);line-height:1;color:var(--accent-ink);margin-bottom:7px}
.proof-grid span{display:block;color:var(--text-soft);font-size:.88rem;line-height:1.45}
.featured-layout{align-items:stretch}
.featured-card.flagship{min-height:100%;border-color:color-mix(in srgb,var(--accent) 42%,var(--border-strong));box-shadow:0 28px 70px -38px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 44%,transparent)}
.featured-card.flagship .fc-media{aspect-ratio:16/9}
.featured-card.flagship .fc-body{gap:.82rem;padding:clamp(22px,2.2vw,30px)}
.featured-card.flagship .fc-body h3{font-size:clamp(1.42rem,2.2vw,2rem);line-height:1.12}
.featured-card.flagship .fc-body p{font-size:1rem;line-height:1.65}
.fc-label{display:inline-flex;align-self:flex-start;padding:.25rem .55rem;border-radius:999px;font-family:var(--font-mono);font-size:.62rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--accent-ink);background:var(--accent-soft);border:1px solid var(--accent-line)}
.fc-proof{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:.15rem 0 .2rem}
.fc-proof span{display:flex;flex-direction:column;gap:2px;padding:10px;border-radius:10px;border:1px solid var(--border);background:color-mix(in srgb,var(--surface-2) 78%,transparent);color:var(--text-soft);font-size:.72rem;line-height:1.25}
.fc-proof b{font-family:var(--font-display);font-size:.88rem;color:var(--text)}
.section-support .section-head{max-width:680px;margin-bottom:32px}
.section-support .section-title{font-size:clamp(1.85rem,3.5vw,2.85rem)}
.section-support .section-sub{font-size:.98rem}
.section-support .cert-card{box-shadow:0 14px 32px -28px var(--accent-glow)}
.growth-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:18px}
.growth-card{position:relative;display:flex;flex-direction:column;gap:8px;min-height:156px;padding:18px;border-radius:16px;border:1px solid var(--border-strong);background:linear-gradient(135deg,var(--surface) 45%,color-mix(in srgb,var(--accent) 10%,var(--surface)));box-shadow:0 16px 34px -28px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 38%,transparent)}
.growth-card i{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;background:var(--accent-soft);border:1px solid var(--accent-line);color:var(--accent-ink);font-size:1rem}
.growth-card strong{font-family:var(--font-display);font-size:1rem;color:var(--text)}
.growth-card span{color:var(--text-soft);font-size:.9rem;line-height:1.52}
.contact-title{max-width:760px;margin-left:auto;margin-right:auto}
.contact-sub{line-height:1.65}
.sf-col a[data-ch]:hover .sf-lbl{transform:translateX(54px)}
@media (max-width:991.98px){.featured-card.flagship .fc-media{aspect-ratio:16/8}.growth-grid{grid-template-columns:repeat(2,1fr)}.proof-grid{grid-template-columns:1fr}}
@media (max-width:767.98px){.fc-proof{grid-template-columns:1fr}.growth-grid{grid-template-columns:1fr}.featured-card.flagship .fc-media{aspect-ratio:16/10}}
@media (max-width:575.98px){.hero-terminal{width:100%;max-width:100%;font-size:.72rem;padding:.48rem .7rem}.hero-terminal code{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.hero-terminal .term-answer{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.hero-name{font-size:clamp(3rem,15vw,4.15rem);letter-spacing:-.035em;text-align:center}.hero-name .hn-line2{display:block;width:max-content;max-width:100%;margin-inline:auto}.hero-main .hero-hello{display:block;width:max-content;margin-inline:auto}.hero-tagline{font-size:clamp(1.35rem,7vw,1.8rem)}.hero-lead{font-size:1rem;max-width:100%;overflow-wrap:anywhere}.hero-craft{font-size:.94rem}.hero-status{max-width:100%;font-size:.72rem;white-space:normal}.hero-pipeline{width:100%;max-width:100%;padding:10px 10px 12px;border-radius:18px}.pipe-head{align-items:flex-start}.pipe-id{font-size:.68rem;min-width:0}.pipe-id-mute{display:none}.pipe-status{font-size:.54rem;padding:.22rem .44rem;letter-spacing:.07em;white-space:nowrap}.pipe-port{padding:.46rem .55rem;gap:.5rem}.pipe-port-body span{font-size:.64rem}.pipe-port-tag{display:none}.pipe-node .wb-step{font-size:.6rem}.pipe-node .wb-term-body{font-size:.68rem}.wb-metric-val{font-size:1.18rem}.wb-deploy-row{gap:.55rem}.wb-deploy-ic{width:34px;height:34px}.wb-deploy-sub{font-size:.66rem}.wb-deploy-chips .float-chip{font-size:.64rem;padding:.34rem .56rem}.pipe-note{font-size:.58rem}}
@media (max-width:575.98px){html,body{width:100%;max-width:100%;overflow-x:hidden}.nav-shell .container,.hero .container,.hero-inner,.hero-main,.hero-aside{width:100%;max-width:100%;min-width:0}.hero-main,.hero-aside{overflow:hidden}.hero-terminal{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center}.hero-terminal .term-answer{grid-column:1 / -1;display:block}.hero-lead,.hero-craft{min-width:0;max-width:100%;overflow-wrap:anywhere}.hero-lead .lead-key{display:inline}.hero-stats-row{width:100%;max-width:100%}.hero-stats dd{white-space:normal}.hero-pipeline *{min-width:0}.pipe-port-body,.wb-deploy-body{min-width:0}.pipe-port-body span,.wb-deploy-sub{white-space:normal;overflow-wrap:anywhere}}

/* Hero craft console: a compact replacement for the repeated paragraph/note */
.hero-craft{margin:-8px 0 16px;max-width:62ch}
.craftline-kicker{display:inline-flex;align-items:center;gap:.42rem;margin-bottom:9px;font-family:var(--font-mono);font-size:.68rem;font-weight:800;letter-spacing:.09em;text-transform:uppercase;color:var(--accent-ink)}
.craftline-kicker i{font-size:.82rem}
.craft-switchboard{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}
.craft-switchboard .hc{position:relative;display:flex;align-items:center;justify-content:center;gap:.42rem;min-height:42px;padding:.56rem .58rem;border-radius:12px;border:1px solid var(--border);background:color-mix(in srgb,var(--surface) 78%,transparent);font-family:var(--font-mono);font-size:.72rem;font-weight:800;color:var(--text);overflow:visible;isolation:isolate;transition:transform .28s var(--ease),border-color .28s var(--ease),box-shadow .28s var(--ease),background .28s var(--ease),color .28s var(--ease)}
.craft-switchboard .hc i{color:var(--accent-ink);font-size:.9rem;transition:transform .28s var(--ease),filter .28s var(--ease)}
.craft-switchboard .hc b{font:inherit;white-space:nowrap}
.craft-switchboard .hc::after{content:attr(data-readout);position:absolute;left:50%;bottom:calc(100% + 8px);z-index:12;max-width:230px;width:max-content;white-space:normal;text-align:center;transform:translate(-50%,6px) scale(.96);font-family:var(--font-mono);font-size:.58rem;font-weight:650;line-height:1.35;letter-spacing:.01em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 94%,transparent);border:1px solid var(--accent-line);border-radius:8px;padding:.38rem .52rem;box-shadow:0 14px 28px -16px var(--accent-glow);opacity:0;pointer-events:none;transition:opacity .22s var(--ease),transform .22s var(--ease)}
.craft-switchboard .hc:hover,.craft-switchboard .hc.play{transform:translateY(-3px);border-color:var(--accent);background:color-mix(in srgb,var(--accent) 10%,var(--surface));box-shadow:0 14px 28px -16px var(--accent-glow)}
.craft-switchboard .hc:hover::after,.craft-switchboard .hc.play::after{opacity:1;transform:translate(-50%,0) scale(1)}
.hc-ux::before{content:"";position:absolute;inset:7px;border-radius:8px;border:1px solid transparent;pointer-events:none;transition:border-color .24s var(--ease),box-shadow .24s var(--ease)}
.hc-ux:hover::before,.hc-ux.play::before{border-color:var(--accent-line);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--accent) 22%,transparent)}
.hc-ux:hover i,.hc-ux.play i{transform:translate(3px,-2px) rotate(-8deg)}
.hc-eval::before{content:"";position:absolute;left:10px;right:10px;bottom:8px;height:3px;border-radius:3px;background:linear-gradient(90deg,var(--accent),#22c55e);transform:scaleX(.22);transform-origin:left;transition:transform .35s var(--ease)}
.hc-eval:hover::before,.hc-eval.play::before{transform:scaleX(1)}
.hc-eval:hover i,.hc-eval.play i{filter:drop-shadow(0 0 7px var(--accent-glow));transform:scale(1.08)}
.hc-cost::before{content:"";position:absolute;right:8px;top:8px;width:6px;height:6px;border-radius:50%;background:#22c55e;box-shadow:-8px 8px 0 color-mix(in srgb,var(--accent) 70%,#22c55e),-16px 16px 0 color-mix(in srgb,var(--accent) 42%,transparent);opacity:.55;transition:transform .28s var(--ease),opacity .28s var(--ease)}
.hc-cost:hover::before,.hc-cost.play::before{opacity:1;transform:translate(-4px,4px)}
.hc-cost:hover i,.hc-cost.play i{transform:rotate(-12deg) scale(1.08)}
.craft-switchboard .hc-detail{color:var(--text);text-decoration:none}
.craft-switchboard .hc-detail::before{content:"";position:absolute;left:50%;top:50%;width:18px;height:18px;border:1.5px solid var(--accent);border-radius:50%;transform:translate(-50%,-50%) scale(.45);opacity:0;pointer-events:none;transition:opacity .22s var(--ease),transform .28s var(--ease)}
.craft-switchboard .hc-detail:hover::before,.craft-switchboard .hc-detail.play::before{opacity:.85;transform:translate(-50%,-50%) scale(1)}
.craft-switchboard .hc-detail:hover i,.craft-switchboard .hc-detail.play i{transform:rotate(90deg) scale(1.08)}
.hero-transform{position:relative;display:grid;grid-template-columns:auto 42px auto 42px auto;align-items:center;gap:6px;max-width:62ch;margin:0 0 24px;padding:12px 14px;border:1px solid var(--border);border-radius:14px;background:linear-gradient(135deg,color-mix(in srgb,var(--surface) 86%,transparent),color-mix(in srgb,var(--accent) 7%,var(--surface)));box-shadow:0 16px 34px -28px var(--accent-glow)}
.ht-node{display:inline-flex;align-items:center;justify-content:center;gap:.42rem;min-height:34px;padding:.42rem .58rem;border-radius:10px;border:1px solid var(--border);background:color-mix(in srgb,var(--surface) 84%,transparent);font-family:var(--font-mono);font-size:.68rem;font-weight:800;color:var(--text-soft);white-space:nowrap;transition:transform .28s var(--ease),color .28s var(--ease),border-color .28s var(--ease),background .28s var(--ease)}
.ht-node i{color:var(--accent-ink)}
.ht-path{position:relative;height:2px;border-radius:2px;background:var(--border-strong);overflow:hidden}
.ht-path::before{content:"";position:absolute;inset:0;background:var(--grad);transform:translateX(-102%);animation:htFlow 2.6s var(--ease) infinite}
.hero-transform:hover .ht-node{color:var(--text);border-color:var(--accent-line);background:color-mix(in srgb,var(--accent) 8%,var(--surface));transform:translateY(-1px)}
.hero-transform small{grid-column:1 / -1;margin-top:2px;font-size:.72rem;color:var(--text-mute);font-family:var(--font-mono)}
@keyframes htFlow{0%{transform:translateX(-102%)}42%,100%{transform:translateX(102%)}}
@media (max-width:575.98px){.craft-switchboard{grid-template-columns:repeat(2,minmax(0,1fr))}.craft-switchboard .hc{font-size:.66rem}.craft-switchboard .hc::after{display:none}.hero-transform{grid-template-columns:1fr;gap:7px}.ht-path{width:2px;height:18px;justify-self:center}.ht-path::before{transform:translateY(-102%);animation:htFlowY 2.6s var(--ease) infinite}.ht-node{white-space:normal}.hero-transform small{text-align:center}}
@media (max-width:575.98px){.hero-lead{font-size:.96rem;line-height:1.62}.hero-lead .lead-key{display:inline!important;white-space:normal;overflow-wrap:anywhere}.craft-switchboard .hc b{white-space:normal;text-align:center}.hero-transform{overflow:hidden}.hero-transform small{display:block;max-width:100%;font-size:.66rem;line-height:1.45;white-space:normal;overflow-wrap:anywhere}}
@media (max-width:575.98px){.hero-terminal{display:inline-flex;width:auto;max-width:100%;grid-template-columns:none;flex-wrap:nowrap;align-items:center;gap:.46rem;margin-bottom:16px;padding:.42rem .58rem;border-radius:12px;font-size:.66rem;box-shadow:0 12px 24px -20px var(--accent-glow)}.hero-terminal::before{display:none}.hero-terminal::after{height:1px}.hero-terminal code{display:inline-flex;align-items:center;min-width:0;max-width:none;white-space:nowrap;overflow:visible;text-overflow:clip}.hero-terminal .term-prompt,.hero-terminal .term-sep,.hero-terminal .term-path{display:none}.hero-terminal .term-cmd{color:var(--accent-ink);font-weight:800}.hero-terminal .term-answer{display:inline-flex;flex-basis:auto;grid-column:auto;min-width:0;margin-top:0;font-size:.62rem;line-height:1;color:var(--text-mute);white-space:nowrap;overflow:visible;text-overflow:clip}.hero-terminal .term-answer::before{width:5px;height:5px;margin-right:.28rem;border-radius:1.5px;box-shadow:0 0 0 2px var(--accent-soft)}.hero-terminal .term-caret{display:none}.tt-dots{gap:4px}.tt-dots i{width:7px;height:7px}}
@keyframes htFlowY{0%{transform:translateY(-102%)}42%,100%{transform:translateY(102%)}}
@media (prefers-reduced-motion:reduce){.craft-switchboard .hc,.craft-switchboard .hc i,.craft-switchboard .hc::after,.hc-ux::before,.hc-eval::before,.hc-cost::before,.craft-switchboard .hc-detail::before,.ht-node{transition:none}.ht-path::before{animation:none;transform:none}}

/* About proof microinteractions: quote/profile/capabilities/education only; Off the clock stays untouched */
#about .about-quote::after{content:"impact > income";position:absolute;left:28px;bottom:-12px;font-family:var(--font-mono);font-size:.58rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 88%,transparent);border:1px solid var(--accent-line);border-radius:6px;padding:.16rem .42rem;opacity:0;transform:translateY(6px) scale(.96);pointer-events:none;box-shadow:0 10px 22px -14px var(--accent-glow);transition:opacity .28s var(--ease),transform .28s var(--ease)}
#about .about-quote:hover::after{opacity:1;transform:translateY(0) scale(1)}
#about .ap-loc{position:relative;width:max-content;max-width:100%;overflow:visible}
#about .ap-loc::before{content:"";position:absolute;left:1.15rem;right:0;bottom:-4px;height:1.5px;background:linear-gradient(90deg,var(--accent),transparent);border-radius:2px;transform:scaleX(0);transform-origin:left;transition:transform .38s var(--ease)}
/* Same rule as the portrait: a field annotates on ITS OWN hover, not the
   card's. This underline used to draw whenever the pointer was anywhere in the
   card, so hovering the name lit up the location. */
#about .ap-loc:hover::before{transform:scaleX(1)}
#about .ap-avail[data-readout]{position:relative;width:max-content;max-width:100%}
#about .ap-avail[data-readout]::after{content:attr(data-readout);position:absolute;left:0;top:calc(100% + 7px);font-family:var(--font-mono);font-size:.56rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:#0f7a3c;background:color-mix(in srgb,#22c55e 14%,var(--surface));border:1px solid color-mix(in srgb,#22c55e 34%,transparent);border-radius:6px;padding:.2rem .42rem;white-space:nowrap;opacity:0;transform:translateY(-4px) scale(.95);pointer-events:none;z-index:8;box-shadow:0 10px 20px -14px rgba(34,197,94,.75);transition:opacity .25s var(--ease),transform .25s var(--ease)}
#about .ap-avail[data-readout]:hover::after{opacity:1;transform:translateY(0) scale(1)}
.do-list li{position:relative;border-radius:14px;padding:.18rem .5rem .18rem 0;margin-right:-.5rem;isolation:isolate}
.do-list li::before{content:attr(data-step);position:absolute;right:8px;top:0;font-family:var(--font-mono);font-size:.56rem;font-weight:800;letter-spacing:.08em;color:var(--accent-ink);opacity:0;transform:translateY(4px);transition:opacity .24s var(--ease),transform .24s var(--ease)}
.do-list li::after{content:attr(data-proof);position:absolute;left:47px;bottom:-9px;font-family:var(--font-mono);font-size:.57rem;font-weight:700;letter-spacing:.02em;color:var(--text-mute);background:color-mix(in srgb,var(--surface) 92%,transparent);border:1px solid var(--border);border-radius:6px;padding:.12rem .38rem;white-space:nowrap;opacity:0;transform:translateY(-4px);pointer-events:none;z-index:6;box-shadow:0 8px 18px -14px var(--accent-glow);transition:opacity .24s var(--ease),transform .24s var(--ease),border-color .24s var(--ease),color .24s var(--ease)}
.do-list li:hover::before{opacity:.85;transform:none}
.do-list li:hover::after{opacity:1;transform:none;color:var(--accent-ink);border-color:var(--accent-line)}
.do-list li > span:last-child{min-width:0}

/* ---------------------------------------------------------------------
   "How I work" — a LOOP, not a checklist  (`#about .do-loop`)

   The four items are not four independent bullets: research feeds a
   prototype, the prototype gets shipped, shipping teaches you what to
   learn next, and that sends you back to research. Rendering them as a
   bulleted list threw that relationship away and read as a generic CV
   block. So: numbered nodes on a spine, and a return path that carries
   04 back into 01. The claim the card makes is the SHAPE, not the prose.

   ⚠ The connector is anchored to the LI, not to the rail. A flex rail
   with `align-items:stretch` + `flex:1` was tried first and does not
   work here: the rail sizes to its own content and ends up centred, so
   the last connector collapsed to its `min-height`. Anchoring to the li
   (`position:relative`, already set) makes `top`/`bottom` deterministic
   however the sub-line wraps, which is the property that matters.

   Pseudo-element budget on this list is FULL — `li::before` is the
   connector (free only because the step number is now permanent inside
   the node), `li::after` is still the proof chip, `.do-ic::after` is
   still the hover ring. Anything new needs a real element.

   The spine deliberately runs PAST node 04 to the bottom edge before the
   return path takes it left and up: the alternative is pinning the
   bracket to node 04's centre, which cannot be expressed in CSS without
   hardcoding a row height that changes the moment text wraps.
   --------------------------------------------------------------------- */
.do-loop{position:relative;padding-left:23px}
.do-loop li{align-items:flex-start;margin-bottom:14px}
.do-loop li::after{left:70px}
.do-rail{display:block;flex-shrink:0}
.do-loop li::before{content:"";left:16px;right:auto;top:41px;bottom:-19px;width:2px;height:auto;border-radius:2px;opacity:1;transform:none;background:linear-gradient(180deg,var(--accent-line),color-mix(in srgb,var(--accent) 14%,transparent))}
.do-loop li:last-child::before{bottom:0}
.do-loop .do-ic{width:34px;height:34px;border-radius:50%;font-family:var(--font-mono);font-size:.7rem;font-weight:700;letter-spacing:.03em;margin-top:0}
.do-loop .do-ic::after{border-radius:50%;inset:-4px}
.do-tx{display:flex;flex-direction:column;gap:.1rem;padding-top:.35rem}
.do-loop strong{font-family:var(--font-display);font-size:1.02rem;font-weight:700;letter-spacing:-.012em;line-height:1.18}
.do-sub{font-size:.84rem;line-height:1.45;color:var(--text-mute);transition:color var(--t)}
.do-loop li:hover .do-sub{color:var(--text-soft)}

/* the return path — 04 back into 01. A left bracket plus an arrowhead:
   cheaper and sharper than an SVG, and it themes itself from the vars. */
/* `top:25px` is node 01's MEASURED centre (li padding + half the 34px
   node), and the arrowhead sits 4.5px above it so its tip lands on that
   same line. If you change the node size or the li padding, re-measure —
   the arrow pointing at nothing is the tell. */
.do-loop::before{content:"";position:absolute;left:0;top:25px;bottom:0;width:14px;border:2px solid var(--accent-line);border-right:0;border-radius:9px 0 0 9px;opacity:.55;pointer-events:none}
.do-loop::after{content:"";position:absolute;left:11px;top:20.5px;border:4.5px solid transparent;border-left-color:var(--accent);opacity:.75;pointer-events:none}

/* the loop RUNS: each node takes the beat in turn, 1.4s apart */
@keyframes doBeat{0%,9%{border-color:var(--accent);box-shadow:0 6px 14px -9px var(--accent-glow),0 0 0 0 color-mix(in srgb,var(--accent) 42%,transparent)}30%,100%{border-color:color-mix(in srgb,var(--accent) 26%,transparent);box-shadow:0 6px 14px -9px var(--accent-glow),0 0 0 7px transparent}}
.do-loop li .do-ic{animation:doBeat 5.6s var(--ease) infinite}
.do-loop li:nth-child(2) .do-ic{animation-delay:1.4s}
.do-loop li:nth-child(3) .do-ic{animation-delay:2.8s}
.do-loop li:nth-child(4) .do-ic{animation-delay:4.2s}
@media (prefers-reduced-motion:reduce){.do-loop li .do-ic{animation:none}}
@media (max-width:575.98px){.do-loop{padding-left:19px}.do-loop strong{font-size:.96rem}.do-sub{font-size:.8rem}}
.do-ic{position:relative}
.do-ic::after{content:"";position:absolute;inset:-4px;border:1px solid var(--accent-line);border-radius:13px;opacity:0;transform:scale(.78);pointer-events:none;transition:opacity .24s var(--ease),transform .32s var(--ease)}
.do-list li:hover .do-ic::after{opacity:.9;transform:scale(1)}
.do-list li:hover .do-ic i{animation:doPing .46s var(--ease)}
@keyframes doPing{45%{transform:scale(1.18) rotate(-6deg)}}
.edu-item{overflow:hidden;isolation:isolate}
.edu-item::before{content:"";position:absolute;left:0;right:0;bottom:0;height:2px;background:linear-gradient(90deg,var(--accent),var(--accent-2));transform:scaleX(0);transform-origin:left;transition:transform .38s var(--ease);box-shadow:0 0 10px var(--accent-glow)}
.edu-item::after{content:attr(data-proof);position:absolute;right:14px;bottom:7px;font-family:var(--font-mono);font-size:.55rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--accent-ink);opacity:0;transform:translateX(8px);pointer-events:none;transition:opacity .25s var(--ease),transform .25s var(--ease)}
.edu-item:hover::before{transform:scaleX(1)}
.edu-item:hover::after{opacity:1;transform:none}
.edu-grade{position:relative;overflow:hidden;z-index:0}
.edu-grade::before{content:"";position:absolute;inset:0 auto 0 0;width:var(--score,0%);background:color-mix(in srgb,var(--accent) 18%,transparent);transform:scaleX(.18);transform-origin:left;transition:transform .38s var(--ease)}
.edu-item:hover .edu-grade::before{transform:scaleX(1)}
.edu-badge i{transition:transform .3s var(--ease)}
.edu-item:hover .edu-badge i{animation:eduStamp .48s var(--ease)}
@keyframes eduStamp{45%{transform:translateY(-2px) rotate(-7deg) scale(1.12)}}
@media (max-width:575.98px){#about .about-quote::after,#about .ap-avail[data-readout]::after,.do-list li::after,.edu-item::after{display:none}.do-list li::before{right:2px;top:-2px}.edu-grade::before{transform:scaleX(1)}}
@media (prefers-reduced-motion:reduce){#about .about-quote::after,#about .ap-loc::before,#about .ap-avail[data-readout]::after,.do-list li::before,.do-list li::after,.do-ic::after,.edu-item::before,.edu-item::after,.edu-grade::before,.edu-badge i{transition:none}.do-list li:hover .do-ic i,.edu-item:hover .edu-badge i{animation:none}}

/* Hero quality loop: one meaningful craft system instead of disconnected widgets */
.hero-lead .kw-messy:hover .md-ch,.hero-lead .kw-messy.play .md-ch{transform:none}
.quality-loop{position:relative;max-width:62ch;margin:0 0 24px;padding:14px 16px 15px;border:1px solid var(--border-strong);border-radius:18px;background:linear-gradient(135deg,color-mix(in srgb,var(--surface) 82%,transparent),color-mix(in srgb,var(--accent) 7%,var(--surface)));box-shadow:0 18px 42px -30px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 50%,transparent);overflow:hidden;isolation:isolate}
.quality-loop::before{content:"";position:absolute;inset:0;background:radial-gradient(180px 90px at 12% 0%,color-mix(in srgb,var(--accent) 14%,transparent),transparent 70%),radial-gradient(220px 120px at 100% 100%,color-mix(in srgb,var(--accent-2) 10%,transparent),transparent 72%);pointer-events:none}
.quality-loop::after{content:"";position:absolute;inset:8px;border:1px dashed color-mix(in srgb,var(--accent) 20%,transparent);border-radius:14px;opacity:0;transform:scale(.985);transition:opacity .28s var(--ease),transform .28s var(--ease);pointer-events:none}
.quality-loop:hover::after{opacity:1;transform:none}
.quality-loop>*{position:relative;z-index:1}
.ql-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}
.quality-loop .craftline-kicker{margin:0;color:var(--accent-ink)}
.ql-proof{display:inline-flex;align-items:center;gap:.38rem;font-family:var(--font-mono);font-size:.62rem;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:var(--text-mute);white-space:nowrap}
.ql-proof i{color:#22c55e}
.ql-rail{position:absolute;left:30px;right:30px;top:60px;height:2px;background:color-mix(in srgb,var(--accent) 14%,var(--border));border-radius:2px;overflow:hidden}
.ql-rail span{display:block;width:36%;height:100%;border-radius:inherit;background:linear-gradient(90deg,transparent,var(--accent),var(--accent-2),transparent);animation:qlScan 3.2s var(--ease) infinite}
@keyframes qlScan{0%{transform:translateX(-110%)}58%,100%{transform:translateX(320%)}}
.ql-steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}
.ql-step{position:relative;display:flex;align-items:center;justify-content:center;gap:.48rem;min-height:46px;padding:.58rem .62rem;border-radius:13px;border:1px solid var(--border);background:color-mix(in srgb,var(--surface) 88%,transparent);font-family:var(--font-mono);font-size:.72rem;font-weight:850;color:var(--text);box-shadow:0 12px 24px -22px var(--accent-glow);transition:transform .28s var(--ease),border-color .28s var(--ease),background .28s var(--ease),box-shadow .28s var(--ease),color .28s var(--ease)}
.ql-step i{color:var(--accent-ink);transition:transform .28s var(--ease),filter .28s var(--ease)}
.ql-step::before{content:"";position:absolute;left:50%;top:-17px;width:8px;height:8px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 4px var(--accent-soft),0 0 12px var(--accent-glow);transform:translateX(-50%) scale(.72);opacity:.55;transition:opacity .24s var(--ease),transform .24s var(--ease)}
.ql-step::after{content:attr(data-proof);position:absolute;left:50%;bottom:calc(100% + 10px);max-width:190px;width:max-content;white-space:nowrap;transform:translate(-50%,8px) scale(.96);font-family:var(--font-mono);font-size:.57rem;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 94%,transparent);border:1px solid var(--accent-line);border-radius:7px;padding:.26rem .48rem;opacity:0;pointer-events:none;box-shadow:0 12px 24px -14px var(--accent-glow);transition:opacity .22s var(--ease),transform .22s var(--ease)}
.ql-step:hover,.ql-step.play{transform:translateY(-3px);border-color:var(--accent-line);background:color-mix(in srgb,var(--accent) 9%,var(--surface));box-shadow:0 16px 32px -22px var(--accent-glow)}
.ql-step:hover::before,.ql-step.play::before{opacity:1;transform:translateX(-50%) scale(1)}
.ql-step:hover::after,.ql-step.play::after{opacity:1;transform:translate(-50%,0) scale(1)}
.ql-step:hover i,.ql-step.play i{filter:drop-shadow(0 0 7px var(--accent-glow))}
.ql-frame:hover i,.ql-frame.play i{transform:rotate(90deg) scale(1.08)}
.ql-retrieve:hover i,.ql-retrieve.play i{transform:translateX(2px) scale(1.08)}
.ql-verify:hover i,.ql-verify.play i{transform:scale(1.12)}
.ql-ship:hover i,.ql-ship.play i{transform:translate(2px,-2px) rotate(-10deg)}
.ql-note{display:flex;align-items:center;justify-content:center;gap:.55rem;margin:12px 0 0;font-family:var(--font-mono);font-size:.68rem;color:var(--text-mute);letter-spacing:.02em}
.ql-note i{color:var(--accent-ink)}
.ql-note strong{color:var(--accent-ink);font-weight:850}
@media (max-width:575.98px){.quality-loop{margin-bottom:18px;padding:13px 12px 14px;border-radius:16px}.ql-head{align-items:flex-start;margin-bottom:10px}.ql-proof{font-size:.52rem;white-space:normal;text-align:right;line-height:1.35}.ql-rail{display:none}.ql-steps{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.ql-step{min-height:42px;font-size:.65rem;padding:.52rem .42rem}.ql-step::before,.ql-step::after{display:none}.ql-note{font-size:.62rem;justify-content:flex-start;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}}
@media (prefers-reduced-motion:reduce){.hero-lead .kw-messy:hover .md-ch,.hero-lead .kw-messy.play .md-ch,.ql-rail span{animation:none}.quality-loop::after,.ql-step,.ql-step i,.ql-step::before,.ql-step::after{transition:none}.ql-step:hover,.ql-step.play,.ql-step:hover i,.ql-step.play i{transform:none}}

/* About standout pass: visible craft around the bio, excluding Off the clock */
#about .about-quote{padding:18px 22px 18px 30px;border-left:0;border-radius:18px;background:linear-gradient(135deg,color-mix(in srgb,var(--surface) 74%,transparent),color-mix(in srgb,var(--accent) 7%,var(--surface)));border:1px solid var(--border-strong);box-shadow:0 18px 42px -32px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 48%,transparent)}
#about .about-quote::before{content:"";position:absolute;left:0;top:18px;bottom:18px;width:3px;border-radius:3px;background:linear-gradient(180deg,var(--accent),var(--accent-2));box-shadow:0 0 14px var(--accent-glow)}
#about .about-quote .q-mark{left:18px;top:10px;opacity:.24}
#about .about-quote .q-key{position:relative;z-index:0}
#about .about-quote .q-key::after{content:"";position:absolute;left:-3px;right:-3px;bottom:.02em;height:.16em;background:var(--accent);border-radius:999px;opacity:.28;z-index:-1;transform-origin:left;transform:scaleX(.35);transition:transform .45s var(--ease),opacity .3s var(--ease)}
#about .about-quote:hover .q-key::after{transform:scaleX(1);opacity:.42}
.about-thread{position:relative;display:grid;grid-template-columns:auto 1fr auto 1fr auto 1fr auto;align-items:center;gap:8px;margin:18px 0 22px;padding:13px 14px;border:1px solid var(--border);border-radius:16px;background:color-mix(in srgb,var(--surface) 72%,transparent);box-shadow:0 16px 34px -30px var(--accent-glow);overflow:visible}
.about-thread::before{content:"origin trace";position:absolute;left:14px;top:-10px;font-family:var(--font-mono);font-size:.56rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--accent-ink);background:var(--bg);border:1px solid var(--accent-line);border-radius:999px;padding:.12rem .42rem}
.thread-node{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:.4rem;min-height:34px;padding:.42rem .58rem;border-radius:11px;border:1px solid var(--border);background:color-mix(in srgb,var(--surface) 86%,transparent);font-family:var(--font-mono);font-size:.66rem;font-weight:850;color:var(--text);white-space:nowrap;transition:transform .28s var(--ease),border-color .28s var(--ease),background .28s var(--ease),box-shadow .28s var(--ease)}
.thread-node i{color:var(--accent-ink);transition:transform .28s var(--ease)}
.thread-node::after{content:attr(data-note);position:absolute;left:50%;bottom:calc(100% + 8px);transform:translate(-50%,7px) scale(.96);font-family:var(--font-mono);font-size:.56rem;font-weight:750;letter-spacing:.03em;color:var(--accent-ink);background:color-mix(in srgb,var(--surface) 94%,transparent);border:1px solid var(--accent-line);border-radius:7px;padding:.22rem .44rem;white-space:nowrap;opacity:0;pointer-events:none;box-shadow:0 12px 24px -16px var(--accent-glow);transition:opacity .22s var(--ease),transform .22s var(--ease)}
.thread-line{height:2px;border-radius:2px;background:linear-gradient(90deg,color-mix(in srgb,var(--accent) 22%,transparent),var(--accent),color-mix(in srgb,var(--accent-2) 40%,transparent));background-size:220% 100%;animation:threadFlow 3.4s linear infinite}
.thread-node:hover,.thread-node.play{transform:translateY(-2px);border-color:var(--accent-line);background:color-mix(in srgb,var(--accent) 8%,var(--surface));box-shadow:0 14px 28px -20px var(--accent-glow)}
.thread-node:hover::after,.thread-node.play::after{opacity:1;transform:translate(-50%,0) scale(1)}
.thread-node:hover i,.thread-node.play i{transform:scale(1.12)}
.thread-out{color:var(--accent-ink)}
@keyframes threadFlow{to{background-position:-220% 0}}
#about .about-tags{margin-top:14px}
#about .about-tag{box-shadow:0 10px 24px -22px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 48%,transparent)}
#about .about-tag::before{content:"";position:absolute;inset:3px;border-radius:999px;border:1px solid color-mix(in srgb,var(--accent) 16%,transparent);opacity:0;transform:scale(.96);transition:opacity .24s var(--ease),transform .24s var(--ease)}
#about .about-tag:hover::before{opacity:1;transform:scale(1)}
#about .about-profile{border-color:color-mix(in srgb,var(--accent) 28%,var(--border));box-shadow:0 24px 54px -34px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 48%,transparent)}
#about .about-profile::before{background:linear-gradient(115deg,transparent 0 42%,color-mix(in srgb,var(--accent) 12%,transparent) 48%,transparent 54% 100%),radial-gradient(480px 200px at 100% 0%,var(--accent-soft),transparent 60%);background-size:220% 100%,auto;background-position:130% 0,0 0;transition:background-position .75s var(--ease)}
#about .about-profile:hover::before{background-position:-40% 0,0 0}
#about .about-card:not(.about-hobbies-card){border-color:color-mix(in srgb,var(--accent) 24%,var(--border));box-shadow:0 24px 54px -34px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 48%,transparent)}
#about .about-card:not(.about-hobbies-card) .ac-head{padding-bottom:12px;border-bottom:1px dashed var(--border);margin-bottom:16px}
#about .about-card:not(.about-hobbies-card) .ac-head::after{content:"verified";margin-left:auto;font-family:var(--font-mono);font-size:.58rem;font-weight:850;letter-spacing:.1em;text-transform:uppercase;color:var(--accent-ink);background:var(--accent-soft);border:1px solid var(--accent-line);border-radius:999px;padding:.18rem .48rem}
#about .do-list li{padding:.46rem .6rem .46rem 0;margin-bottom:10px;border:1px solid transparent}
#about .do-list li:hover{border-color:var(--accent-line);background:color-mix(in srgb,var(--accent) 6%,transparent);transform:translateX(2px)}
/* ⚠ THE 23px TOP IS FOR THE `ORG` TAG — it is not styling, and taking it back to
   15px silently re-breaks the reported bug. The tag sits above the org name and
   needs 26px of room above that line's box; at 15px the card gave 18px
   (15 padding + 1 border + 2.25 of `.edu-top` centring) and `overflow:hidden`
   sliced 4px off the tag's top. ORG cannot go sideways instead — see §13b. */
#about .edu-item{padding:23px 16px 18px}
#about .edu-grade{box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--accent) 20%,transparent)}
@media (max-width:767.98px){.about-thread{grid-template-columns:1fr;gap:7px}.thread-line{width:2px;height:16px;justify-self:center;background:linear-gradient(180deg,var(--accent),var(--accent-2))}.thread-node{white-space:normal}.thread-node::after{display:none}}
@media (prefers-reduced-motion:reduce){.about-thread .thread-line{animation:none}.thread-node,.thread-node i,.thread-node::after,#about .about-tag::before,#about .about-profile::before,#about .about-quote .q-key::after{transition:none}}

/* Featured section fix: flagship plus compact support cards, no stretched towers */
@media (min-width:992px){.featured-layout{display:grid;grid-template-columns:minmax(0,1.12fr) minmax(360px,.88fr);gap:24px;align-items:start}.featured-layout>[class*="col-"]{width:auto;max-width:none;padding:0}.featured-layout>[class*="col-"]:first-child{grid-row:1 / span 2}.featured-layout .featured-card{height:auto}.featured-layout>[class*="col-"]:not(:first-child) .featured-card{display:grid;grid-template-columns:168px minmax(0,1fr);min-height:0}.featured-layout>[class*="col-"]:not(:first-child) .fc-media{height:100%;min-height:188px;aspect-ratio:auto}.featured-layout>[class*="col-"]:not(:first-child) .fc-body{padding:18px;gap:.55rem}.featured-layout>[class*="col-"]:not(:first-child) .fc-body h3{font-size:1rem;line-height:1.25}.featured-layout>[class*="col-"]:not(:first-child) .fc-body p{flex:0;font-size:.84rem;line-height:1.48}.featured-layout>[class*="col-"]:not(:first-child) .fc-tags{gap:.28rem}.featured-layout>[class*="col-"]:not(:first-child) .fc-tags span{font-size:.61rem;padding:.18rem .4rem}.featured-layout>[class*="col-"]:not(:first-child) .fc-actions{margin-top:.35rem;padding-top:.55rem}.featured-card.flagship{min-height:0}}
@media (min-width:992px) and (max-width:1199.98px){.featured-layout{grid-template-columns:minmax(0,1fr)}.featured-layout>[class*="col-"]:first-child{grid-row:auto}.featured-layout>[class*="col-"]:not(:first-child) .featured-card{grid-template-columns:190px minmax(0,1fr)}}


/* =====================================================================
   PERSONAL PASS — answers the "it looks like a company website" note.
   Three changes only, and none of them touch layout, colour, cards or type:
     1. the seven identical gradient headings are now three (class removed
        in index.html — no rule needed here)
     2. section labels rewritten in first person (index.html)
     3. certifications stop being a trust-badge wall — the big badge
        images hide and the cards compact. Every card, link and the
        Wave 11 credential-wallet hooks are untouched.
   ===================================================================== */
.is-compact .cert-media{display:none}
.is-compact .cert-card{padding:.85rem .95rem}
.is-compact .cert-head{gap:.65rem;align-items:center;margin:0}
.is-compact .cert-logo{width:30px;height:30px}
.is-compact .cert-head h4{font-size:.92rem;line-height:1.2;margin:0}
.is-compact .cert-head span{font-size:.68rem}
.is-compact .cert-foot{margin-top:.6rem;padding-top:.5rem;border-top:1px solid var(--border)}
.is-compact .cert-foot span{font-size:.66rem}
.is-compact #certGrid{--bs-gutter-y:.75rem;--bs-gutter-x:.75rem}


/* =====================================================================
   HERO right column - THE LIFECYCLE CLOUD (req/js/heroflow.js)
   ONE panel, not two. A point cloud walks ten stages, DATA -> MODEL ->
   SHIP, and the telemetry for each stage is printed OVER it: a headline
   figure, a readout strip, a proportional phase rail. The cloud says
   what is happening to the data, the numbers say what they are.
   Points are individually tinted (vertexColors), so Retrieve lights only
   its neighbourhood and Evaluate colours its two classes - the colour is
   data, not decoration.
   Chips are buttons - hover to peek, click to jump, arrows/0-9 to drive.
   ===================================================================== */
.hero-scene{display:flex;align-items:stretch}
.hs-card{display:flex;flex-direction:column;width:100%;padding:clamp(16px,1.7vw,24px);border-radius:var(--radius-lg);background:linear-gradient(135deg,var(--surface) 38%,color-mix(in srgb,var(--accent) 13%,var(--surface)));border:1px solid var(--border-strong);box-shadow:0 18px 40px -28px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 55%,transparent);overflow:hidden}
[data-theme="dark"] .hs-card{box-shadow:0 18px 40px -28px var(--accent-glow),inset 0 1px 0 rgba(255,255,255,.06)}

.hs-head{display:flex;align-items:center;justify-content:space-between;gap:1rem;font-family:var(--font-mono);font-size:.63rem;letter-spacing:.15em;text-transform:uppercase;color:var(--text-soft)}
.hs-id{display:inline-flex;align-items:center;gap:.45rem}
.hs-dot{width:6px;height:6px;border-radius:50%;background:#22C55E;box-shadow:0 0 0 3px rgba(34,197,94,.16);animation:hsPulse 2.4s ease-in-out infinite}
@keyframes hsPulse{0%,100%{box-shadow:0 0 0 3px rgba(34,197,94,.16)}50%{box-shadow:0 0 0 6px rgba(34,197,94,.05)}}
.hs-num{color:var(--accent-ink);font-weight:600;font-variant-numeric:tabular-nums}

.hs-stageline{margin-top:.85rem;min-height:3.4em}
/* Space Grotesk — same reason as .rr-name below: a card title, not a display
   heading. Stage names are single words ("Ingest", "Guardrails"). */
.hs-name{display:block;font-family:var(--font-display);font-weight:600;font-size:clamp(1.4rem,2.2vw,1.95rem);line-height:1.05;letter-spacing:-.02em;color:var(--text)}
.hs-cap{display:block;margin-top:.28rem;font-size:.92rem;line-height:1.45;color:var(--text-soft)}

/* 13 steps, ONE BLOCK PER PHASE, laid out in TWO COLUMNS, every name visible.
   History: thirteen numbers + thirteen names crammed in one row read as noise,
   so the names were hidden behind numbers — which was worse, because you then
   couldn't see what the thirteen steps ARE without hovering each one. Short
   labelled phase blocks solve both: the phase structure is legible at a glance
   and no step is hidden. The rail below keeps the progress fill but drops its
   labels, since these blocks carry them now.
   TWO COLUMNS: five stacked rows used barely half the card's 540px and left the
   right half empty, which forced the chips to stay tiny. Two columns turn five
   rows into three, and the ~50px of height that frees up (plus the whole right
   half) is spent making the chips bigger and easier to hit. The phase label sits
   ON ITS OWN LINE inside the block: horizontal room is the binding constraint
   here, so trading one short line for the 50px the inline label used to eat is
   what lets `10 GUARDRAILS` stay on one line.
   Chips GROW but never SHRINK (`flex:1 0 auto`) — growing squares off the right
   edge of each block, and refusing to shrink means a long name wraps to the next
   line instead of being ellipsised away.

   ⚠ ROW-MAJOR, NOT COLUMN-MAJOR. This first used `grid-auto-flow:column` to put
   the ingest→retrieve half (01-07) on the left and reason→ship (08-13) on the
   right. It reads wrong: your eye goes ACROSS a row first, so the top row showed
   `01 02 03` next to `08 09 10` and the numbers appeared to jump from 3 to 8.
   Normal row flow keeps them in order left-to-right, top-to-bottom —
   Data|Index, then Retrieve|Reason, then Ship. Do not reintroduce column flow.

   The 1:1.06 column ratio is MEASURED, not chosen, and row flow CHANGED it: the
   left column now holds Data/Retrieve/Ship (widest ~250px) and the right holds
   Index/Reason (widest ~263px, since `10 GUARDRAILS` alone is ~104px). That is
   much closer to even than the old 1:1.17 split. Chip padding and the chip gap
   were also shaved to buy back the ~9px a 3-chip row needed, because the two
   widest rows are now in DIFFERENT columns. Re-measure the natural chip widths
   before changing the font size, the padding or a stage NAME. */
.hs-steps{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.06fr);column-gap:.5rem;row-gap:.5rem;margin-top:.65rem}
.hx-prow{display:flex;flex-wrap:wrap;align-items:stretch;align-content:start;gap:.26rem}
.hx-ph{flex:0 0 100%;font-family:var(--font-mono);font-size:.54rem;letter-spacing:.14em;text-transform:uppercase;color:var(--text-mute);transition:color .3s var(--ease)}
.hx-prow.is-on .hx-ph{color:var(--accent-ink);font-weight:600}
.hs-step{display:inline-flex;align-items:center;justify-content:center;gap:.32rem;flex:1 0 auto;min-width:0;white-space:nowrap;font-family:var(--font-mono);font-size:.64rem;letter-spacing:.04em;text-transform:uppercase;color:var(--text-soft);background:transparent;border:1px solid var(--border);border-radius:var(--radius-sm);padding:.46rem .42rem;cursor:pointer;transition:color .3s var(--ease),border-color .3s var(--ease),background .3s var(--ease)}
.hs-step b{font-size:.66rem;font-weight:600;font-variant-numeric:tabular-nums;color:var(--text-mute);transition:color .3s var(--ease)}
.hx-lbl{overflow:hidden;text-overflow:ellipsis}
.hs-step:hover{color:var(--text);border-color:var(--border-strong)}
.hs-step.is-done{color:var(--text-mute)}
.hs-step.is-on{color:var(--accent-ink);border-color:var(--accent-line);background:var(--accent-soft)}
.hs-step.is-on b{color:var(--accent-ink)}

/* the worked-example line — anchors all twelve stages to one story */
.hx-case{display:flex;align-items:center;gap:.4rem;margin:.55rem 0 0;font-size:.72rem;line-height:1.35;color:var(--text-mute)}
.hx-case i{color:var(--accent);font-size:.8rem}
.hx-case b{color:var(--text-soft);font-weight:600}

/* --- card shell ---------------------------------------------------- */
.hx-card{outline:none;transition:border-color var(--t) var(--ease)}
.hx-card:focus-visible{border-color:var(--accent);box-shadow:0 18px 40px -28px var(--accent-glow),0 0 0 3px var(--accent-soft)}
.hx-card.is-paused .hs-dot{background:var(--text-mute);box-shadow:0 0 0 3px color-mix(in srgb,var(--text-mute) 16%,transparent);animation:none}
.hx-head-r{display:inline-flex;align-items:center;gap:.55rem}
.hx-phase{padding:.16rem .45rem;border-radius:3px;font-size:.55rem;font-weight:600;letter-spacing:.16em;background:var(--accent-soft);color:var(--accent-ink)}

/* --- the stage: cloud underneath, telemetry on top ------------------ */
/* Tokens need room to read as words rather than fuzzier dots — this is
   deliberately taller than the dot cloud needed, and a taller box also means
   more px per world unit, so the text itself gets bigger. `fitCloud()` in
   heroflow.js rescales the formation to whatever aspect this ends up being. */
.hx-stage{position:relative;height:clamp(290px,28vw,400px);margin:.8rem 0;border-radius:var(--radius);overflow:hidden;background:color-mix(in srgb,var(--surface) 55%,transparent)}
.hx-stage canvas{display:block;width:100%;height:100%;opacity:0;transition:opacity 1s ease}
.hx-stage.has-gl canvas{opacity:1}
.hx-stage::before{content:"";position:absolute;inset:0;background-image:linear-gradient(var(--grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);background-size:30px 30px;-webkit-mask-image:radial-gradient(ellipse 80% 72% at 50% 50%,#000 18%,transparent 82%);mask-image:radial-gradient(ellipse 80% 72% at 50% 50%,#000 18%,transparent 82%);opacity:.65;pointer-events:none}
/* corner brackets, so the stage reads as an instrument and not wallpaper */
.hx-fr{position:absolute;width:14px;height:14px;border:1.5px solid var(--accent-line);pointer-events:none;opacity:.85}
.hx-fr.tl{left:9px;top:9px;border-right:0;border-bottom:0;border-radius:5px 0 0 0}
.hx-fr.tr{right:9px;top:9px;border-left:0;border-bottom:0;border-radius:0 5px 0 0}
.hx-fr.bl{left:9px;bottom:9px;border-right:0;border-top:0;border-radius:0 0 0 5px}
.hx-fr.br{right:9px;bottom:9px;border-left:0;border-top:0;border-radius:0 0 5px 0}

.hx-stage.is-in .hx-metric{animation:hxUp .45s var(--ease)}
@keyframes hxUp{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}

.hx-metric{position:absolute;left:1.15rem;top:1rem;display:flex;align-items:baseline;gap:.4rem;pointer-events:none}
.hx-metric b{font-family:var(--font-display);font-size:clamp(1.6rem,3vw,2.3rem);line-height:1;font-weight:700;letter-spacing:-.03em;color:var(--accent-ink);font-variant-numeric:tabular-nums;text-shadow:0 1px 12px var(--accent-glow)}
.hx-metric span{font-family:var(--font-mono);font-size:.58rem;letter-spacing:.1em;text-transform:uppercase;color:var(--text-soft)}

/* --- orbit controls ------------------------------------------------- */
/* pan-y, not none: a vertical swipe still scrolls the page on a phone,
   a horizontal one rotates the cloud. */
.hx-stage.is-orbit{cursor:grab;touch-action:pan-y}
.hx-stage.is-grabbing{cursor:grabbing}
.hx-ctl{position:absolute;right:.85rem;top:.85rem;display:flex;flex-direction:column;gap:.25rem;z-index:2}
.hx-ctl button{width:24px;height:24px;display:grid;place-items:center;padding:0;border-radius:6px;border:1px solid var(--border);background:color-mix(in srgb,var(--surface) 86%,transparent);color:var(--text-soft);font-family:var(--font-mono);font-size:.78rem;line-height:1;cursor:pointer;transition:color var(--t-fast) var(--ease),border-color var(--t-fast) var(--ease),background var(--t-fast) var(--ease)}
.hx-ctl button:hover{color:var(--accent-ink);border-color:var(--accent-line);background:var(--accent-soft)}
.hx-ctl button i{font-size:.68rem}
.hx-hint{position:absolute;right:.85rem;bottom:.95rem;padding:.16rem .45rem;border-radius:3px;background:color-mix(in srgb,var(--surface) 86%,transparent);border:1px solid var(--border);font-family:var(--font-mono);font-size:.52rem;letter-spacing:.06em;color:var(--text-mute);pointer-events:none;transition:opacity .4s var(--ease)}
.hx-hint.is-gone{opacity:0}
@media (max-width:575.98px){.hx-hint{display:none}}

/* --- TECHNIQUE / STACK spec strip -----------------------------------
   Moved out from an overlay inside the canvas onto its own block under it.
   The diagram shows WHAT the step does; this names HOW the industry does
   it and WHAT you reach for. Two label/value rows, so it reads as a spec
   sheet rather than a row of loose tags. */
.hx-spec{display:grid;gap:.34rem;margin:0 0 .8rem;padding:.7rem .9rem;border-radius:var(--radius);border:1px solid var(--border);background:color-mix(in srgb,var(--surface) 72%,transparent)}
.hx-spec > div{display:grid;grid-template-columns:64px minmax(0,1fr);gap:.75rem;align-items:baseline}
.hx-spec dt{font-family:var(--font-mono);font-size:.52rem;letter-spacing:.16em;text-transform:uppercase;color:var(--text-mute)}
.hx-spec dd{margin:0;font-size:.78rem;line-height:1.45;color:var(--text-soft)}
.hx-stack{display:flex;flex-wrap:wrap;gap:.28rem}
.hx-stack span{padding:.12rem .42rem;border-radius:4px;border:1px solid var(--accent-line);background:var(--accent-soft);font-family:var(--font-mono);font-size:.62rem;color:var(--accent-ink);white-space:nowrap;animation:hxUp .4s var(--ease) backwards;animation-delay:calc(var(--i,0) * .06s + .06s)}

/* --- phase rail: progress that also tells you WHERE you are --------- */
.hx-rail{display:flex;gap:.3rem}
.hx-seg{position:relative;flex:var(--n,1);display:block;padding-top:.3rem}
.hx-seg::before{content:"";display:block;height:3px;border-radius:2px;background:var(--border)}
.hx-seg i{position:absolute;left:0;top:.3rem;height:3px;width:100%;border-radius:2px;background:var(--accent);transform-origin:left;transform:scaleX(var(--f,0));transition:transform .12s linear}
/* labels live on the chip rows now — the rail is pure progress */

.hx-step{position:relative;overflow:hidden}
.hx-step::after{content:"";position:absolute;left:0;bottom:0;width:100%;height:2px;background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform var(--t) var(--ease)}
.hx-step.is-on::after{transform:scaleX(1)}

/* 992-1279: the WORST case, and not an obvious one. The hero has already split
   into two columns, but the card only gets ~1/2 of the container — so it is
   ~450px at 1024 and ~505px at 1200, and MEASURED, two columns need ~515px
   before `10 GUARDRAILS` stops wrapping (the crossover lands near 1222px wide).
   So below 1280 drop back to one column and put the phase label back INLINE:
   one inline row per phase is far shorter than five label-on-top blocks, and the
   ~355px left over is plenty for the widest row, so the chips stay big.
   If you retune the chip size, re-measure this crossover — it moves. */
@media (min-width:992px) and (max-width:1279.98px){.hs-steps{grid-template-columns:minmax(0,1fr);grid-template-rows:auto;grid-auto-flow:row;row-gap:.4rem}.hx-prow{align-items:center}.hx-ph{flex:0 0 50px;font-size:.5rem}.hs-step{font-size:.62rem;padding:.42rem .48rem}}
/* Tablet: the hero stacks, so the card goes full-width and the two columns get
   roomier, not tighter. Never set plain `gap` here — it would reset the
   asymmetric column-gap the grid depends on. */
@media (max-width:991.98px){.hx-stage{height:320px}.hs-steps{column-gap:.6rem;row-gap:.45rem}.hs-step{font-size:.62rem;padding:.4rem .5rem}}
/* Phone: one column again — half of ~340px cannot hold `10 GUARDRAILS`. Chips
   still fill the row and stay ~34px tall; below that they stop being tappable. */
@media (max-width:575.98px){.hx-stage{height:290px}.hs-steps{grid-template-columns:minmax(0,1fr);grid-template-rows:auto;grid-auto-flow:row;row-gap:.4rem}.hx-prow{gap:.26rem}.hx-ph{font-size:.5rem}.hs-step{font-size:.58rem;padding:.42rem .44rem}.hx-metric{left:.85rem;top:.8rem}.hx-spec{padding:.6rem .75rem}.hx-spec > div{grid-template-columns:58px minmax(0,1fr);gap:.5rem}.hx-case{font-size:.68rem}}
@media (prefers-reduced-motion:reduce){
    .hs-dot{animation:none}
    .hx-stage canvas{transition:none}
    .hx-stage.is-in .hx-metric,.hx-stack span{animation:none}
    .hx-seg i{transition:none}
}



/* =====================================================================
   FEATURED — three FULL-WIDTH rows (req/js/featuredrun.js)
   Steps run down the left, the live output fills the right. Wide and
   short rather than narrow and tall, so every step label is readable and
   the output has room to breathe without the card getting huge.
   Wave 2's HUD brackets are off here (they read as a second frame with a
   bordered output panel inside); still active on .project-card.
   ===================================================================== */
@media (min-width:992px){
    .featured-layout{display:grid!important;grid-template-columns:minmax(0,1fr)!important;gap:16px!important;align-items:stretch!important}
    .featured-layout>[class*="col-"]{grid-row:auto!important;width:auto!important;max-width:none!important;padding:0!important}
    .featured-layout>[class*="col-"] .featured-card{display:flex!important;flex-direction:column!important;grid-template-columns:none!important;height:auto!important}
    .featured-layout>[class*="col-"] .fc-body{padding:20px 22px!important;gap:0!important;display:flex!important;flex-direction:column!important}
    .featured-layout>[class*="col-"] .fc-body h3{font-size:1.15rem!important;line-height:1.2!important}
    .featured-layout>[class*="col-"] .fc-actions{margin-top:0!important;padding-top:.75rem!important}
}
.featured-card.is-runrow{display:flex;flex-direction:column;height:auto}
.featured-card.is-runrow::before{display:none!important;content:none!important}
.featured-card.is-runrow .fc-media,.featured-card.is-runrow .fc-body > p,
.featured-card.is-runrow .fc-tags,.featured-card.is-runrow .fc-proof{display:none!important}
.featured-card.is-runrow h3{font-size:1.15rem;line-height:1.2;margin:.22rem 0 0}
.featured-card.is-runrow .fc-label{font-size:.55rem}
.featured-card.is-runrow .fc-actions{margin-top:0;padding-top:.75rem;border-top:1px solid var(--border);display:flex;justify-content:space-between;gap:.5rem}
.featured-card.is-runrow .fc-link{font-size:.8rem}

.rr-run{display:flex;flex-direction:column;margin:.7rem 0 .5rem;min-width:0}

/* top bar spans the full row */
.rr-bar{display:flex;align-items:center;gap:.8rem;flex-wrap:wrap;padding-bottom:.6rem;border-bottom:1px solid var(--border);font-family:var(--font-mono);font-size:.58rem;letter-spacing:.12em;text-transform:uppercase;color:var(--text-soft)}
.rr-id{display:inline-flex;align-items:center;gap:.4rem;color:var(--text);font-weight:600;flex-shrink:0}
.rr-dot{width:6px;height:6px;border-radius:50%;background:#22C55E;box-shadow:0 0 0 2.5px rgba(34,197,94,.16);animation:rrPulse 2.2s ease-in-out infinite;flex-shrink:0}
@keyframes rrPulse{0%,100%{box-shadow:0 0 0 2.5px rgba(34,197,94,.16)}50%{box-shadow:0 0 0 5px rgba(34,197,94,.05)}}
.rr-ctx{color:var(--text-mute);letter-spacing:.04em;text-transform:none;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rr-count{margin-left:auto;color:var(--accent-ink);font-weight:600;font-variant-numeric:tabular-nums;flex-shrink:0}

/* the split — steps left, output right */
.rr-grid{display:grid;grid-template-columns:214px minmax(0,1fr);gap:20px;margin-top:.8rem;align-items:start}

.rr-side{min-width:0}
.rr-chips{display:flex;flex-direction:column;gap:.18rem}
.rr-group{margin:.35rem 0 .1rem;font-family:var(--font-mono);font-size:.46rem;letter-spacing:.16em;text-transform:uppercase;color:var(--text-mute)}
.rr-group:first-child{margin-top:0}
.rr-chip{display:flex;align-items:center;gap:.45rem;width:100%;padding:.32rem .5rem;border-radius:var(--radius-sm);border:1px solid transparent;background:transparent;cursor:pointer;font-family:var(--font-mono);font-size:.6rem;letter-spacing:.02em;color:var(--text-mute);transition:color .28s,border-color .28s,background .28s;text-align:left;min-width:0}
.rr-chip b{font-size:.55rem;font-weight:600;opacity:.55;font-variant-numeric:tabular-nums;flex-shrink:0}
.rr-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rr-chip:hover{color:var(--text-soft);background:var(--surface-2)}
.rr-chip.is-done{color:var(--text-soft)}
.rr-chip.is-done b{color:#16A34A;opacity:1}
.rr-chip.is-on{color:var(--on-accent,#fff);background:var(--accent);border-color:var(--accent)}
.rr-chip.is-on b{opacity:.8;color:inherit}

/* right side */
.rr-main{display:flex;flex-direction:column;min-width:0}
/* Space Grotesk, NOT Fraunces. This is a card title at 1.3rem — the display
   serif is for display sizes only (h1/h2), and these names are short strings
   like "QA" and "Amazon Aurora" where a WONKed serif just reads as a different
   typeface bolted on. The Fraunces axes go with it: `opsz/SOFT/WONK` mean
   nothing to Space Grotesk and only invite the Wave-6 trap later. */
.rr-name{display:block;font-family:var(--font-display);font-weight:600;font-size:1.3rem;line-height:1.1;letter-spacing:-.018em;color:var(--text)}
.rr-out{margin:.6rem 0 .7rem;min-height:150px;padding:.85rem .95rem;border-radius:var(--radius);border:1px solid var(--border);background:var(--surface);overflow:hidden;opacity:0;transform:translateY(3px)}
.rr-out.is-in{animation:rrIn .35s var(--ease) forwards}
@keyframes rrIn{to{opacity:1;transform:none}}
.rr-progress{height:2px;border-radius:2px;background:var(--border);overflow:hidden}
.rr-progress i{display:block;height:100%;background:var(--accent);transform-origin:left;transform:scaleX(0)}

/* ---- output renderers — wide row means they can breathe ---- */
.ro-code{margin:0;padding:0}
.ro-code code{font-family:var(--font-mono);font-size:.7rem;line-height:1.7;color:var(--text);white-space:pre-wrap;overflow-wrap:anywhere;display:block}
.ro-badges{display:flex;flex-direction:column;gap:.32rem}
.ro-badge{display:grid;grid-template-columns:auto 1fr;gap:.55rem;align-items:baseline;font-family:var(--font-mono);font-size:.68rem;line-height:1.5}
.ro-badge b{padding:.14rem .42rem;border-radius:3px;background:var(--accent);color:var(--on-accent,#fff);font-size:.54rem;letter-spacing:.08em;white-space:nowrap}
.ro-badge span{color:var(--text-soft);overflow-wrap:anywhere}
.ro-pills{display:flex;flex-wrap:wrap;gap:.3rem}
.ro-pill{padding:.22rem .5rem;border-radius:999px;border:1px solid var(--border-strong);font-family:var(--font-mono);font-size:.63rem;color:var(--text-soft);white-space:nowrap}
.ro-pill.is-pos{background:rgba(34,197,94,.12);border-color:rgba(34,197,94,.35);color:#16A34A}
.ro-pill.is-neg{background:rgba(220,38,38,.10);border-color:rgba(220,38,38,.32);color:#DC2626}
.ro-pill.is-neu{background:var(--surface-2);color:var(--text-mute)}
.ro-group{margin-bottom:.5rem}
.ro-group > b{display:block;font-family:var(--font-mono);font-size:.52rem;letter-spacing:.16em;color:var(--text-mute);margin-bottom:.2rem}
.ro-stats{display:flex;flex-wrap:wrap;gap:1.1rem;margin-top:.6rem;padding-top:.55rem;border-top:1px solid var(--border)}
.ro-stats span{font-family:var(--font-mono);font-size:.62rem;color:var(--text-mute);white-space:nowrap}
.ro-stats b{color:var(--accent-ink);font-size:1.05rem;margin-right:.2rem;font-family:var(--font-display)}
.ro-note{margin:.6rem 0 0;font-size:.82rem;line-height:1.55;color:var(--text-soft)}
.ro-note b{color:var(--text);font-weight:600}

@media (max-width:991.98px){
    .rr-grid{grid-template-columns:1fr;gap:14px}
    .rr-chips{flex-direction:row;flex-wrap:wrap}
    .rr-chip{width:auto;border-color:var(--border)}
    .rr-group{width:100%}
    .rr-out{min-height:0}
}
@media (prefers-reduced-motion:reduce){.rr-dot{animation:none}.rr-out{opacity:1;transform:none;animation:none}}

/* tighten the step list so the 12-step NLP row isn't noticeably taller */
.rr-chips{gap:.12rem}
.rr-chip{padding:.26rem .5rem;font-size:.585rem}
.rr-group{margin:.28rem 0 .06rem}

/* =====================================================================
   FEATURED — output styled to match the real app screens
   The app renders Order of Execution as stacked white cards with a left
   accent bar and a solid badge; code blocks with keyword highlighting.
   This mirrors that. Vanilla-Tilt is destroyed on these rows in JS — a
   1320px card skewing under the cursor read as the layout sliding.
   ===================================================================== */
.featured-card.is-runrow{transform:none!important;transition:box-shadow .35s var(--ease),border-color .35s var(--ease)!important}
.featured-card.is-runrow:hover{transform:none!important;box-shadow:0 22px 48px -30px var(--accent-glow)}
.featured-card.is-runrow .js-tilt-glare{display:none!important}

/* --- badge rows: white card + left accent bar + solid pill, like the app --- */
.ro-badges{gap:.3rem}
.ro-badge{display:flex;align-items:center;gap:.6rem;padding:.4rem .6rem .4rem .55rem;border-radius:var(--radius-sm);background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--accent);box-shadow:0 1px 2px rgba(15,23,42,.04)}
[data-theme="dark"] .ro-badge{background:var(--surface-2);box-shadow:none}
.ro-badge b{padding:.2rem .5rem;border-radius:4px;background:var(--accent);color:#fff;font-family:var(--font-mono);font-size:.55rem;font-weight:600;letter-spacing:.1em;white-space:nowrap;flex-shrink:0}
.ro-badge span{font-family:var(--font-mono);font-size:.72rem;color:var(--text);overflow-wrap:anywhere}
.ro-badge span::before{content:":";color:var(--text-mute);margin-right:.45rem}

/* --- code: keyword highlighting like the app's SQL panel --- */
.ro-code{margin:0;padding:.7rem .8rem;border-radius:var(--radius-sm);background:var(--code-bg);border:1px solid var(--border)}
.ro-code code{font-size:.72rem;line-height:1.75}
.ro-code .kw{font-style:normal;color:var(--accent-ink);font-weight:700}
[data-theme="dark"] .ro-code .kw{color:var(--accent)}
.ro-code .op{font-style:normal;color:var(--text-mute)}

/* --- pills: closer to the app's entity/sentiment chips --- */
.ro-pill{padding:.26rem .55rem;font-size:.66rem;background:var(--surface);border-color:var(--border);box-shadow:0 1px 2px rgba(15,23,42,.04)}
[data-theme="dark"] .ro-pill{background:var(--surface-2);box-shadow:none}
.ro-pill.is-pos{background:#16A34A;border-color:#16A34A;color:#fff;font-weight:600}
.ro-pill.is-neg{background:#DC2626;border-color:#DC2626;color:#fff;font-weight:600}
.ro-pill.is-neu{background:#6B7280;border-color:#6B7280;color:#fff;font-weight:600}

/* --- group headers like the app's ORG / GPE / DATE labels --- */
.ro-group > b{font-size:.55rem;letter-spacing:.18em;color:var(--text-mute)}

.ro-note{font-size:.8rem;color:var(--text-soft)}
.rr-out{padding:1rem 1.1rem;background:color-mix(in srgb,var(--surface) 92%,transparent)}

/* steps 2-up inside the left column — halves the wasted height on the
   12-step NLP row without shrinking the type */
.rr-grid{grid-template-columns:330px minmax(0,1fr)}
.rr-chips{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.2rem}
.rr-group{grid-column:1/-1}
.rr-chip{width:auto}
@media (max-width:1199.98px){.rr-grid{grid-template-columns:300px minmax(0,1fr)}}
@media (max-width:991.98px){.rr-grid{grid-template-columns:1fr}.rr-chips{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* =====================================================================
   FEATURED — legibility pass
   Bigger step type, and real vertical rhythm inside the output panel.
   The clutter came from stacked blocks (badges + stats + note) sitting
   flush against each other with no separation.
   ===================================================================== */

/* --- steps: larger, roomier --- */
.rr-grid{grid-template-columns:368px minmax(0,1fr);gap:26px}
.rr-chips{gap:.3rem}
.rr-chip{padding:.46rem .6rem;font-size:.72rem;letter-spacing:.01em;gap:.5rem;border-radius:6px}
.rr-chip b{font-size:.66rem;opacity:.6}
.rr-group{margin:.55rem 0 .15rem;font-size:.55rem;letter-spacing:.18em}
.rr-group:first-child{margin-top:0}

/* --- output: consistent rhythm between stacked blocks --- */
.rr-out{padding:1.15rem 1.25rem;line-height:1.6}
.rr-out > * + *{margin-top:.85rem}

/* badge rows breathe */
.ro-badges{gap:.42rem}
.ro-badge{padding:.5rem .7rem .5rem .65rem;gap:.7rem;align-items:flex-start;line-height:1.55}
.ro-badge b{padding:.22rem .52rem;font-size:.58rem;line-height:1.35;margin-top:.02rem}
.ro-badge span{font-size:.74rem;line-height:1.6}

/* code */
.ro-code{padding:.85rem .95rem}
.ro-code code{font-size:.73rem;line-height:1.95}

/* pills */
.ro-pills{gap:.36rem;line-height:1.9}
.ro-pill{padding:.3rem .6rem;font-size:.68rem}

/* entity / feature groups */
.ro-group{margin-bottom:.75rem}
.ro-group:last-child{margin-bottom:0}
.ro-group > b{font-size:.58rem;margin-bottom:.34rem;letter-spacing:.18em}

/* stats row sits apart from whatever is above it */
.ro-stats{gap:1.4rem;margin-top:.95rem;padding-top:.75rem}
.ro-stats span{font-size:.66rem}
.ro-stats b{font-size:1.15rem;margin-right:.25rem}

/* the explanatory line gets clear air */
.ro-note{margin-top:.95rem;padding-top:.75rem;border-top:1px solid var(--border);font-size:.83rem;line-height:1.65}

/* stage title + row header spacing */
.rr-name{font-size:1.4rem;margin-bottom:.15rem}
.rr-bar{padding-bottom:.7rem;font-size:.62rem}
.rr-grid{margin-top:1rem}

@media (max-width:1199.98px){.rr-grid{grid-template-columns:320px minmax(0,1fr);gap:20px}}
@media (max-width:991.98px){
    .rr-grid{grid-template-columns:1fr}
    .rr-chip{font-size:.72rem}
}

/* =====================================================================
   FEATURED — steps made legible
   They were --text-mute on a light card, which reads as disabled rather
   than "not the current step". Raised to --text-soft with a visible
   surface + border, bumped the type, and kept the active/done states
   clearly distinct from each other.
   ===================================================================== */
.rr-grid{grid-template-columns:392px minmax(0,1fr)}

.rr-chip{
    font-size:.82rem;
    letter-spacing:.005em;
    padding:.55rem .7rem;
    gap:.55rem;
    color:var(--text-soft);
    background:var(--surface-2);
    border:1px solid var(--border);
    border-radius:7px;
}
.rr-chip b{font-size:.74rem;opacity:1;color:var(--text-mute);font-weight:700}

.rr-chip:hover{color:var(--text);background:var(--surface);border-color:var(--accent-line)}

/* completed — clearly read as done, not disabled */
.rr-chip.is-done{color:var(--text);background:rgba(34,197,94,.08);border-color:rgba(34,197,94,.3)}
.rr-chip.is-done b{color:#16A34A}

/* current */
.rr-chip.is-on{color:var(--on-accent,#fff);background:var(--accent);border-color:var(--accent);font-weight:600;box-shadow:0 4px 12px -6px var(--accent-glow)}
.rr-chip.is-on b{color:#fff;opacity:.85}

/* group labels were the faintest thing on the card */
.rr-group{font-size:.62rem;letter-spacing:.16em;color:var(--text-soft);font-weight:600;margin:.7rem 0 .25rem}
.rr-group:first-child{margin-top:0}

@media (max-width:1199.98px){.rr-grid{grid-template-columns:340px minmax(0,1fr)}.rr-chip{font-size:.76rem}}
@media (max-width:991.98px){.rr-grid{grid-template-columns:1fr}.rr-chip{font-size:.8rem}}

/* =====================================================================
   FEATURED — entity groups as rows, not stacked blocks
   ORG / FLIGHT / GPE / DATE / FAC were five stacked label+pills blocks,
   which made the NER panel enormous. Now each group is one table-like
   row: label in a fixed left column, pills flowing on the right.
   ===================================================================== */
.ro-group{display:grid;grid-template-columns:74px minmax(0,1fr);gap:.7rem;align-items:baseline;margin-bottom:.42rem;padding-bottom:.42rem;border-bottom:1px solid var(--border)}
.ro-group:last-child{margin-bottom:0;padding-bottom:0;border-bottom:0}
.ro-group > b{margin:0;font-size:.56rem;letter-spacing:.14em;color:var(--text-mute);text-align:right;line-height:1.9}
.ro-group .ro-pills{gap:.3rem;line-height:1.7}
.ro-group .ro-pill{padding:.22rem .5rem;font-size:.64rem}

/* =====================================================================
   HERO · COMPOSITION PASS — hierarchy over inventory
   The left column had grown to nine stacked blocks (terminal chip, hello,
   name, tagline, lead, quality-loop card, CTAs, status line, stat rail) at
   634 x 893px, with margins running 24/4/18/22/30/24/22/16 — near-uniform,
   so nothing grouped and nothing separated and the eye just fell down a
   list. Three blocks are gone from the markup; the rest is regrouped into
   three clusters — identity · pitch · action — tight inside, wide between.
   No new colour and no new component: this only sets rhythm and repairs
   two elements that read as accidental.
   ===================================================================== */
/* The columns were centred against each other, which was invisible while the
   copy ran 893px against the panel's 854px. At 610px it would have floated the
   name 122px down the page, so the two columns share a top edge instead. */
.hero-inner{align-items:start}
.hero-main .hero-hello{margin-bottom:6px}
.hero-main .hero-name{margin-bottom:34px}
.hero-main .hero-tagline{margin-bottom:16px}
.hero-main .hero-lead{margin-bottom:42px}
/* .hero-ctas' own margin-bottom closes the action cluster — it is set further
   down, next to the note explaining why it cannot live on .hero-stats-row. */

/* The portrait sat at .62em against Fraunces' ~.7em cap height, so it read
   as a sticker pinned to the A rather than a letterform inside the word. */
.hero-name .hn-pic{width:.72em;height:.72em}

/* The lead carried six emphasised runs in 34 words. The 12-word bold closing
   clause outweighed the three accent keys, so the eye landed on the end of
   the paragraph instead of on the specialisms. Weight drops to 500; the
   .kw-ship underline draw (Wave 12b) is untouched. */
.hero-lead .kw-ship{font-weight:500}

/* Third stat tile. .hero-stats is repeat(3,1fr) but only two tiles were left,
   so a 179px column sat empty and the rail read as broken. Availability fills
   it — the highest-value fact here for anyone scanning — and it used to be a
   separate .hero-status line below, so this removes a block as well as a hole.
   The live dot is an ::after, NOT a nested span: motion.js 4j and main.js
   both assign textContent on [data-count] tiles and would wipe a child node. */
.hero-stats .hs-open dt{display:inline-flex;align-items:center;gap:.42rem}
.hero-stats .hs-open dt::after{content:"";width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 0 0 var(--accent-glow);animation:pulse 2s ease-in-out infinite;flex-shrink:0}
@media (prefers-reduced-motion:reduce){.hero-stats .hs-open dt::after{animation:none}}

/* =====================================================================
   HERO · ASK CONSOLE (Wave 19) — the fake terminal, made real
   Paired with req/js/ask.js. The chip removed in the composition pass printed
   a fake shell command at the TOP of the column and did nothing; this is the
   same instrument-panel idea with the joke removed, anchoring the BOTTOM of the
   column with a prompt that actually answers. It reuses the WALL-E widget
   wholesale, so the most on-brand element an AI engineer's page can carry costs
   no new backend and no new dependency.
   The console labels itself via [data-readout], which is also what keeps the
   curio cursor quiet over it (cursor.js SUPPRESS_SELECTOR group 1).
   ===================================================================== */
.hero-ask-wrap{max-width:540px;margin-top:34px}
.hero-ask{position:relative;display:flex;align-items:center;gap:.55rem;padding:.6rem .6rem .6rem .85rem;border:1px solid var(--border-strong);border-radius:14px;background:linear-gradient(135deg,color-mix(in srgb,var(--surface) 88%,transparent),color-mix(in srgb,var(--accent) 5%,var(--surface)));box-shadow:0 16px 38px -30px var(--accent-glow),inset 0 1px 0 color-mix(in srgb,#fff 46%,transparent);overflow:hidden;isolation:isolate;transition:border-color .3s var(--ease),box-shadow .3s var(--ease),transform .3s var(--ease)}
.hero-ask:hover{border-color:var(--accent-line);transform:translateY(-1px);box-shadow:0 22px 46px -30px var(--accent-glow)}
.hero-ask.is-on{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft),0 22px 46px -28px var(--accent-glow);transform:translateY(-1px)}

/* The prompt arrow is the console's "live" tell. It shifts and brightens the
   moment the field takes focus, so the console acknowledges you before the
   model does. */
.ha-caret{flex:0 0 auto;font-family:var(--font-mono);font-size:1rem;font-weight:700;line-height:1;color:var(--accent-ink);opacity:.62;transition:opacity .3s var(--ease),transform .3s var(--ease)}
.hero-ask.is-on .ha-caret{opacity:1;transform:translateX(2px)}

.ha-input{flex:1 1 auto;min-width:0;border:0;outline:0;background:none;font-family:var(--font-mono);font-size:.82rem;color:var(--text);padding:.24rem 0}
.ha-input::placeholder{color:var(--text-mute);opacity:1}
.ha-input:focus{outline:0}

/* Provenance, not decoration: it names WHICH model answers. Both this and the
   key badge clear out the moment you engage, so the field never competes with
   the text you are actually entering. */
.ha-brand{flex:0 0 auto;font-family:var(--font-mono);font-size:.58rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--text-mute);opacity:.75;transition:opacity .25s var(--ease)}
.hero-ask.is-on .ha-brand,.hero-ask.has-text .ha-brand{opacity:0}

.ha-key{flex:0 0 auto;font-family:var(--font-mono);font-size:.62rem;font-weight:700;line-height:1;color:var(--text-mute);background:color-mix(in srgb,var(--surface-2) 80%,transparent);border:1px solid var(--border);border-bottom-width:2px;border-radius:5px;padding:.2rem .38rem;transition:opacity .25s var(--ease),transform .25s var(--ease)}
.hero-ask.is-on .ha-key,.hero-ask.has-text .ha-key{opacity:0;transform:scale(.9)}

.ha-send{flex:0 0 auto;display:grid;place-items:center;width:32px;height:32px;border:1px solid var(--border);border-radius:9px;background:var(--surface-2);color:var(--text-mute);font-size:.86rem;cursor:pointer;transition:background .25s var(--ease),color .25s var(--ease),border-color .25s var(--ease),transform .25s var(--ease)}
.hero-ask.has-text .ha-send{background:var(--grad);border-color:transparent;color:var(--on-accent);box-shadow:0 10px 22px -12px var(--accent-glow)}
.ha-send:hover{transform:translateX(1px) scale(1.04);border-color:var(--accent-line);color:var(--accent-ink)}
.hero-ask.has-text .ha-send:hover{color:var(--on-accent)}
.ha-send:active{transform:scale(.94)}

/* One-shot sweep on focus, reusing the detection-scan vocabulary the cursor and
   the Wave 3 parse line already speak — so focusing reads as "acquiring", not
   as a generic glow. */
.ha-scan{position:absolute;inset:0;border-radius:inherit;pointer-events:none;opacity:0;background:linear-gradient(100deg,transparent 42%,color-mix(in srgb,var(--accent) 26%,transparent) 50%,transparent 58%)}
.hero-ask.is-sweep .ha-scan{animation:haSweep .78s var(--ease)}
@keyframes haSweep{0%{opacity:1;transform:translateX(-100%)}100%{opacity:0;transform:translateX(100%)}}

/* Routing: the question has left this console and the panel is opening. */
.hero-ask.is-routing{border-color:var(--accent);pointer-events:none}
.hero-ask.is-routing .ha-scan{opacity:1;animation:haSweep .58s var(--ease) infinite}
.hero-ask.is-routing .ha-input{opacity:.45}

/* Attract: one pulse after the hero intro lands, then never again. */
.hero-ask.is-attract{animation:haAttract 1.6s var(--ease)}
@keyframes haAttract{0%,100%{box-shadow:0 16px 38px -30px var(--accent-glow)}45%{box-shadow:0 0 0 4px var(--accent-soft),0 20px 44px -26px var(--accent-glow)}}
.hero-ask.is-attract .ha-caret{animation:haBlink .5s steps(1) 3}
@keyframes haBlink{50%{opacity:.15}}

.ha-chips{display:flex;align-items:center;flex-wrap:wrap;gap:.38rem;margin-top:.6rem;padding-left:.2rem}
.ha-try{font-family:var(--font-mono);font-size:.58rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;color:var(--text-mute);margin-right:.15rem}
.ha-chip{font-family:var(--font-mono);font-size:.66rem;font-weight:600;color:var(--text-soft);background:color-mix(in srgb,var(--surface) 70%,transparent);border:1px solid var(--border);border-radius:999px;padding:.24rem .6rem;cursor:pointer;transition:color .22s var(--ease),border-color .22s var(--ease),background .22s var(--ease),transform .22s var(--ease)}
.ha-chip:hover{color:var(--accent-ink);border-color:var(--accent-line);background:var(--accent-soft);transform:translateY(-2px)}
.ha-chip:active{transform:translateY(0) scale(.97)}

/* Phone: the brand + key badge go (no hover to reveal them, and the row needs
   the width), and the chips are grown to the project's 34px tap-target floor —
   they are inline-flex so min-height actually applies to a button with only
   padding. Send goes to 36px for the same reason. */
@media (max-width:575.98px){.hero-ask-wrap{max-width:100%;margin-top:26px}.ha-brand,.ha-key{display:none}.ha-input{font-size:.78rem}.ha-send{width:36px;height:36px}.ha-chips{gap:.32rem;margin-top:.7rem}.ha-chip{display:inline-flex;align-items:center;min-height:34px;padding:.3rem .68rem;font-size:.64rem}}
@media (prefers-reduced-motion:reduce){.hero-ask,.ha-caret,.ha-brand,.ha-key,.ha-send,.ha-chip{transition:none}.hero-ask:hover,.hero-ask.is-on,.ha-send:hover,.ha-chip:hover{transform:none}.hero-ask.is-attract,.hero-ask.is-sweep .ha-scan,.hero-ask.is-routing .ha-scan,.hero-ask.is-attract .ha-caret{animation:none}}

/* =====================================================================
   HERO · "messy data" — IT IS A MESS, AND YOU WATCH IT GET CORRECTED
   Third pass, and the brief is now explicit: the phrase must LOOK messy, and
   it must be CORRECTED.

   Pass 1 (original Wave 12b) had rest = messy, corrected on hover. Right idea,
   but it was read as a font bug twice — because a bare tilt has nothing on it
   that says "on purpose", and fonts render badly all the time.
   Pass 2 over-corrected: rest went clean and the mess became a blip. That threw
   away the whole point, which is that the mess is the SUBJECT of the sentence.

   Pass 3 keeps the mess and fixes what actually made it read as breakage:

     1. A FLAG. At rest the phrase carries a grey dotted rule underneath — the
        universal "this string is bad" mark. A rendering bug never comes with a
        dotted underline, so the tilt now reads as content that has been flagged
        rather than type that has failed.
     2. A CORRECTION YOU CAN SEE HAPPEN, left to right. Characters straighten on
        a staggered ramp (~35ms per character) instead of all at once, so it
        reads as a pass running over the string, not as a state swap. The dotted
        rule fades out while a solid accent rule DRAWS beneath it in the same
        direction, arriving with the last character.
     3. A RECEIPT. The chip names what was removed — NULL, NaN, dupe, struck
        through — landing on `clean`. Same floating-readout idiom as
        .lead-key[data-readout] two words later in the same sentence.

   Nobody has to hover to see it: telemetry.js §12b already adds .play once at
   2400ms, which runs the whole correction as a one-shot demo and then releases
   it back to messy — correct, because the phrase in the sentence still IS
   "messy data".

   The chip is a SIBLING of .kw-messy, never a child: §12b rebuilds the phrase
   with `messy.innerHTML = messy.textContent.replace(...)`, so a nested element
   would be flattened into the character spans. Hence .messy-wrap, and hence the
   sibling combinator `.kw-messy.play ~ .md-ops`. NO JS CHANGED.
   ===================================================================== */
.messy-wrap{position:relative;display:inline-block}

/* -- rest: the mess. Bolder than the original 4-8deg, because a SUBTLE wrongness
   is exactly what reads as a bug; an obvious one reads as a joke. Still legible,
   which it must be — this is a word in a sentence, not an ornament. -- */
.kw-messy .md-ch:nth-child(1){transform:rotate(-9deg) translateY(-2.5px)}
.kw-messy .md-ch:nth-child(2){transform:rotate(7deg) translateY(2px)}
.kw-messy .md-ch:nth-child(3){transform:rotate(-5deg) translateY(3px)}
.kw-messy .md-ch:nth-child(4){transform:rotate(10deg) translateY(-1.5px)}
.kw-messy .md-ch:nth-child(5){transform:rotate(-8deg) translateY(2.5px)}
.kw-messy .md-ch:nth-child(6){transform:rotate(6deg) translateY(-3px)}
.kw-messy .md-ch:nth-child(7){transform:rotate(-11deg) translateY(1.5px)}
.kw-messy .md-ch:nth-child(8){transform:rotate(5deg) translateY(-2px)}
.kw-messy .md-ch:nth-child(9){transform:rotate(-6deg) translateY(2.5px)}
.kw-messy .md-ch{display:inline-block;transition:transform .34s var(--ease);transition-delay:0s}

/* -- corrected. Specificity matters here: the rest rules above are (0,3,0)
   because of :nth-child, so the corrected state has to reach (0,4,0) or it
   loses. `.messy-wrap:hover` (not `.kw-messy:hover`) so the chip counts as part
   of the same surface and hovering it does not cancel the correction. -- */
.messy-wrap:hover .kw-messy .md-ch,.kw-messy.play .md-ch:nth-child(n){transform:none}

/* the sweep: ~35ms per character, left to right. This is the difference between
   "it corrected" and "you watched it correct". Delays are on the CORRECTED state
   only, so releasing snaps back to messy in one motion rather than unravelling. */
.messy-wrap:hover .kw-messy .md-ch:nth-child(1),.kw-messy.play .md-ch:nth-child(1){transition-delay:.02s}
.messy-wrap:hover .kw-messy .md-ch:nth-child(2),.kw-messy.play .md-ch:nth-child(2){transition-delay:.055s}
.messy-wrap:hover .kw-messy .md-ch:nth-child(3),.kw-messy.play .md-ch:nth-child(3){transition-delay:.09s}
.messy-wrap:hover .kw-messy .md-ch:nth-child(4),.kw-messy.play .md-ch:nth-child(4){transition-delay:.125s}
.messy-wrap:hover .kw-messy .md-ch:nth-child(5),.kw-messy.play .md-ch:nth-child(5){transition-delay:.16s}
.messy-wrap:hover .kw-messy .md-ch:nth-child(6),.kw-messy.play .md-ch:nth-child(6){transition-delay:.195s}
.messy-wrap:hover .kw-messy .md-ch:nth-child(7),.kw-messy.play .md-ch:nth-child(7){transition-delay:.23s}
.messy-wrap:hover .kw-messy .md-ch:nth-child(8),.kw-messy.play .md-ch:nth-child(8){transition-delay:.265s}
.messy-wrap:hover .kw-messy .md-ch:nth-child(9),.kw-messy.play .md-ch:nth-child(9){transition-delay:.3s}

/* -- the flag: CORRUPTION, not an underline (4th pass, 2026-08-26)
   The underline is gone at the owner's request. Its job was to mark the mess as
   deliberate, so that job moves ONTO the characters themselves, which is both
   cooler and more literal:

     · DROPPED VALUES — characters 3, 5 and 7 sit at reduced opacity. Missing
       values are the canonical data problem, so the phrase is visibly holed:
       "me·sy d·ta". This is the single clearest "this is dirty" signal available
       without adding chrome, and unlike a tilt it cannot be mistaken for a font.
     · SIGNAL GHOST — a two-colour horizontal split behind each glyph, the
       registration error you get off a bad channel. Broken fonts do not ghost.
     · MISALIGNMENT — the existing rotate/translate, kept.

   Correcting fills the holes, collapses the ghost and squares the characters, all
   on the same left-to-right sweep. The mess now reads as data, not as damage. -- */
.kw-messy .md-ch{text-shadow:1.6px 0 0 color-mix(in srgb,var(--accent) 34%,transparent),-1.6px 0 0 color-mix(in srgb,var(--accent-2) 26%,transparent)}
.kw-messy .md-ch:nth-child(3){opacity:.34}
.kw-messy .md-ch:nth-child(5){opacity:.58}
.kw-messy .md-ch:nth-child(7){opacity:.34}
.messy-wrap:hover .kw-messy .md-ch,.kw-messy.play .md-ch:nth-child(n){opacity:1;text-shadow:none}

/* -- the receipt -- */
.md-ops{position:absolute;bottom:calc(100% + 9px);left:0;display:flex;align-items:center;gap:.4rem;white-space:nowrap;-webkit-user-select:none;user-select:none;font-family:var(--font-mono);font-size:.62rem;font-weight:700;letter-spacing:.02em;background:color-mix(in srgb,var(--surface) 92%,transparent);-webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);border:1px solid var(--accent-line);border-radius:8px;padding:.3rem .55rem;box-shadow:0 14px 30px -16px var(--accent-glow);opacity:0;transform:translateY(7px) scale(.96);transform-origin:bottom left;pointer-events:none;z-index:9;transition:opacity var(--t) var(--ease),transform var(--t) var(--ease)}
/* ⚠ THE RECEIPT SITS BESIDE "messy data", NOT ABOVE IT. Above, it landed on the
   hero tagline's rotating chip: measured at 1440, the chip's bottom is 15.2px
   below the receipt's top and the two overlap horizontally as well, so **57% of
   the receipt's height rendered on top of the hero's loudest, animated element**
   — and the receipt has `z-index:9`, so it won. There is only **13.8px** of clear
   gap between the chip and the lead, and the receipt needs **35.9px** (26.9 tall
   + 9 offset); no amount of trimming closes a 22px shortfall. Below is no better
   — that covers a full line of the lead's own body text.
   Sideways there is **332–491px** of empty line from 768 to 1440 against the
   receipt's 148px, and it stays on its own line so it can never reach the chip
   again. Same "sideways is the only direction with room" answer as the `.ap-id`
   and education tags. */
.md-ops{bottom:auto;top:50%;left:calc(100% + 10px);transform:translate(-6px,-50%) scale(.96);transform-origin:left center}
.messy-wrap:hover .md-ops{opacity:1;transform:translate(0,-50%) scale(1)}
/* .play lands on .kw-messy, so the chip is reached as its sibling */
.kw-messy.play ~ .md-ops{animation:mdOps 1.6s var(--ease)}
/* ⚠ Every frame must carry the -50% or the receipt drops half its own height:
   this used to be `transform:none` at the held frames, which was correct only
   while the resting transform was a plain translateY. */
@keyframes mdOps{0%{opacity:0;transform:translate(-6px,-50%) scale(.96)}14%,72%{opacity:1;transform:translate(0,-50%) scale(1)}100%{opacity:0;transform:translate(5px,-50%) scale(.98)}}

/* struck through = removed. The accent strike is what makes these read as
   operations applied, rather than as a list of words. */
.md-ops i{font-style:normal;color:var(--text-mute);text-decoration:line-through;text-decoration-color:color-mix(in srgb,var(--accent) 65%,transparent);text-decoration-thickness:1.5px;opacity:0}
.md-ops b{color:var(--accent-ink);font-weight:800;opacity:0}
.md-ops b::before{content:"\2192";margin-right:.34rem;color:var(--text-mute)}
.messy-wrap:hover .md-ops i,.kw-messy.play ~ .md-ops i,.messy-wrap:hover .md-ops b,.kw-messy.play ~ .md-ops b{animation:mdOpIn .32s var(--ease) both}
.messy-wrap:hover .md-ops i:nth-child(1),.kw-messy.play ~ .md-ops i:nth-child(1){animation-delay:.06s}
.messy-wrap:hover .md-ops i:nth-child(2),.kw-messy.play ~ .md-ops i:nth-child(2){animation-delay:.16s}
.messy-wrap:hover .md-ops i:nth-child(3),.kw-messy.play ~ .md-ops i:nth-child(3){animation-delay:.26s}
.messy-wrap:hover .md-ops b,.kw-messy.play ~ .md-ops b{animation-delay:.4s}
@keyframes mdOpIn{0%{opacity:0;transform:translateY(3px)}100%{opacity:1;transform:none}}

/* Reduced motion: §12b returns early, so .md-ch spans are never built and the
   phrase is plain text — but the ::after flag would still draw under a string
   that can no longer be seen correcting. Drop the flag entirely there and let
   the clean rule stand, so reduced motion gets the RESULT rather than a
   permanent error mark it cannot clear. */
@media (prefers-reduced-motion:reduce){
.kw-messy .md-ch{transition:none;opacity:1;text-shadow:none;transform:none}
.md-ops{transition:none}
.messy-wrap:hover .md-ops i,.kw-messy.play ~ .md-ops i,.messy-wrap:hover .md-ops b,.kw-messy.play ~ .md-ops b,.kw-messy.play ~ .md-ops{animation:none}
.messy-wrap:hover .md-ops i,.messy-wrap:hover .md-ops b{opacity:1}
}

@media (max-width:575.98px){.md-ops{font-size:.56rem;padding:.26rem .45rem;gap:.32rem}}

/* =====================================================================
   HERO · the CTA row was flush against the stat rail
   Bootstrap's .m-0 on .hero-stats resolves to margin:0!important, so BOTH the
   base .hero-stats-row{margin-top:30px} and any override of it are dead rules.
   The gap used to be supplied by the .hero-status line that sat between the
   CTAs and the rail; folding that line INTO the rail removed the spacer along
   with it and the buttons ended up touching. Put the gap on .hero-ctas, which
   carries no !important. Check the computed value, not the declaration.
   ===================================================================== */
.hero-main .hero-ctas{margin-bottom:30px}

/* =====================================================================
   HERO · the mark under the name is a single stroke again (2026-08-26)
   It was briefly a tokenizer mark — a segmented path with a `Pav|an` boundary
   tick and a `tokenizer · Pav|an` readout on hover. That was my idea, not a
   request, and the owner rejected it. Removed entirely: `.hn-seg`, `.hn-tick`
   and `.hn-tok` no longer exist in markup, CSS or docs.

   Back to one continuous stroke, which is the right mark for a NAME anyway —
   you underline a signature. It needs no explanation, which is exactly the
   quality the tokenizer version lacked. The draw-in on load and the redraw on
   hover are the parent's original behaviour (`hnScribIn` / `hnScribRe`,
   styles.css ~412) and were never touched.
   ⚠ Do not reintroduce a concept here. It is a flourish under a name.
   ===================================================================== */

/* =====================================================================
   HERO · Wave 6b/6c — THE GREETING IS A PROMPT, THE NAME IS THE COMPLETION
   One idea joins the two things that were flat: `Hey, I'm` had no treatment at
   all, and the name's only entrance was a generic GSAP fade/rise shared with
   five other blocks. Framing them as prompt → completion makes the whole top of
   the hero do the thing the page is about, and it costs no new element beyond a
   caret.

     ›  Hey, I'm            <- types itself, caret blinks, caret retires
        Aradhya Pavan.      <- then GENERATES, one subword token at a time

   The name arriving in four beats — Ar · adhya · Pav · an — is the same split
   the tokenizer mark under it draws (§2.9), so the two now explain each other
   instead of being two unrelated NLP references.
   ===================================================================== */

/* -- 6b. "Hey, I'm" — italic Fraunces, and it WAVES ----------------------
   THE COPY IS FIXED: it says `Hey, I'm`, nothing else. Two rewrites of the words
   were rejected and both reasons stand: (1) a `›` chevron + character type-in +
   blinking caret — three terminal clichés on an eight-character label, above a
   tagline that already types; (2) a time-of-day greeting ("Good afternoon, I'm"),
   rejected as something every site does. The idea lives in the type and the
   gesture instead.

   TYPE. It was JetBrains Mono at 20px, which read as a code label — cold, and a
   FOURTH typeface inside the first two elements of the page. It is now **italic
   Fraunces**, the same family as the 115px name directly beneath it, so the
   greeting and the name read as one editorial line instead of two unrelated
   voices. Real italic, not synthesised: the font link loads `ital` 0 AND 1.

   ⚠ This is a DELIBERATE fourth use of `--font-title`, and the audit count in the
   docs moves 3 -> 4. It does not contradict "a display serif only works at display
   size", because that rule is about card titles set with display optical sizing.
   The fix is the `opsz` axis, which exists for exactly this: the name runs
   `opsz 144` (display cut — fine hairlines, tight fit), the greeting runs
   `opsz 28` (text cut — thicker hairlines, more open shapes) so the same family
   stays legible at 25px. Never set a small Fraunces size without lowering `opsz`.
   `SOFT 40` rounds the terminals a little, `WONK 1` keeps the swashy alternates —
   both chosen to make a greeting feel warm rather than formal.
   ===================================================================== */
.hero-hello{position:relative;display:inline-block;font-family:var(--font-title);font-style:italic;font-size:clamp(1.15rem,1.9vw,1.55rem);font-weight:500;font-variation-settings:'opsz' 28,'SOFT' 40,'WONK' 1;letter-spacing:.004em;color:var(--text-soft)}
.hh-txt{display:inline-block}
.hh-txt > span{display:inline-block}
.hero-hello .hh-txt{transition:color .32s var(--ease)}
.hero-hello:hover .hh-txt{color:var(--text)}

/* GESTURE. A greeting is waved, and this greeting is text, so the text waves:
   each glyph lifts and tips in turn, left to right, like a hand.

   Four details carry it, and they are the whole point:
     · PUNCTUATION SITS IT OUT (.hh-p). Bouncing punctuation is exactly what makes
       this kind of thing read as a cheap bounce instead of a wave.
     · AMPLITUDE DECAYS across the phrase (1 -> .68), so energy drains as the wave
       travels, the way a real one does.
     · A COLOUR RIPPLE RIDES THE CREST. `color` is set only at the 38% frame, so
       it interpolates from whatever the glyph currently is and back again — which
       both keeps the hover colour working and, more importantly, makes the travel
       VISIBLE. At this size the motion alone is only ~8px; the tint is what lets
       you actually read it as a wave crossing the phrase.
     · IT TIPS AND SWELLS AS IT LIFTS, with a soft overshoot coming down, so each
       glyph arcs rather than hopping vertically. The rotation is smaller than it
       would be upright (3.5deg, not 5) because italic already leans — stacking
       more tilt on a slanted face reads as a wobble.
   main.js `heyWave()` toggles `.is-wave`: a class toggle, because re-applying the
   same `animation-name` will not restart it. */
.hero-hello.is-wave .hh-txt > span{animation:hhWave .7s cubic-bezier(.34,.9,.36,1) both}
@keyframes hhWave{
0%,100%{transform:none}
38%{transform:translateY(calc(var(--amp,1) * -.34em)) rotate(calc(var(--amp,1) * -3.5deg)) scale(calc(1 + var(--amp,1) * .045));color:var(--accent-ink)}
70%{transform:translateY(calc(var(--amp,1) * .055em)) rotate(calc(var(--amp,1) * 1.1deg))}
}
/* 55ms per glyph — slow enough to read as a wave, quick enough to stay a gesture */
.hero-hello.is-wave .hh-txt > span:nth-child(1){animation-delay:0s;--amp:1}
.hero-hello.is-wave .hh-txt > span:nth-child(2){animation-delay:.055s;--amp:.94}
.hero-hello.is-wave .hh-txt > span:nth-child(3){animation-delay:.11s;--amp:.88}
.hero-hello.is-wave .hh-txt > span:nth-child(4){animation-delay:.165s;--amp:.82}
.hero-hello.is-wave .hh-txt > span:nth-child(5){animation-delay:.22s;--amp:.78}
.hero-hello.is-wave .hh-txt > span:nth-child(6){animation-delay:.275s;--amp:.73}
.hero-hello.is-wave .hh-txt > span:nth-child(7){animation-delay:.33s;--amp:.68}
/* must come AFTER the nth-child rules — same specificity, source order decides */
.hero-hello.is-wave .hh-txt > .hh-p{animation:none}

/* -- 6c. The name generates, token by token -------------------------------
   ⚠ `animation-fill-mode` here is **backwards**, never `both`. Wave 6 writes
   `transform` and `font-variation-settings` INLINE per letter on mousemove, and
   a filling animation outranks an inline declaration — `both` would leave the
   final frame permanently applied and kill the attention lift for good. The
   keyframes also deliberately avoid `transform` for the same reason: opacity
   and blur only, which reads as "materialising" anyway.
   `.hn-ch` spans are built by telemetry.js §11, so these delays start when the
   split happens (never under reduced motion — there the name is simply there). */
.hn-ch{animation:hnGen .55s var(--ease) backwards}
@keyframes hnGen{from{opacity:0;filter:blur(11px)}to{opacity:1;filter:blur(0)}}

/* Four beats, matching the four tokens the mark under the name draws.
   `.hero-name > .hn-wd` is "Aradhya"; `.text-grad > .hn-wd` is "Pavan". */
.hero-name > .hn-wd > .hn-ch:nth-child(-n+2){animation-delay:.55s}
.hero-name > .hn-wd > .hn-ch:nth-child(n+3){animation-delay:.70s}
.text-grad > .hn-wd > .hn-ch:nth-child(-n+3){animation-delay:.88s}
.text-grad > .hn-wd > .hn-ch:nth-child(n+4){animation-delay:1.03s}
/* the full stop lands last, and keeps its existing inference pulse */
.hero-dot{animation:hnGen .55s 1.18s var(--ease) backwards,heroDotPulse 2.4s ease-in-out infinite}

/* -- 6b(ii). Attention you can SEE ----------------------------------------
   Wave 6 already blooms weight and lifts each letter by the Gaussian, but a
   weight change on a serif is felt more than seen. telemetry.js §11 now also
   publishes the kernel value as `--a` (0..1) per letter, so the heat can be
   painted: a glow that is genuinely absent at --a:0.
   ⚠ It must go to fully transparent, not just to blur-radius 0. `.text-grad`
   letters are `-webkit-text-fill-color:transparent`, so a 0-blur shadow would
   show straight THROUGH the glyph as a hard silhouette. Hence color-mix down to
   0%, which is the only part of this that has to be exact. */
.hn-ch{--a:0}
.hero-name .hn-ch{text-shadow:0 0 calc(var(--a) * 26px) color-mix(in srgb,var(--accent) calc(var(--a) * 62%),transparent)}

@media (prefers-reduced-motion:reduce){
.hero-hello.is-wave .hh-txt > span{animation:none}
.hn-ch{animation:none;text-shadow:none}
.hero-dot{animation:none}
}

/* =====================================================================
   HERO · the rotating word gets its OWN LINE — it was hopping mid-type
   Reported, and reproduced exactly: `.hl` was `display:inline-block`, so the
   browser re-ran line-breaking on EVERY keystroke of the typewriter. Short
   prefixes still fitted in the 93px left over on line 1 and rendered there;
   the moment the word outgrew that, it wrapped. Measured on "grounded":

     ""  "g"  "gr"  "gro"   -> top 469, left 616   (tail of line 1)
     "grou" onwards         -> top 509, left  67   (start of line 2)

   So the word appeared at the end of one line and then hopped down and back to
   the left, four characters in, every single cycle.

   `display:block` takes it out of the inline flow entirely, so the break is
   structural instead of a wrapping side-effect and the position cannot depend on
   the word's length. `width:max-content` keeps the highlight snug to the text
   rather than stretching the accent block across the column; `max-width:100%`
   is the guard for a word wider than the column.

   ⚠ Do not put this back to `inline-block` "to save a line". The line is not
   optional — it is what makes the position stable while the text animates.
   ===================================================================== */
.hero-tagline .hl{display:block;width:max-content;max-width:100%;margin-top:.12em}

/* =====================================================================
   HERO STAT RAIL · the hover ring, and why it had square corners
   ---------------------------------------------------------------------
   Reported as *"on hover these three sucks … see the border how worst it
   looks"*, on desktop AND mobile. Two facts collide:

     .hero-stats        border-radius:16px; overflow:hidden
     .hero-stats > div  border-radius:0
     .hero-stats:has(div:hover){overflow:visible}

   `overflow:hidden` is what normally hides the mismatch — the rail clips its
   children to its own rounded box. But the rail has to go `overflow:visible`
   on hover so the third tile's `data-readout` tooltip can escape upward, and
   the moment it does, the hovered tile's `inset 0 0 0 1px` ring is drawn as a
   SQUARE inside a 16px-rounded rail. The first and last tiles sit ON those
   rounded corners, so up to ~4.7px of bare square corner pokes outside the
   rail's own border at each of them — a hairline L hanging off the outside of
   a rounded box, which is exactly what it looks like.

   ⚠ THE FIX IS TO GIVE THE TILES THE RADIUS, NOT TO TAKE BACK THE OVERFLOW.
     Re-clipping the rail kills the readout, which is the whole point of the
     third tile. The inner radius is the rail's minus its own border.
   ⚠ AND IT IS POSITION-DEPENDENT, SO IT HAS TO FOLLOW THE BREAKPOINTS. The
     rail is a 3-column row down to 480px and a single stacked column below
     that, so "the outer corners" are LEFT/RIGHT above 480 and TOP/BOTTOM
     below it. Getting this wrong is invisible until someone hovers on a phone.
   ===================================================================== */
.hero-stats > div{border-radius:0}
.hero-stats > div:first-child{border-radius:calc(var(--radius) - 1px) 0 0 calc(var(--radius) - 1px)}
.hero-stats > div:last-child{border-radius:0 calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0}
@media (max-width:480px){
    .hero-stats > div:first-child{border-radius:calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0}
    .hero-stats > div:last-child{border-radius:0 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px)}
}
/* The ring also sat exactly on top of the rail's own 0.8px border along the
   middle tile's top and bottom edge, so that edge rendered ~1.8px against
   0.8px everywhere else and read as a misaligned double rule. Pulling the
   highlight one pixel inside the tile separates the two: the rail keeps its
   border, the tile gets a ring. */
.hero-stats > div:hover{box-shadow:inset 0 0 0 1px var(--accent-line),inset 0 0 0 2px color-mix(in srgb,var(--surface) 82%,transparent),inset 0 0 26px -12px var(--accent-glow)}

/* =====================================================================
   SKILLS · .skill-feature .proof-grid — the responsive rule that never ran
   ---------------------------------------------------------------------
   Reported as *"see the 2nd image in the mobile it looks worst"*: the three
   proof cards stayed in three columns on a phone, 96px each, so "shipped
   builds" / "deep case studies" broke into cramped two- and three-line
   stacks inside a 45px text column.

   ⚠ THE RESPONSIVE RULE EXISTED AND LOST ON SPECIFICITY. §2233 carries
     `@media (max-width:991.98px){ .proof-grid{grid-template-columns:1fr} }`
     — specificity (0,1,0) — against the base `.skill-feature .proof-grid`
     at (0,2,0). A media query does not raise specificity, so the base rule
     wins at EVERY width and the collapse never happened. Measured at 390px:
     `grid-template-columns: 96px 96px 96px`.
   ⚠ This is the second time a rule in this file has been dead on arrival for
     this reason. When a responsive override "does nothing", read the COMPUTED
     value and compare selector specificity before touching the value itself.
   ===================================================================== */
@media (max-width:767.98px){
    .skill-feature .proof-grid{grid-template-columns:1fr;gap:8px}
    .skill-feature .proof-grid div{min-height:0}
}

/* =====================================================================
   HERO STAT RAIL · the stacked mobile layout
   ---------------------------------------------------------------------
   Reported as *"how worst it looks in mobile man"*. Below 480px the rail
   becomes a single column, and it inherited the desktop tile's
   `flex-direction:column` — so each of the three facts got a full-width row
   with the value on one line and its label on the next. Measured at 390px:
   **three 68px rows, 208px of rail**, each row 338px wide carrying about
   120px of text. Two thirds of every row was empty and the block ate a third
   of the fold for three short facts.

   A stacked rail is a SPEC LIST, not three stacked cards: value left, label
   right, one line each. Same three facts, ~128px instead of 208, and the
   width is actually used.

   ⚠ `align-items:baseline`, not `center` — `dt` is 1.15rem display and `dd`
     is .72rem body, so centring them leaves the two texts visibly unaligned
     against each other. The baseline is the only shared reference.
   ⚠ `dd` needs `min-width:0` or "AI/ML & full-stack roles" refuses to shrink
     and pushes the row wider than the rail at 320px.
   ===================================================================== */
@media (max-width:480px){
    .hero-stats > div{flex-direction:row;align-items:baseline;justify-content:space-between;gap:12px;padding:12px 15px}
    .hero-stats dt{font-size:1.15rem}
    .hero-stats dd{min-width:0;text-align:right}
    /* The readout hangs UNDER the tile here. Stacked, the third tile is the
       bottom row, so the default `bottom:calc(100% + 9px)` parks a 230px nowrap
       chip directly over the middle row. Below it there is nothing but air. */
    .hero-stats div[data-readout]::after{bottom:auto;top:calc(100% + 9px);transform:translate(-50%,-7px) scale(.96)}
    .hero-stats div[data-readout]:hover::after{transform:translate(-50%,0) scale(1)}
}
