/* Only loaded on /education/. Shared portfolio typography and controls stay intact. */
.education-page{overflow-x:clip}
.education-page :is(h1,h2,h3,p,figure){margin:0}
.education-page a{color:inherit;text-decoration:none}
.education-page button{font:inherit}
.education-page [hidden]{display:none!important}
.education-main {
  width: 100%;
  max-width: none;
  margin: 0 auto;
  padding: 0 var(--page-padding);
}
.education-hero{display:grid;grid-template-columns:minmax(0,1fr) 270px;gap:72px;align-items:center;padding:64px 0 68px}
.education-hero .section-label{margin-bottom:20px}
.education-hero h1{font-size:clamp(38px,4.4vw,62px);font-weight:700;line-height:1.12;letter-spacing:-2.4px;text-wrap:balance;max-width:790px}
.jade-period{color:var(--jade-500)}
.education-hero-lead{font-size:19px;line-height:1.65;max-width:620px;margin-top:23px!important}
.education-hero-note{color:var(--color-text-secondary);font-size:13px;line-height:1.8;max-width:570px;margin-top:13px!important}
.education-hero-sketch{width:100%;max-width:270px;fill:none;stroke:var(--charcoal-900);stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;opacity:.8}
.sketch-jade{stroke:var(--jade-500)}
.sketch-pale{fill:var(--jade-50);stroke:none}
.sketch-fill{fill:var(--jade-500);stroke:var(--charcoal-900)}
.sketch-muted{stroke:var(--gray-green-600);stroke-width:1.4}
.academic-journey{padding:32px 0 84px;border-top:1px solid var(--color-border-default)}
.journey-annotation{font-size:10px;line-height:1.65;font-weight:500;color:var(--jade-700);letter-spacing:1.1px;text-transform:uppercase}
.certificate-dialog{width:min(710px,calc(100% - 48px));max-height:calc(100dvh - 64px);max-height:calc(100svh - 64px);overflow:auto;overscroll-behavior:contain;margin:auto;padding:34px;border:1px solid var(--color-border-default);border-radius:20px;background:var(--color-bg-surface);color:var(--color-text-primary)}
.certificate-dialog:not([open]){display:none}
.certificate-dialog[open]{animation:certificate-enter .2s ease-out}
.certificate-dialog::backdrop{background:rgba(23,33,30,.28)}
@keyframes certificate-enter{from{opacity:0;transform:scale(.985)}to{opacity:1;transform:scale(1)}}
.certificate-dialog>.journey-annotation{padding-right:40px;margin-bottom:22px}
.certificate-close{position:absolute;right:17px;top:14px;width:44px;height:44px;display:grid;place-items:center;padding:0;border:0;border-radius:10px;background:transparent;color:var(--charcoal-900)}
.certificate-close:hover{background:var(--jade-50)}
.certificate-close svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round}
.certificate-dialog h2{font-size:23px;line-height:1.45;font-weight:600;letter-spacing:-.55px;margin-top:24px}
.certificate-modal-institution{font-size:13px;color:var(--color-text-secondary);margin-top:10px!important}
.certificate-modal-meta{font-size:11px;font-weight:500;color:var(--jade-700);margin-top:15px!important}
.certificate-modal-skills{font-size:11px;color:var(--color-text-secondary);line-height:1.8;margin-top:9px!important}
.certificate-no-image{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;min-height:174px;padding:24px;border:1px solid var(--color-border-default);border-radius:12px;background:var(--color-bg-primary);text-align:center}
.certificate-no-image svg{width:46px;height:51px;fill:none;stroke:var(--jade-700);stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round}
.certificate-no-image p,.certificate-image-error{font-size:12px;line-height:1.8;color:var(--color-text-secondary)}
.certificate-image{display:block;width:100%;height:auto;max-height:48svh;object-fit:contain;border-radius:6px}
.certificate-image-error{padding:28px 0}
.certificate-slide{position:relative}
.certificate-arrow{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:44px;display:grid;place-items:center;border-radius:50%;border:1px solid var(--color-border-default);background:var(--color-bg-surface);color:var(--jade-700);font-size:22px;line-height:1;cursor:pointer;box-shadow:0 2px 10px rgba(23,33,30,.14);z-index:2}
.certificate-previous{left:12px}
.certificate-next{right:12px}
.certificate-arrow:hover{background:var(--jade-500);border-color:var(--jade-500);color:#fff}
.certificate-arrow:disabled{opacity:.35;cursor:default}
.certificate-arrow:disabled:hover{background:var(--color-bg-surface);border-color:var(--color-border-default);color:var(--jade-700)}
.certificate-image-position{font-size:11px;color:var(--color-text-secondary);text-align:center;margin-top:12px}
.education-page.certificate-open{overflow:hidden}
.journey-heading{margin-bottom:36px}
/* Timeline: one continuous S-curve (SVG, drawn by education.js) behind alternating cards. */
.timeline-wrap{--tl-accent:#2F6F5E;--tl-line:#CFDAD4;--tl-dot-bg:var(--color-bg-primary,#F7F9F7);position:relative;padding:8px 0}
.timeline-curve{position:absolute;inset:0;width:100%;height:100%;overflow:visible;z-index:0;pointer-events:none;fill:none;stroke:var(--tl-line);stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round}
.timeline-list{position:relative;z-index:1;display:grid;gap:28px;list-style:none;margin:0;padding:0}
.timeline-item{position:relative;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);column-gap:64px}
.timeline-item:nth-child(odd) .timeline-card{grid-column:1}
.timeline-item:nth-child(even) .timeline-card{grid-column:2}
.timeline-dot{position:absolute;z-index:2;top:34px;left:calc(50% - 18px);width:14px;height:14px;margin:-7px 0 0 -7px;box-sizing:border-box;border:3px solid var(--tl-accent);border-radius:50%;background:var(--tl-dot-bg)}
.timeline-item:nth-child(even) .timeline-dot{left:calc(50% + 18px)}
.timeline-card{display:grid;grid-template-columns:220px minmax(0,1fr);min-width:0;overflow:hidden;background:var(--color-bg-surface);border:1px solid var(--color-border-default);transition:border-color .2s}
.timeline-card:hover,.timeline-card-degree{border-color:var(--tl-accent)}
.timeline-media{position:relative;display:grid;place-items:center;width:100%;min-height:150px;margin:0;padding:0;overflow:hidden;border:0;border-right:1px solid var(--color-border-default);border-radius:0;background:var(--color-bg-accent);color:var(--color-text-secondary);font-size:11px;line-height:1.5;text-align:center}
button.timeline-media:not(:disabled){cursor:pointer}
.timeline-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.timeline-media.has-image .timeline-placeholder{display:none}
.timeline-media .category-icon{width:84px;height:84px;color:var(--charcoal-900)}
.timeline-media.has-icon .timeline-placeholder{display:none}
.timeline-media-logo img.university-mark{position:static;width:60px;height:60px;object-fit:contain}
.university-fallback{font-size:18px;font-weight:700;letter-spacing:1px;color:var(--tl-accent)}
.timeline-content{display:flex;flex-direction:column;justify-content:center;min-width:0;padding:16px 18px;overflow-wrap:anywhere}
.timeline-year{font-size:11px;font-weight:700;line-height:1.5;letter-spacing:.4px;color:var(--tl-accent)}
.timeline-title{font-size:16px;font-weight:700;line-height:1.4;letter-spacing:-.2px;margin-top:4px!important}
.timeline-issuer{font-size:13px;line-height:1.5;color:var(--color-text-secondary);margin-top:2px!important}
.timeline-focus{font-size:12px;line-height:1.6;color:var(--color-text-secondary);margin-top:8px!important}
.timeline-link{align-self:flex-start;margin:6px 0 0;padding:4px 0 1px;border:0;border-bottom:1px solid transparent;background:none;color:var(--tl-accent);font-size:12px;font-weight:600;line-height:1.5;cursor:pointer;transition:border-color .2s}
.timeline-link:hover,.timeline-link:focus-visible{border-bottom-color:var(--tl-accent)}
.timeline-link:disabled{visibility:hidden;cursor:default}
.certificate-verify-btn{display:inline-block;margin-top:18px;padding:4px 0 1px;border-bottom:1px solid transparent;color:var(--jade-700);font-size:13px;font-weight:600;line-height:1.5;text-decoration:none}
.certificate-verify-btn:hover,.certificate-verify-btn:focus-visible{border-bottom-color:var(--jade-700)}
.education-noscript{font-size:12px;line-height:1.8;color:var(--color-text-secondary);padding-bottom:32px}
.education-page .footer-links{display:flex;gap:28px}
.education-page .footer-links a:hover{color:var(--jade-700)}
@media(max-width:1050px){
 .education-hero{grid-template-columns:minmax(0,1fr) 190px;gap:32px}
 .education-hero h1{font-size:46px}
 .timeline-item{column-gap:56px}
}
@media(max-width:760px){
 .education-hero{padding:42px 0 44px;gap:20px;grid-template-columns:1fr}
 .education-hero-sketch{display:none}
 .education-hero h1{font-size:clamp(35px,7.7vw,48px);letter-spacing:-1.5px;max-width:600px}
 .education-hero-lead{font-size:16px;margin-top:20px!important}
 .education-hero-note{font-size:12px;margin-top:12px!important}
 .education-hero .section-label{margin-bottom:16px}
 .academic-journey{padding:25px 0 58px}
 .journey-heading{margin-bottom:30px}
 .journey-annotation{font-size:9px;letter-spacing:.9px}
 .timeline-list{gap:24px}
 .timeline-item{display:block;padding-left:36px}
 .timeline-item:nth-child(n) .timeline-card{grid-column:auto}
 .timeline-dot,.timeline-item:nth-child(even) .timeline-dot{left:11px}
 .timeline-card{grid-template-columns:140px minmax(0,1fr)}
 .timeline-media-logo img.university-mark{width:48px;height:48px}
 .certificate-dialog{width:calc(100% - 24px);max-height:calc(100svh - 32px);padding:26px 22px;border-radius:16px}
 .certificate-dialog h2{font-size:20px}
 .certificate-close{right:9px;top:7px}
 .certificate-no-image{min-height:150px}
 .education-page .nav-links{box-shadow:none;border-radius:0}
}
@media(prefers-reduced-motion:reduce){
 .education-page *,.education-page *::before,.education-page *::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}
}
@media(forced-colors:active){
 .timeline-dot{background:Canvas;border-color:CanvasText}
 .timeline-curve{stroke:CanvasText}
 .timeline-card{border-color:CanvasText}
 .certificate-dialog{border-color:CanvasText}
}

