/* ============================================================
   davidwc — Data Analyst Portfolio (SOURCE OF TRUTH — mirrored in README.md)
   Motion-Driven + Minimalism · neutral canvas · ambient hero (mesh + orb + grain)
   Fonts: Space Grotesk (headings) · DM Sans (body) · JetBrains Mono (data)
   DARK IS DEFAULT. Light theme lives in [data-theme="light"].
   Any color must use a token — never a raw hex in a component, or it
   won't flip when the theme toggles.
   ============================================================ */
:root {
  color-scheme: dark;

  /* Color strategy (portfolio): NEUTRAL background so the work is the loudest
     thing on screen. Text near-white/near-black. Accent used sparingly —
     eyebrow, section labels, the lead button, and links. Nothing else. */
  --color-bg:        #0A0A0B;
  --color-text:      #F5F5F6;
  --color-muted:     #9C9CA5;
  --color-primary:   #3B82F6;
  --color-primary-dark: #2563EB;
  --color-accent:    #22D3EE;
  --color-border:    #26262A;
  --color-card:      rgba(255,255,255,.035);
  --color-input:     rgba(255,255,255,.05);
  --color-chip:      rgba(255,255,255,.03);
  --on-primary:      #fff;

  /* rgba channels for composites */
  --scrim-rgb:   10, 10, 11;
  --nav-rgb:     10, 10, 11;     /* nav glass */
  --primary-rgb: 59, 130, 246;

  /* section bands — neutral steps, not hues. Just enough separation to read
     as distinct chapters without tinting the work inside them. */
  --band-hero-1:#0A0A0B; --band-hero-2:#0D0D0F;
  --band-about-1:#0D0D0F; --band-about-2:#0F0F12;
  --band-projects-1:#0F0F12; --band-projects-2:#0C0C0E;
  --band-hobby-1:#0C0C0E; --band-hobby-2:#101013;
  --band-contact-1:#101013; --band-contact-2:#0A0A0B;
  --strip-bg:#0E0E10;

  /* ambient hero background */
  --mesh-1:rgba(78,104,152,.68);
  --mesh-2:rgba(46,66,102,.60);
  --mesh-3:rgba(92,82,120,.46);
  --mesh-opacity:.72;
  --grain-opacity:.34;
  --grain-blend:overlay;
  /* Siri-style orb.
     NO mix-blend-mode here — `screen`/`overlay` composite against a
     RECTANGULAR backdrop group, and the parent's border-radius + overflow
     cannot clip that, so it paints a visible square around the sphere.
     Colors are pre-boosted to make up for losing additive blending. */
  --orb-base:rgba(24,30,48,.42);
  --orb-1:rgba(72,142,250,.92);
  --orb-2:rgba(34,211,238,.80);
  --orb-3:rgba(167,139,250,.70);
  --orb-glow:rgba(59,130,246,.26);
  --orb-inner-glow:rgba(120,180,255,.16);
  --orb-rim:rgba(255,255,255,.20);
  --orb-sheen:rgba(255,255,255,.10);
  --orb-opacity:.62;
  --color-error:#F87171;

  /* illustration surfaces — the SVG artwork themes through these */
  --il-desk:#141D31; --il-desk-edge:#2B3D5E; --il-stand:#1C2740; --il-pot:#22314F;
  --il-panel:#0E1626; --il-screen:#0B1322; --il-stroke:#2B3654;
  --il-card:#0E1728; --il-key:#182338; --il-keycap:#2A3A5B;
  --il-mug:#1A2745; --il-track:#1E2A44; --il-muted:#44557A; --il-kpi:#F4F4F6;

  /* data-viz palette */
  --chart-1:#3B82F6; --chart-2:#22D3EE; --chart-3:#A78BFA; --chart-4:#F472B6;

  /* type */
  --font-heading:"Space Grotesk",sans-serif;
  --font-body:"DM Sans",sans-serif;
  --font-mono:"JetBrains Mono",monospace;
  --fs-h1:clamp(2.75rem,8vw,6rem);
  --fs-h2:clamp(2rem,4.5vw,3.25rem);
  --fs-h3:clamp(1.25rem,2.2vw,1.6rem);
  --fs-body:1.0625rem;
  --fs-small:.8125rem;

  /* spacing / misc */
  /* Content width scales with the viewport instead of locking at 1200px —
     on a 2000px screen that left ~400px of dead gutter per side. Anything
     that must line up with the container (e.g. the hero orb) reads this
     same token, so they can never drift apart. */
  --container-max:clamp(1200px, 78vw, 1560px);
  --gutter:clamp(1.25rem,4vw,3rem);
  --band-pad:clamp(5rem,11vh,8rem);
  --radius:14px;
  --radius-sm:8px;
  --nav-h:72px;
  --ease:cubic-bezier(.16,1,.3,1);
}

/* ============================================================
   LIGHT THEME
   accent/primary darkened well beyond dark-mode values so they clear
   4.5:1 on the light background (cyan #22D3EE is ~1.7:1 on white).
   ============================================================ */
:root[data-theme="light"] {
  color-scheme: light;

  --color-bg:        #FAFAFA;
  --color-text:      #101012;
  --color-muted:     #56565E;
  --color-primary:   #2563EB;
  --color-primary-dark: #1D4ED8;
  --color-accent:    #0E7490;
  --color-border:    #E3E3E7;
  --color-card:      #FFFFFF;
  --color-input:     #FFFFFF;
  --color-chip:      #F0F0F2;
  --on-primary:      #fff;

  --scrim-rgb:   250, 250, 250;
  --nav-rgb:     250, 250, 250;
  --primary-rgb: 37, 99, 235;

  --band-hero-1:#FBFBFB; --band-hero-2:#F5F5F6;
  --band-about-1:#F5F5F6; --band-about-2:#FAFAFA;
  --band-projects-1:#FAFAFA; --band-projects-2:#F4F4F5;
  --band-hobby-1:#F4F4F5; --band-hobby-2:#FAFAFA;
  --band-contact-1:#FAFAFA; --band-contact-2:#F6F6F7;
  --strip-bg:#F4F4F5;

  --chart-1:#2563EB; --chart-2:#0891B2; --chart-3:#7C3AED; --chart-4:#DB2777;

  /* ambient: paler mesh, multiply grain, darker ring on a light page */
  --mesh-1:rgba(176,196,232,.75);
  --mesh-2:rgba(206,210,222,.70);
  --mesh-3:rgba(214,204,228,.60);
  --mesh-opacity:.75;
  --grain-opacity:.26;
  --grain-blend:multiply;
  --orb-base:rgba(255,255,255,.42);
  --orb-1:rgba(37,99,235,.42);
  --orb-2:rgba(8,145,178,.38);
  --orb-3:rgba(124,58,237,.30);
  --orb-glow:rgba(37,99,235,.13);
  --orb-inner-glow:rgba(37,99,235,.10);
  --orb-rim:rgba(16,16,20,.10);
  --orb-sheen:rgba(255,255,255,.45);
  --orb-opacity:.55;
  --color-error:#DC2626;

  /* illustration goes light so the desk scene doesn't read as a dark slab */
  --il-desk:#D6DEEE; --il-desk-edge:#B4C2DC; --il-stand:#C7D2E8; --il-pot:#B4C2DC;
  --il-panel:#E4EAF6; --il-screen:#F5F8FE; --il-stroke:#C0CCE3;
  --il-card:#FFFFFF; --il-key:#DCE4F2; --il-keycap:#B4C2DC;
  --il-mug:#DCE4F2; --il-track:#D3DCEE; --il-muted:#8B97AE; --il-kpi:#0C0D12;
}

/* Illustration classes — SVG fills route through tokens so the artwork
   flips with the theme. Never hardcode a hex on these shapes. */
.il-desk{fill:var(--il-desk)}
.il-desk-edge{fill:var(--il-desk-edge)}
.il-stand{fill:var(--il-stand)}
.il-pot{fill:var(--il-pot)}
.il-panel{fill:var(--il-panel);stroke:var(--il-stroke)}
.il-screen{fill:var(--il-screen)}
.il-card{fill:var(--il-card);stroke:var(--il-stroke)}
.il-key{fill:var(--il-key);stroke:var(--il-stroke)}
.il-keycap{fill:var(--il-keycap)}
.il-mug{fill:var(--il-mug)}
.il-track{stroke:var(--il-track)}
.il-muted{fill:var(--il-muted)}
.il-kpi{fill:var(--il-kpi)}
.il-hole{fill:var(--il-screen)}
.il-axis{stroke:var(--il-stroke)}
/* About-section tablet dashboard */
.il-c1{fill:var(--chart-1)} .il-c2{fill:var(--chart-2)} .il-c3{fill:var(--chart-3)}
.il-s1{stroke:var(--chart-1)} .il-s2{stroke:var(--chart-2)}
.il-grid{stroke:var(--il-stroke);stroke-opacity:.6}
/* gradient stops can't take var() as a presentation attribute — use classes */
.il-a1{stop-color:var(--chart-2);stop-opacity:.38}
.il-a2{stop-color:var(--chart-2);stop-opacity:0}