/* Animated Python category icon (reusable component, no dependencies). */
.python-stage{display:inline-block;line-height:0;transition:transform .45s cubic-bezier(.34,1.4,.4,1)}
.python-stage:hover{transform:translateY(-6px) scale(1.04)}
.python-icon{display:block;width:100%;height:auto;overflow:visible}
@media (prefers-reduced-motion: no-preference){
.python-float{animation:py-float 6s ease-in-out infinite}
.python-tilt{animation:py-tilt 9s ease-in-out infinite;transform-box:fill-box;transform-origin:center}
.python-breathe{animation:py-breathe 6s ease-in-out infinite;transform-box:fill-box;transform-origin:center}
.python-upper{animation:py-float 4.6s ease-in-out -1.2s infinite}
.python-lower{animation:py-float 5.4s ease-in-out -2.8s infinite}
.python-draw{stroke-dasharray:1;stroke-dashoffset:1}
.python-head{transform-box:fill-box;transform-origin:center;transform:scale(0)}
.python-eye{opacity:0}
.python-stage.is-inview .python-draw{animation:py-draw 1.5s cubic-bezier(.5,0,.3,1) forwards}
.python-stage.is-inview .python-upper .python-draw{animation-delay:.15s}
.python-stage.is-inview .python-lower .python-draw{animation-delay:.45s}
.python-stage.is-inview .python-head{animation:py-pop .7s cubic-bezier(.34,1.56,.64,1) forwards}
.python-stage.is-inview .python-upper .python-head{animation-delay:1s}
.python-stage.is-inview .python-lower .python-head{animation-delay:1.25s}
.python-stage.is-inview .python-eye{animation:py-fade .5s ease forwards}
.python-stage.is-inview .python-upper .python-eye{animation-delay:1.45s}
.python-stage.is-inview .python-lower .python-eye{animation-delay:1.65s}
.python-dot{animation:py-dot 4.5s ease-in-out infinite}
.python-dot.d2{animation-delay:-1.2s;animation-duration:5.2s}
.python-dot.d3{animation-delay:-2.6s}
.python-dot.d4{animation-delay:-3.4s;animation-duration:3.8s}
.python-accent{stroke-dasharray:1;stroke-dashoffset:1;opacity:0}
.python-stage.is-inview .python-accent{animation:py-accent 7s ease-in-out infinite}
.python-stage.is-inview .python-accent.a2{animation-delay:1.8s}
.python-stage.is-inview .python-accent.a3{animation-delay:3.6s}
.python-stage.is-inview .python-accent.a4{animation-delay:5.2s}
.python-stage:hover .python-breathe{animation-duration:3s}
.python-stage:hover .python-float{animation-duration:3.4s}
.python-stage:hover .python-dot{animation-duration:2.6s;opacity:1}
}
@keyframes py-float{0%,100%{transform:translateY(-4px)}50%{transform:translateY(0)}}
@keyframes py-tilt{0%,100%{transform:rotate(-1deg)}50%{transform:rotate(1deg)}}
@keyframes py-breathe{0%,100%{transform:scale(1)}50%{transform:scale(1.02)}}
@keyframes py-draw{to{stroke-dashoffset:0}}
@keyframes py-pop{to{transform:scale(1)}}
@keyframes py-fade{to{opacity:1}}
@keyframes py-dot{0%,100%{transform:translate(0,0);opacity:.85}50%{transform:translate(4px,-6px);opacity:.3}}
@keyframes py-accent{0%{stroke-dashoffset:1;opacity:0}12%{opacity:.9}40%{stroke-dashoffset:0;opacity:.9}75%{stroke-dashoffset:0;opacity:.9}100%{stroke-dashoffset:0;opacity:0}}
@media (prefers-reduced-motion: reduce){
.python-stage,.python-stage *{animation:none!important;transition:none!important}
}