/* ============================================================
   Reset / base
   ============================================================ */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth;scroll-padding-top:calc(var(--nav-h) + 12px)}
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}
body{font-family:var(--font-body);color:var(--color-text);background:var(--color-bg);
  font-size:var(--fs-body);line-height:1.6;-webkit-font-smoothing:antialiased;overflow-x:hidden}
h1,h2,h3{font-family:var(--font-heading);font-weight:600;line-height:1.08;letter-spacing:-.02em}
a{color:inherit;text-decoration:none}
img,svg{display:block;max-width:100%}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
::selection{background:var(--color-primary);color:var(--on-primary)}
:focus-visible{outline:2px solid var(--color-accent);outline-offset:3px;border-radius:4px}

.container{width:min(var(--container-max),100% - 2*var(--gutter));margin-inline:auto}

.skip-link{position:absolute;left:1rem;top:-3rem;z-index:200;background:var(--color-primary);
  color:var(--on-primary);padding:.6rem 1rem;border-radius:var(--radius-sm);transition:top .2s var(--ease)}
.skip-link:focus{top:1rem}

/* mono section label */
.label{font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-accent);
  text-transform:uppercase;letter-spacing:.12em;display:flex;align-items:center;gap:.75rem;margin-bottom:1.25rem}
.label::after{content:"";height:1px;width:64px;background:linear-gradient(90deg,var(--color-accent),transparent)}

/* ============================================================
   Section color bands
   ============================================================ */
.band{position:relative;padding-block:var(--band-pad);overflow:hidden;transition:background .4s}
.band-hero    {background:linear-gradient(180deg,var(--band-hero-1),var(--band-hero-2))}
.band-about   {background:linear-gradient(180deg,var(--band-about-1),var(--band-about-2))}
.band-projects{background:linear-gradient(180deg,var(--band-projects-1),var(--band-projects-2))}
.band-hobby   {background:linear-gradient(180deg,var(--band-hobby-1),var(--band-hobby-2))}
.band-contact {background:linear-gradient(180deg,var(--band-contact-1),var(--band-contact-2))}

/* ============================================================
   Nav
   ============================================================ */
/* z-index above .mobile-menu (110): .nav makes its own stacking context,
   so the toggle's z-index can't escape it — the whole bar must sit on top. */
.nav{position:fixed;inset:0 0 auto 0;z-index:120;height:var(--nav-h);
  display:flex;align-items:center;border-bottom:1px solid transparent;
  transition:background .35s,border-color .35s,backdrop-filter .35s}
.nav.scrolled{background:rgba(var(--nav-rgb),.72);backdrop-filter:blur(14px);
  -webkit-backdrop-filter:blur(14px);border-bottom-color:var(--color-border)}
/* NO width:100% here — it would override .container's gutter and push the
   brand flush to the screen edge, out of line with the hero copy below. */
.nav-inner{display:flex;align-items:center;justify-content:space-between}
.brand{font-family:var(--font-heading);font-weight:700;font-size:1.125rem;
  display:flex;align-items:center;gap:.6rem;min-height:44px}
.brand .dot{width:8px;height:8px;border-radius:50%;background:var(--color-accent);box-shadow:0 0 12px var(--color-accent)}
.nav-right{display:flex;align-items:center;gap:.35rem}
.nav-links{display:flex;gap:.25rem;list-style:none}
.nav-links a{position:relative;display:inline-flex;align-items:center;min-height:44px;
  padding:0 1rem;font-size:.9375rem;color:var(--color-muted);transition:color .2s}
.nav-links a::after{content:"";position:absolute;left:1rem;right:1rem;bottom:8px;height:2px;border-radius:2px;
  background:var(--color-primary);transform:scaleX(0);transform-origin:left;transition:transform .3s var(--ease)}
.nav-links a:hover,.nav-links a.active{color:var(--color-text)}
.nav-links a:hover::after,.nav-links a.active::after{transform:scaleX(1)}

/* theme switch */
.theme-toggle{display:inline-flex;align-items:center;justify-content:center;min-height:44px;min-width:44px;padding:0 .3rem}
.tt-track{position:relative;display:block;width:50px;height:27px;border-radius:999px;
  background:var(--color-input);border:1px solid var(--color-border);transition:background .3s,border-color .3s}