/* Animated Linux category icon (reusable component, no dependencies). */
   .linux-stage {
     display: inline-block;
     line-height: 0;
     transition: transform 0.45s cubic-bezier(0.34, 1.4, 0.4, 1);
   }
   .linux-stage:hover { transform: translateY(-6px) scale(1.04); }
   .linux-icon { display: block; width: 100%; height: auto; overflow: visible; }

   /* All motion lives behind this media query so that
      prefers-reduced-motion users always get the finished artwork. */
   @media (prefers-reduced-motion: no-preference) {
     .linux-float   { animation: lx-float 6s ease-in-out infinite; }
     .linux-tilt    { animation: lx-tilt 9s ease-in-out infinite;
                      transform-box: fill-box; transform-origin: center; }
     .linux-breathe { animation: lx-breathe 6s ease-in-out infinite;
                      transform-box: fill-box; transform-origin: center; }

     /* Entrance states (invisible until .is-inview triggers the reveal). */
     .linux-draw   { stroke-dasharray: 1; stroke-dashoffset: 1; fill-opacity: 0; }
     .linux-pop    { transform: scale(0); transform-box: fill-box; transform-origin: center; }
     .linux-fadein { opacity: 0; }

     .linux-stage.is-inview .linux-draw {
       animation: lx-draw 1.4s ease-out var(--d, 0s) forwards,
                  lx-fillin 0.8s ease-out calc(var(--d, 0s) + 0.55s) forwards;
     }
     .linux-stage.is-inview .linux-pop {
       animation: lx-pop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) var(--d, 0s) forwards;
     }
     .linux-stage.is-inview .linux-fadein {
       animation: lx-fade 0.6s ease var(--d, 0s) forwards;
     }

     /* Idle life: occasional blink, tiny alternating feet, slow gear. */
     .linux-eye { transform-box: fill-box; transform-origin: center;
                  animation: lx-blink 6s ease-in-out infinite; }
     .foot-l { transform-box: fill-box; transform-origin: 50% 20%;
               animation: lx-foot 3s ease-in-out infinite; }
     .foot-r { transform-box: fill-box; transform-origin: 50% 20%;
               animation: lx-foot 3s ease-in-out -1.5s infinite; }
     .linux-gearspin { transform-box: fill-box; transform-origin: center;
                       animation: lx-spin 15s linear infinite; }
     .term-cursor { animation: lx-cursor 1.2s ease-in-out infinite; }

     /* Decorative dots drift outward and fade, each on its own rhythm. */
     .linux-dot  { animation: lx-dot 4.5s ease-in-out infinite; }
     .linux-dot.d2 { animation-delay: -1.2s; animation-duration: 5.2s; }
     .linux-dot.d3 { animation-delay: -2.6s; }
     .linux-dot.d4 { animation-delay: -3.4s; animation-duration: 3.8s; }

     /* Curved sketch strokes slowly draw, hold, and release. */
     .linux-accent { stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; }
     .linux-stage.is-inview .linux-accent { animation: lx-accent 7s ease-in-out infinite; }
     .linux-stage.is-inview .linux-accent.a2 { animation-delay: 1.8s; }
     .linux-stage.is-inview .linux-accent.a3 { animation-delay: 3.6s; }

     /* Hover: everything gets slightly more energetic. */
     .linux-stage:hover .linux-breathe  { animation-duration: 3s; }
     .linux-stage:hover .linux-gearspin { animation-duration: 6s; }
     .linux-stage:hover .foot-l,
     .linux-stage:hover .foot-r          { animation-duration: 1.6s; }
     .linux-stage:hover .linux-dot      { animation-duration: 2.6s; opacity: 1; }
     .linux-stage:hover .term-box       { stroke-opacity: 1; }
   }

   @keyframes lx-float   { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
   @keyframes lx-tilt    { 0%, 100% { transform: rotate(-1deg); } 50% { transform: rotate(1deg); } }
   @keyframes lx-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.015); } }
   @keyframes lx-draw    { to { stroke-dashoffset: 0; } }
   @keyframes lx-fillin  { to { fill-opacity: 1; } }
   @keyframes lx-pop     { to { transform: scale(1); } }
   @keyframes lx-fade    { to { opacity: 1; } }
   @keyframes lx-blink   { 0%, 91%, 100% { transform: scaleY(1); } 94%, 96% { transform: scaleY(0.08); } }
   @keyframes lx-foot    { 0%, 100% { transform: rotate(-4deg); } 50% { transform: rotate(4deg); } }
   @keyframes lx-spin    { to { transform: rotate(360deg); } }
   @keyframes lx-cursor   { 0%, 100% { opacity: 1; } 50% { opacity: 0.15; } }
   @keyframes lx-dot     { 0%, 100% { transform: translate(0, 0); opacity: 0.85; }
                           50% { transform: translate(4px, -6px); opacity: 0.3; } }
   @keyframes lx-accent  { 0%   { stroke-dashoffset: 1; opacity: 0; }
                           12%  { opacity: 0.9; }
                           40%  { stroke-dashoffset: 0; opacity: 0.9; }
                           75%  { stroke-dashoffset: 0; opacity: 0.9; }
                           100% { stroke-dashoffset: 0; opacity: 0; } }

   @media (prefers-reduced-motion: reduce) {
     .linux-stage, .linux-stage * { animation: none !important; transition: none !important; }
   }