.theme-toggle:hover .tt-track{border-color:var(--color-primary)}
.tt-thumb{position:absolute;top:2px;left:2px;width:21px;height:21px;border-radius:50%;
  background:var(--color-primary);display:grid;place-items:center;color:var(--on-primary);
  transition:transform .35s var(--ease),background .3s}
:root[data-theme="light"] .tt-thumb{transform:translateX(23px)}
.tt-thumb svg{width:13px;height:13px}
.tt-sun{display:none}
:root[data-theme="light"] .tt-sun{display:block}
:root[data-theme="light"] .tt-moon{display:none}

/* hamburger */
.nav-toggle{display:none;width:44px;height:44px;flex-direction:column;align-items:center;justify-content:center;gap:5px}
.nav-toggle span{display:block;width:22px;height:2px;border-radius:2px;background:var(--color-text);
  transition:transform .3s var(--ease),opacity .2s}
.nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
.nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

.mobile-menu{position:fixed;inset:0;z-index:110;background:rgba(var(--nav-rgb),.97);
  backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;
  visibility:hidden;opacity:0}
.mobile-menu a{font-family:var(--font-heading);font-size:clamp(2rem,8vw,3rem);font-weight:600;
  padding:.5rem 1.5rem;color:var(--color-text);transition:color .2s}
.mobile-menu a:hover{color:var(--color-primary)}
.mobile-menu .menu-meta{margin-top:2rem;font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-muted)}

@media(max-width:768px){
  .nav-links{display:none}
  .nav-toggle{display:flex}
}

/* ============================================================
   HERO — Motion-Driven + Minimalism
   Type carries the page. The background is ambient only: a slow gradient
   mesh, ONE drifting ring, and a grain overlay. No narrative, no illustration,
   nothing that competes with the headline or the work below.
   ============================================================ */
.hero{min-height:100svh;display:flex;align-items:center;
  padding-top:var(--nav-h);position:relative;overflow:hidden}
.ambient{position:absolute;inset:0;pointer-events:none;overflow:hidden}

/* 1 — slow gradient mesh. Three blurred blobs on prime-ish durations so they
       never resync into a visible loop. */
.mesh{position:absolute;inset:-28%;filter:blur(100px);opacity:var(--mesh-opacity)}
.mesh span{position:absolute;display:block;border-radius:50%;will-change:transform}
.mesh .m1{width:52vmax;height:52vmax;left:1%;top:-10%;background:var(--mesh-1);
  animation:mesh-a 37s ease-in-out infinite alternate}
.mesh .m2{width:44vmax;height:44vmax;right:-8%;top:10%;background:var(--mesh-2);
  animation:mesh-b 47s ease-in-out infinite alternate}
.mesh .m3{width:40vmax;height:40vmax;left:36%;bottom:-20%;background:var(--mesh-3);
  animation:mesh-c 53s ease-in-out infinite alternate}
@keyframes mesh-a{from{transform:translate3d(0,0,0) scale(1)}   to{transform:translate3d(13%,9%,0)  scale(1.18)}}
@keyframes mesh-b{from{transform:translate3d(0,0,0) scale(1.12)}to{transform:translate3d(-11%,13%,0) scale(.92)}}
@keyframes mesh-c{from{transform:translate3d(0,0,0) scale(.95)} to{transform:translate3d(9%,-11%,0) scale(1.2)}}

/* 2 — the single focal element: a Siri-style orb.
   Three off-center color lobes rotate at different (non-multiple) rates inside
   a circular clip, so the colors swirl and never repeat the same arrangement.
   A plain rotating circle looks static because it's rotationally symmetric —
   the asymmetric lobes are what make the motion readable.
   Right edge is inset to match the container's left inset exactly. */
.orb{position:absolute;top:50%;
  right:max(var(--gutter), calc((100% - var(--container-max)) / 2));
  width:clamp(280px, 46vmin, 620px);aspect-ratio:1;
  transform:translateY(-50%);opacity:var(--orb-opacity);
  animation:orb-breathe 5.5s ease-in-out infinite}
/* outer bloom */
.orb::before{content:"";position:absolute;inset:-16%;border-radius:50%;
  background:radial-gradient(circle,var(--orb-glow) 0%,transparent 68%)}

.orb-core{position:absolute;inset:0;border-radius:50%;overflow:hidden;
  background:var(--orb-base);isolation:isolate}