/* Animated Machine Learning category icon (reusable component, no dependencies). */
   .ml-stage {
     display: inline-block;
     line-height: 0;
     transition: transform 0.45s cubic-bezier(0.34, 1.4, 0.4, 1);
   }
   .ml-stage:hover { transform: translateY(-6px) scale(1.04); }
   .ml-icon { display: block; width: 100%; height: auto; overflow: visible; }

   /* All motion lives behind this media query so that
      prefers-reduced-motion users always get the finished artwork. */
   @media (prefers-reduced-motion: no-preference) {
     .ml-float   { animation: ml-float 6s ease-in-out infinite; }
     .ml-breathe { animation: ml-breathe 6s ease-in-out infinite;
                   transform-box: fill-box; transform-origin: center; }

     /* Entrance states (invisible until .is-inview triggers the reveal). */
     .ml-draw   { stroke-dasharray: 1; stroke-dashoffset: 1; fill-opacity: 0; }
     .ml-pop    { transform: scale(0); transform-box: fill-box; transform-origin: center; }
     .ml-fadein { opacity: 0; }

     .ml-stage.is-inview .ml-draw {
       animation: ml-draw 1.2s ease-out var(--d, 0s) forwards,
                  ml-fillin 0.6s ease-out calc(var(--d, 0s) + 0.5s) forwards;
     }
     .ml-stage.is-inview .ml-pop {
       animation: ml-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) var(--d, 0s) forwards;
     }
     .ml-stage.is-inview .ml-fadein {
       animation: ml-fade 0.5s ease var(--d, 0s) forwards;
     }

     /* Idle life. */
     .ml-node { transform-box: fill-box; transform-origin: center; opacity: 0.92;
                animation: ml-pulse 3.2s ease-in-out infinite;
                animation-delay: var(--pd, 0s); }
     .ml-signal { offset-rotate: 0deg;
                  animation: ml-flow 6s linear infinite;
                  animation-delay: var(--sd, 0s); }
     .ml-bar { transform-box: fill-box; transform-origin: 50% 100%;
               animation: ml-bar 3.6s ease-in-out infinite;
               animation-delay: var(--bd, 0s); }
     .ml-fold-loop { animation: ml-foldloop 8s ease-in-out 2.2s infinite; }
     /* The looping fold needs its entrance + loop in one declaration,
        otherwise the entrance rule (higher specificity) would win forever. */
     .ml-stage.is-inview .ml-draw.ml-fold-loop {
       animation: ml-draw 1.2s ease-out var(--d, 0s) forwards,
                  ml-fillin 0.6s ease-out calc(var(--d, 0s) + 0.5s) forwards,
                  ml-foldloop 8s ease-in-out 2.2s infinite;
     }

     /* Decorative dots drift outward and fade, each on its own rhythm. */
     .ml-dot  { animation: ml-dot 4.5s ease-in-out infinite; }
     .ml-dot.d2 { animation-delay: -1.2s; animation-duration: 5.2s; }
     .ml-dot.d3 { animation-delay: -2.6s; }
     .ml-dot.d4 { animation-delay: -3.4s; animation-duration: 3.8s; }

     /* Curved sketch strokes slowly draw, hold, and release. */
     .ml-accent { stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; }
     .ml-stage.is-inview .ml-accent { animation: ml-accent 7s ease-in-out infinite; }
     .ml-stage.is-inview .ml-accent.a2 { animation-delay: 1.8s; }

     /* Hover: slightly more energetic, never spinny. */
     .ml-stage:hover .ml-breathe { animation-duration: 3s; }
     .ml-stage:hover .ml-float   { animation-duration: 3.4s; }
     .ml-stage:hover .ml-node   { animation-duration: 1.4s; opacity: 1; }
     .ml-stage:hover .ml-signal { animation-duration: 3s; }
     .ml-stage:hover .ml-bar    { animation-duration: 1.6s; }
     .ml-stage:hover .ml-dot    { animation-duration: 2.6s; opacity: 1; }
   }

   @keyframes ml-float   { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
   @keyframes ml-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.015); } }
   @keyframes ml-draw    { to { stroke-dashoffset: 0; } }
   @keyframes ml-fillin  { to { fill-opacity: 1; } }
   @keyframes ml-pop     { to { transform: scale(1); } }
   @keyframes ml-fade    { to { opacity: 1; } }
   @keyframes ml-pulse   { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } }
   @keyframes ml-flow    { 0% { offset-distance: 0%; opacity: 0; }
                           15% { opacity: 0.9; } 85% { opacity: 0.9; }
                           100% { offset-distance: 100%; opacity: 0; } }
   @keyframes ml-bar     { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(1.12); } }
   @keyframes ml-foldloop { 0%, 82%, 100% { stroke-dashoffset: 0; }
                            88%, 94% { stroke-dashoffset: 1; } }
   @keyframes ml-dot     { 0%, 100% { transform: translate(0, 0); opacity: 0.85; }
                           50% { transform: translate(4px, -6px); opacity: 0.3; } }
   @keyframes ml-accent  { 0%   { stroke-dashoffset: 1; opacity: 0; }
                           12%  { opacity: 0.9; }
                           40%  { stroke-dashoffset: 0; opacity: 0.9; }
                           75%  { stroke-dashoffset: 0; opacity: 0.9; }
                           100% { stroke-dashoffset: 0; opacity: 0; } }

   @media (prefers-reduced-motion: reduce) {
     .ml-stage, .ml-stage * { animation: none !important; transition: none !important; }
   }

/* Animated Web Development category icon (reusable component, no dependencies). */
   .web-stage {
     display: inline-block;
     line-height: 0;
     transition: transform 0.45s cubic-bezier(0.34, 1.4, 0.4, 1);
   }
   .web-stage:hover { transform: translateY(-6px) scale(1.04); }
   .web-icon { display: block; width: 100%; height: auto; overflow: visible; }

   /* All motion lives behind this media query so that
      prefers-reduced-motion users always get the finished artwork. */
   @media (prefers-reduced-motion: no-preference) {
     .web-float { animation: web-float 6s ease-in-out infinite; }
     .web-tilt  { animation: web-tilt 9s ease-in-out infinite;
                  transform-box: fill-box; transform-origin: center; }

     /* Entrance states (invisible until .is-inview triggers the reveal). */
     .web-draw   { stroke-dasharray: 1; stroke-dashoffset: 1; fill-opacity: 0; }
     .web-pop    { transform: scale(0); transform-box: fill-box; transform-origin: center; }
     .web-fadein { opacity: 0; }

     .web-stage.is-inview .web-draw {
       animation: web-draw 1.2s ease-out var(--d, 0s) forwards,
                  web-fillin 0.6s ease-out calc(var(--d, 0s) + 0.5s) forwards;
     }
     .web-stage.is-inview .web-pop {
       animation: web-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) var(--d, 0s) forwards;
     }
     .web-stage.is-inview .web-fadein {
       animation: web-fade 0.5s ease var(--d, 0s) forwards;
     }

     /* Idle life. */
     .web-cloudbob { animation: web-cloudbob 5s ease-in-out infinite; }
     .web-pagesettle { animation: web-settle 7s ease-in-out infinite; }
     .web-signal { offset-rotate: 0deg;
                   animation: web-flow 7s linear infinite;
                   animation-delay: var(--sd, 0s); }
     .web-cursor { animation: web-cursor 1.2s ease-in-out infinite; }
     .px-code { transition: transform 0.45s cubic-bezier(0.34, 1.4, 0.4, 1); }

     /* Decorative dots drift outward and fade, each on its own rhythm. */
     .web-dot  { animation: web-dot 4.5s ease-in-out infinite; }
     .web-dot.d2 { animation-delay: -1.2s; animation-duration: 5.2s; }
     .web-dot.d3 { animation-delay: -2.6s; }
     .web-dot.d4 { animation-delay: -3.4s; animation-duration: 3.8s; }

     /* Curved sketch strokes slowly draw, hold, and release. */
     .web-accent { stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; }
     .web-stage.is-inview .web-accent { animation: web-accent 7s ease-in-out infinite; }
     .web-stage.is-inview .web-accent.a2 { animation-delay: 1.8s; }

     /* Hover: subtle and professional. */
     .web-stage:hover .web-float { animation-duration: 3.4s; }
     .web-stage:hover .px-code { transform: translate(2px, -3px); }
     .web-stage:hover .web-signal { animation-duration: 3.5s; }
     .web-stage:hover .web-cloudbob { animation-duration: 2.5s; }
     .web-stage:hover .web-pagesettle { animation-duration: 3.5s; opacity: 1; }
     .web-stage:hover .web-dot { animation-duration: 2.6s; opacity: 1; }
   }

   @keyframes web-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
   @keyframes web-tilt  { 0%, 100% { transform: rotate(-0.5deg); } 50% { transform: rotate(0.5deg); } }
   @keyframes web-draw   { to { stroke-dashoffset: 0; } }
   @keyframes web-fillin { to { fill-opacity: 1; } }
   @keyframes web-pop    { to { transform: scale(1); } }
   @keyframes web-fade   { to { opacity: 1; } }
   @keyframes web-cloudbob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
   @keyframes web-settle { 0%, 100% { opacity: 0.9; } 50% { opacity: 1; } }
   @keyframes web-flow   { 0% { offset-distance: 0%; opacity: 0; }
                           15% { opacity: 0.85; } 85% { opacity: 0.85; }
                           100% { offset-distance: 100%; opacity: 0; } }
   @keyframes web-cursor { 0%, 100% { opacity: 1; } 50% { opacity: 0.15; } }
   @keyframes web-dot    { 0%, 100% { transform: translate(0, 0); opacity: 0.85; }
                           50% { transform: translate(4px, -6px); opacity: 0.3; } }
   @keyframes web-accent { 0%   { stroke-dashoffset: 1; opacity: 0; }
                           12%  { opacity: 0.9; }
                           40%  { stroke-dashoffset: 0; opacity: 0.9; }
                           75%  { stroke-dashoffset: 0; opacity: 0.9; }
                           100% { stroke-dashoffset: 0; opacity: 0; } }

   @media (prefers-reduced-motion: reduce) {
     .web-stage, .web-stage * { animation: none !important; transition: none !important; }
   }