.ob{position:absolute;inset:-10%;border-radius:50%;
  filter:blur(34px);will-change:transform}
.ob1{background:radial-gradient(circle at 32% 34%,var(--orb-1) 0%,transparent 58%);
  animation:orb-spin 10s linear infinite}
.ob2{background:radial-gradient(circle at 70% 42%,var(--orb-2) 0%,transparent 55%);
  animation:orb-spin-r 14s linear infinite}
.ob3{background:radial-gradient(circle at 50% 76%,var(--orb-3) 0%,transparent 60%);
  animation:orb-spin 17s linear infinite}
/* specular highlight so it reads as a sphere, not a flat disc */
.orb-sheen{position:absolute;inset:0;border-radius:50%;
  background:radial-gradient(circle at 34% 26%,var(--orb-sheen) 0%,transparent 46%)}
.orb-particles{position:absolute;left:-9%;top:-9%;width:118%;height:118%;pointer-events:none}
.orb-rim{position:absolute;inset:0;border-radius:50%;
  border:1.5px solid var(--orb-rim);
  box-shadow:inset 0 0 60px var(--orb-inner-glow)}

@media(max-width:900px){
  .orb{width:min(60vmin,360px);top:auto;bottom:6%;transform:none;animation:none}
  .orb::before{inset:-10%}
}
@keyframes orb-spin  {from{transform:rotate(0deg)   scale(1)}
                      50% {transform:rotate(180deg) scale(1.14)}
                      to  {transform:rotate(360deg) scale(1)}}
@keyframes orb-spin-r{from{transform:rotate(360deg) scale(1.1)}
                      50% {transform:rotate(180deg) scale(.94)}
                      to  {transform:rotate(0deg)   scale(1.1)}}
@keyframes orb-breathe{0%,100%{transform:translateY(-50%) scale(1)}
                       50%    {transform:translateY(-50%) scale(1.035)}}

/* 3 — grain (SVG fractal noise, no image request) */
.grain{position:absolute;inset:0;opacity:var(--grain-opacity);mix-blend-mode:var(--grain-blend);
  background-size:170px 170px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='170' height='170'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='170' height='170' filter='url(%23n)'/%3E%3C/svg%3E")}

@media(prefers-reduced-motion:reduce){
  .mesh span,.orb,.ob{animation:none}
  .orb-particles{opacity:.75}
}

.hero .container{position:relative;z-index:3}
.eyebrow{font-family:var(--font-mono);font-size:.9375rem;color:var(--color-accent);margin-bottom:1.75rem}
.hero h1{font-size:clamp(2.85rem,8.55vw,7.13rem);max-width:min(13ch,58%);font-weight:600;
  letter-spacing:-.035em;line-height:.97}
/* word-mask reveal: each word rises out of its own clipped box.
   The padding/margin pair keeps descenders (the g in "Turning") from clipping. */
.hero h1 .wm{display:inline-block;overflow:hidden;vertical-align:bottom;
  padding-bottom:.14em;margin-bottom:-.14em}
.hero h1 .wm i{display:inline-block;font-style:normal;will-change:transform}
.sub{margin-top:2rem;max-width:min(50ch,54%);font-size:clamp(1.05rem,1.5vw,1.2rem);color:var(--color-text)}
.sub b{color:var(--color-accent);font-weight:600}
.ctas{margin-top:2.75rem;display:flex;gap:1rem;flex-wrap:wrap}

/* Wide screens: pull the copy column in and let the orb own the right side,
   so the hero reads as two distinct zones rather than one crowded row. */
@media(min-width:1500px){
  .hero h1{max-width:min(13ch,50%)}
  .sub{max-width:min(50ch,46%)}
}

/* Narrow: the orb sits BELOW the copy, so drop the % clearance caps —
   otherwise the body text wraps into a painfully narrow column.
   Must live after the base .hero h1 / .sub rules; equal specificity means
   source order decides. */
@media(max-width:900px){
  .hero h1{max-width:15ch}
  .sub{max-width:56ch}
}

/* ============================================================
   BUTTONS — one canonical interaction for the whole site
   Resting: neutral pill (card bg, border, body text).
   Hover:   lifts 5px and fills with the darker primary, white text, shadow.
   Every button AND social pill shares this. Don't add a competing variant.
   ⚠ Never let a GSAP transform (magnetic/quickTo x-y) target these — an
   inline transform overwrites the CSS lift and hover appears to do nothing.
   See README → "Buttons".
   ============================================================ */
.btn,.social{
  display:inline-flex;align-items:center;justify-content:center;gap:.6rem;
  min-height:48px;padding:0 1.75rem;border-radius:999px;
  border:1px solid var(--color-border);background:var(--color-card);color:var(--color-text);
  font-family:var(--font-heading);font-weight:500;font-size:1rem;cursor:pointer;will-change:transform;
  transition:transform .25s var(--ease),background .25s,color .25s,border-color .25s,box-shadow .25s}
.social{padding:0 1.25rem;font-size:.9375rem}
.btn:hover,.social:hover,
.btn:focus-visible,.social:focus-visible{
  transform:translateY(-5px);
  background:var(--color-primary-dark);border-color:var(--color-primary-dark);
  color:var(--on-primary);box-shadow:0 14px 28px rgba(var(--primary-rgb),.35)}
.social svg{width:18px;height:18px}
.btn .arrow{transition:transform .25s var(--ease)}
.btn:hover .arrow,.btn:focus-visible .arrow{transform:translateX(4px)}
/* the contact form's submit sits centered under the fields */
form .btn{align-self:center}

/* Lead variant — the ONE primary action per view (hero CTA, subpage "back").
   It changes the RESTING fill only. Hover/focus is inherited unchanged from
   .btn:hover above, which wins on specificity (0,2,0 vs 0,1,0), so every
   button on the site still animates identically. Never give this its own
   hover rule — that would fork the interaction. */
.btn-lead{
  background:var(--color-primary);border-color:var(--color-primary);
  color:var(--on-primary);box-shadow:0 8px 24px rgba(var(--primary-rgb),.3)}

/* skills marquee */
.marquee{position:relative;z-index:2;border-block:1px solid var(--color-border);padding-block:1rem;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent)}
.marquee-track{display:flex;gap:3rem;width:max-content;animation:marquee 28s linear infinite}
.marquee:hover .marquee-track{animation-play-state:paused}
.marquee-track span{font-family:var(--font-mono);font-size:.9375rem;color:var(--color-muted);
  display:flex;align-items:center;gap:3rem;white-space:nowrap}