/* Animated Artificial Intelligence category icon (reusable component, no dependencies). */
   .ai-stage {
     display: inline-block;
     line-height: 0;
     transition: transform 0.45s cubic-bezier(0.34, 1.4, 0.4, 1);
   }
   .ai-stage:hover { transform: translateY(-6px) scale(1.04); }
   .ai-icon { display: block; width: 100%; height: auto; overflow: visible; }

   /* All motion lives behind this media query so that
      prefers-reduced-motion users always get the finished artwork. */
   @media (prefers-reduced-motion: no-preference) {
     .ai-float { animation: ai-float 6s ease-in-out infinite; }
     .ai-tilt  { animation: ai-tilt 9s ease-in-out infinite;
                 transform-box: fill-box; transform-origin: center; }

     /* Entrance states (invisible until .is-inview triggers the reveal). */
     .ai-draw   { stroke-dasharray: 1; stroke-dashoffset: 1; fill-opacity: 0; }
     .ai-pop    { transform: scale(0); transform-box: fill-box; transform-origin: center; }
     .ai-fadein { opacity: 0; }

     .ai-stage.is-inview .ai-draw {
       animation: ai-draw 1.2s ease-out var(--d, 0s) forwards,
                  ai-fillin 0.6s ease-out calc(var(--d, 0s) + 0.5s) forwards;
     }
     .ai-stage.is-inview .ai-pop {
       animation: ai-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) var(--d, 0s) forwards;
     }
     .ai-stage.is-inview .ai-fadein {
       animation: ai-fade 0.5s ease var(--d, 0s) forwards;
     }

     /* Idle life. */
     .ai-corepulse { transform-box: fill-box; transform-origin: center;
                     animation: ai-pulse 4s ease-in-out infinite; }
     .ai-node { transform-box: fill-box; transform-origin: center;
                animation: ai-pulse 3s ease-in-out infinite;
                animation-delay: var(--pd, 0s); }
     .ai-eye { transform-box: fill-box; transform-origin: center;
               animation: ai-blink 6s ease-in-out infinite; }
     .ai-signal { offset-rotate: 0deg;
                  animation: ai-flow 7s linear infinite;
                  animation-delay: var(--sd, 0s); }
     .ai-gearspin { transform-box: fill-box; transform-origin: center;
                    animation: ai-spin 15s linear infinite; }
     .ai-dbbob { animation: ai-bob 5s ease-in-out infinite; }
     .ai-cloudbob { animation: ai-bob 5s ease-in-out -2.5s infinite; }
     .ai-codecursor { animation: ai-cursor 1.4s ease-in-out infinite; }

     /* Decorative dots drift outward and fade, each on its own rhythm. */
     .ai-dot  { animation: ai-dot 4.5s ease-in-out infinite; }
     .ai-dot.d2 { animation-delay: -1.2s; animation-duration: 5.2s; }
     .ai-dot.d3 { animation-delay: -2.6s; }
     .ai-dot.d4 { animation-delay: -3.4s; animation-duration: 3.8s; }

     /* Curved sketch strokes slowly draw, hold, and release. */
     .ai-accent { stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; }
     .ai-stage.is-inview .ai-accent { animation: ai-accent 7s ease-in-out infinite; }
     .ai-stage.is-inview .ai-accent.a2 { animation-delay: 1.8s; }

     /* Hover: subtle and professional. */
     .ai-stage:hover .ai-float { animation-duration: 3.4s; }
     .ai-stage:hover .ai-corepulse { animation-duration: 2s; }
     .ai-stage:hover .ai-node { animation-duration: 1.2s; }
     .ai-stage:hover .ai-connline { opacity: 0.8; }
     .ai-stage:hover .ai-signal { animation-duration: 3.5s; }
     .ai-stage:hover .ai-gearspin { animation-duration: 6s; }
     .ai-stage:hover .ai-dot { animation-duration: 2.6s; opacity: 1; }
   }

   @keyframes ai-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
   @keyframes ai-tilt  { 0%, 100% { transform: rotate(-0.5deg); } 50% { transform: rotate(0.5deg); } }
   @keyframes ai-draw   { to { stroke-dashoffset: 0; } }
   @keyframes ai-fillin { to { fill-opacity: 1; } }
   @keyframes ai-pop    { to { transform: scale(1); } }
   @keyframes ai-fade   { to { opacity: 1; } }
   @keyframes ai-pulse  { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }
   @keyframes ai-blink  { 0%, 91%, 100% { transform: scaleY(1); } 94%, 96% { transform: scaleY(0.08); } }
   @keyframes ai-flow   { 0% { offset-distance: 0%; opacity: 0; }
                          15% { opacity: 0.85; } 85% { opacity: 0.85; }
                          100% { offset-distance: 100%; opacity: 0; } }
   @keyframes ai-spin   { to { transform: rotate(360deg); } }
   @keyframes ai-bob    { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
   @keyframes ai-cursor { 0%, 100% { opacity: 1; } 50% { opacity: 0.15; } }
   @keyframes ai-dot    { 0%, 100% { transform: translate(0, 0); opacity: 0.85; }
                          50% { transform: translate(4px, -6px); opacity: 0.3; } }
   @keyframes ai-accent { 0%   { stroke-dashoffset: 1; opacity: 0; }
                          12%  { opacity: 0.9; }
                          40%  { stroke-dashoffset: 0; opacity: 0.9; }
                          75%  { stroke-dashoffset: 0; opacity: 0.9; }
                          100% { stroke-dashoffset: 0; opacity: 0; } }

   @media (prefers-reduced-motion: reduce) {
     .ai-stage, .ai-stage * { animation: none !important; transition: none !important; }
   }

/* Test: white backdrop behind the Python card logo only. */

/* Python card test: description under the card name; no vertical divider. */
.timeline-desc{font-size:13px;line-height:1.6;color:var(--color-text-secondary);margin-top:6px!important;max-width:44ch}
.timeline-desc:empty{display:none}

/* Category cards: white logo backdrop, no divider, 120px animated logos.
   Scoped to icon cells / non-degree cards so the degree card is untouched.
   A new category only needs a `description` in education.json and an icon
   in CATEGORY_ICONS (a `*-stage` div) to inherit all of this. */
.timeline-media.has-icon{background:#fff;border-right:0}
.timeline-media [class*="-stage"]{width:120px}
.timeline-item:not(.timeline-item-degree) .timeline-link{font-size:11px}
.timeline-item:not(.timeline-item-degree) .timeline-card:hover{border-color:var(--color-border-default)}