.marquee-track span::after{content:"◆";font-size:.5rem;color:var(--color-primary)}
@keyframes marquee{to{transform:translateX(-50%)}}

/* ============================================================
   About
   ============================================================ */
.about-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:clamp(2rem,6vw,4.5rem);align-items:center}
.about-copy h2{font-size:clamp(2rem,4.5vw,3rem);margin-bottom:1.5rem}
.about-copy p{margin-bottom:1.1rem;max-width:58ch;color:var(--color-text)}
.about-copy b{color:var(--color-accent);font-weight:600}
.about-art{position:relative}
/* The tablet's feDropShadow extends past the viewBox on all four sides, and an
   SVG viewport clips by default — which sliced the soft shadow flat and drew a
   visible rectangle around the illustration. Let it bleed instead. */
.about-art svg{overflow:visible}
.chips{display:flex;flex-wrap:wrap;gap:.55rem;margin-top:1.75rem}
.chip{font-family:var(--font-mono);font-size:var(--fs-small);padding:.45rem .85rem;border-radius:999px;
  border:1px solid var(--color-border);background:var(--color-chip);color:var(--color-text);
  transition:color .2s,border-color .2s,transform .2s var(--ease)}
.chip:hover{color:var(--color-accent);border-color:var(--color-accent);transform:translateY(-2px)}
@media(max-width:860px){.about-grid{grid-template-columns:1fr}}

/* ============================================================
   Projects
   ============================================================ */
.head{display:flex;align-items:end;justify-content:space-between;gap:1rem;margin-bottom:2.5rem}
.head h2{font-size:var(--fs-h2)}
.head span{font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-muted);text-align:right}
.grid{display:grid;grid-template-columns:repeat(6,1fr);gap:1.25rem}
/* Project cards AND hobby cards share ONE definition so they can't drift
   apart visually. Only grid placement and thumb size differ. */
.card,.hcard{position:relative;overflow:hidden;border-radius:var(--radius);
  border:1px solid var(--color-border);background:var(--color-card);padding:1.75rem;min-height:230px;
  display:flex;flex-direction:column;justify-content:flex-start;transition:transform .35s var(--ease),border-color .35s}
.card{grid-column:span 3}
.card.wide{grid-column:span 6}
.card:hover,.hcard:hover{transform:translateY(-6px);border-color:rgba(var(--primary-rgb),.55)}
.card h3,.hcard h3{font-size:1.4rem;margin-bottom:.4rem}
.card p,.hcard p{color:var(--color-muted);font-size:.9375rem}
/* keep copy clear of the thumbnail column so art never overlaps text */
.card .body,.hcard .body{max-width:66%}
.card.wide .body{max-width:80%}
.card .thumb,.hcard .thumb{position:absolute;right:1.5rem;bottom:1.5rem;width:128px;height:auto;opacity:.6;
  pointer-events:none;transition:opacity .35s,transform .35s var(--ease)}
.card:hover .thumb,.hcard:hover .thumb{opacity:.9;transform:translateY(-4px)}
@media(max-width:860px){
  .grid{grid-template-columns:1fr}
  .card,.card.wide{grid-column:span 1}
  .card .body,.card.wide .body{max-width:74%}
}

/* ============================================================
   Mid parallax strip
   ============================================================ */
.strip{position:relative;height:340px;overflow:hidden;background:var(--strip-bg);border-block:1px solid var(--color-border)}
.strip .layer svg{width:150%;opacity:.55}
.strip-cap{position:absolute;inset:0;display:grid;place-items:center;z-index:2;text-align:center;padding-inline:1.5rem}
.strip-cap h2{font-size:clamp(1.6rem,3.4vw,2.6rem);max-width:18ch}
.strip::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,var(--strip-bg),transparent 40%,transparent 60%,var(--strip-bg))}

/* ============================================================
   Hobby
   ============================================================ */
.hobby{display:grid;grid-template-columns:repeat(3,1fr);gap:1.25rem}
.hcard .body{max-width:70%}
.hcard .thumb{width:104px}
@media(max-width:860px){.hobby{grid-template-columns:1fr}}

/* ============================================================
   Contact
   ============================================================ */
.contact-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(2rem,6vw,4.5rem)}
.contact-intro h2{font-size:var(--fs-h2);max-width:14ch}
.contact-intro p{margin-top:1.25rem;max-width:42ch;color:var(--color-muted)}
.socials{display:flex;flex-wrap:wrap;gap:1rem;margin-top:2rem}
/* .social styling is defined with .btn — see the BUTTONS block above */
form{display:flex;flex-direction:column;gap:1rem}
.f{display:flex;flex-direction:column;gap:.4rem}
.f label{font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-muted);text-transform:uppercase;letter-spacing:.06em}
.f input,.f textarea{font:inherit;color:var(--color-text);background:var(--color-input);
  border:1px solid var(--color-border);border-radius:var(--radius-sm);padding:.8rem 1rem;min-height:48px;
  transition:border-color .2s,box-shadow .2s}
.f textarea{min-height:130px;resize:vertical}
.f input:focus,.f textarea:focus{outline:none;border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(var(--primary-rgb),.2)}
.form-status{font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-accent);min-height:1.2em}
.form-status.is-error{color:var(--color-error)}
.f input:disabled,form .btn:disabled{opacity:.6;cursor:not-allowed}
.form-note{font-size:var(--fs-small);color:var(--color-muted)}
@media(max-width:860px){.contact-grid{grid-template-columns:1fr}}

/* ============================================================
   Footer
   ============================================================ */
footer{padding:2rem 0;border-top:1px solid var(--color-border);background:var(--color-bg)}
.footer-inner{display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}
footer p{font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-muted)}
.back-top{display:inline-flex;align-items:center;gap:.5rem;min-height:44px;padding:0 .75rem;
  font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-muted);transition:color .2s,transform .25s var(--ease)}
.back-top:hover{color:var(--color-text);transform:translateY(-3px)}

/* reveal primitives (JS adds nothing — GSAP animates; fallback shows) */
[data-reveal]{opacity:0}
.no-js [data-reveal],.reduced-motion [data-reveal]{opacity:1}

/* ============================================================
   Project / hobby sub-pages (davidwc.com/<slug>/)
   ============================================================ */
.subpage-main{min-height:100svh;display:flex;flex-direction:column;justify-content:center;padding-top:var(--nav-h);
  background:linear-gradient(180deg,var(--band-hero-1),var(--band-about-2))}
.school-case-main{justify-content:flex-start;padding-block:calc(var(--nav-h) + 3rem) 5rem}
.crumb{font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-muted);margin-bottom:1.5rem}
.crumb a{color:var(--color-accent)}
.crumb a:hover{text-decoration:underline}
.subpage-main h1{font-size:clamp(2.25rem,6vw,4rem);max-width:16ch}
.subpage-main .coming{margin-top:1.5rem;color:var(--color-muted);max-width:72ch}
.subpage-main .coming + .coming{margin-top:.75rem}
.subpage-main .coming a{color:var(--color-accent);text-decoration:underline;text-underline-offset:3px}
.subpage-main .btn{align-self:flex-start}
.subpage-main .coming + .btn{margin-top:2rem}
.subpage-actions{display:flex;flex-wrap:wrap;gap:1rem;margin-top:2.5rem}
.rollcall-hero{display:grid;grid-template-columns:minmax(0,1fr) minmax(220px,300px);gap:clamp(2.5rem,7vw,7rem);align-items:center;max-width:1100px}
.rollcall-copy{min-width:0}
.rollcall-icon-link{display:block;width:min(100%,300px);justify-self:end;color:inherit;text-decoration:none}
.rollcall-icon{width:100%;margin:0;padding:.75rem;border:1px solid var(--color-border);border-radius:calc(var(--radius) * 1.5);background:var(--color-card);box-shadow:0 22px 55px rgba(0,0,0,.22);transform:rotate(2deg);transition:transform .35s var(--ease),border-color .35s}
.rollcall-icon:hover{transform:rotate(0) translateY(-4px);border-color:rgba(var(--primary-rgb),.55)}
.rollcall-icon-link:focus-visible{outline:none}
.rollcall-icon-link:focus-visible .rollcall-icon{outline:2px solid var(--color-accent);outline-offset:4px;border-color:var(--color-accent)}
.rollcall-icon img{width:100%;aspect-ratio:1;object-fit:cover;border-radius:var(--radius)}
.rollcall-icon figcaption{margin:.7rem .35rem .15rem;font-family:var(--font-mono);font-size:var(--fs-small);color:var(--color-muted);text-align:center}
.notebook-section{margin-top:clamp(3rem,7vw,5rem)}
.notebook-heading{display:flex;align-items:end;justify-content:space-between;gap:1rem;margin-bottom:1rem}
.notebook-heading h2{font-size:clamp(1.5rem,3vw,2rem)}
.notebook-heading p{color:var(--color-muted);font-size:.9375rem;text-align:right}
.notebook-frame{width:100%;height:min(78vh,900px);min-height:620px;overflow:hidden;
  border:1px solid var(--color-border);border-radius:var(--radius);background:var(--color-card);
  box-shadow:0 22px 55px rgba(0,0,0,.22)}
.notebook-frame iframe{display:block;width:100%;height:100%;border:0;background:var(--color-card)}
.presentation-section{margin-top:clamp(3rem,7vw,5rem)}
.presentation-frame{width:100%;max-width:850px;margin-inline:auto;overflow:hidden;
  border:1px solid var(--color-border);border-radius:var(--radius);background:var(--color-card);
  box-shadow:0 22px 55px rgba(0,0,0,.22)}
.presentation-frame img{display:block;width:100%;height:auto;background:var(--color-card)}
.presentation-actions{display:flex;justify-content:center;flex-wrap:wrap;gap:1rem;margin-top:1.25rem}
.tableau-section{margin-top:3rem}
.tableau-section h2{font-size:clamp(1.5rem,3vw,2rem);margin-bottom:1rem}
.tableau-frame{width:100%;overflow:hidden;border:1px solid var(--color-border);border-radius:var(--radius);
  background:var(--color-card);box-shadow:0 22px 55px rgba(0,0,0,.22)}
.tableau-frame tableau-viz{display:block;width:100%;min-height:780px}
.tableau-frame .tableauPlaceholder{width:100%}
.tableau-frame .tableauPlaceholder img{display:block;width:100%;height:auto}
.tableau-note{margin-top:.75rem;color:var(--color-muted);font-size:.9375rem}
.tableau-note a{color:var(--color-accent);text-decoration:underline;text-underline-offset:3px}
@media(max-width:768px){
  .rollcall-hero{grid-template-columns:1fr;gap:2rem;align-items:start}
  .rollcall-icon-link{order:-1;justify-self:start;width:min(42vw,190px);min-width:150px}
  .school-case-main{padding-block:calc(var(--nav-h) + 2rem) 3rem}
  .notebook-heading{display:block}
  .notebook-heading p{margin-top:.5rem;text-align:left}
  .notebook-frame{height:70vh;min-height:520px}
  .tableau-frame tableau-viz{min-height:640px;height:640px}
}
