/*
Theme Name: jungdev
Theme URI: https://jungdev.com
Author: jungdev
Author URI: https://jungdev.com
Description: Hand-coded WordPress theme for jungdev — high-performance websites for small businesses in AU, US and EU.
Version: 1.2.0
Requires at least: 6.4
Requires PHP: 8.1
License: GNU General Public License v2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: jungdev
*/

/* self-hosted Poppins (was Google Fonts CDN) */
@font-face{font-family:'Poppins';font-style:normal;font-weight:400;font-display:swap;src:url('assets/fonts/poppins-400.woff2') format('woff2')}
@font-face{font-family:'Poppins';font-style:normal;font-weight:500;font-display:swap;src:url('assets/fonts/poppins-500.woff2') format('woff2')}
@font-face{font-family:'Poppins';font-style:normal;font-weight:600;font-display:swap;src:url('assets/fonts/poppins-600.woff2') format('woff2')}
@font-face{font-family:'Poppins';font-style:normal;font-weight:700;font-display:swap;src:url('assets/fonts/poppins-700.woff2') format('woff2')}
@font-face{font-family:'Poppins';font-style:normal;font-weight:800;font-display:swap;src:url('assets/fonts/poppins-800.woff2') format('woff2')}

/* jungdev — theme styles */
@view-transition{navigation:auto}
:root{
  --navy:#051b3f; --blue:#0096d6; --purple:#85529c; --red:#e45257; --gold:#ffb400;
  --blue-d:#0081b8; --purple-d:#6f4183;
  /* Site-wide dark theme: --bg is the page canvas, and --ink/--body/--mut/
     --soft/--line are redefined as their DARK-theme values since almost
     every component already references these variables rather than
     hardcoded colors — flipping the tokens here cascades correctly to
     headings, lead paragraphs and "soft" card backgrounds everywhere
     automatically. The small number of components that deliberately keep
     a light/white background regardless of page theme (the contact form
     fields, the Fiverr credential card) use the explicit --ink-lt/
     --body-lt/--mut-lt "light card" exception tokens instead, so their
     own text stays dark/readable against their own light background. */
  --bg:#040a15;
  --ink:#f2f4f8; --body:#aab4c8; --mut:#8794ad; --soft:#0d1b33; --line:#1e2c47;
  --ink-lt:#051b3f; --body-lt:#465773; --mut-lt:#61718e; --line-lt:#e3e9f2;
  /* Design system extended from the Fiverr credential card: a consistent
     radius/shadow scale applied to every content card, badge, button and
     form field site-wide, instead of the sharp-cornered flat boxes the
     theme started with. */
  --radius-lg:20px; --radius-md:14px; --radius-sm:10px; --radius-pill:999px;
  --shadow-card:0 20px 50px rgb(0,0,0,.35); --shadow-card-sm:0 10px 28px rgb(0,0,0,.3);
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;font-family:'Poppins',sans-serif;color:var(--ink);background:var(--bg)}
a{color:var(--ink);text-decoration:none}
a:hover{color:var(--blue-d)}
img{max-width:100%;display:block}
.wrap{max-width:1440px;margin:0 auto;padding-left:clamp(16px,2.5vw,40px);padding-right:clamp(16px,2.5vw,40px)}
.kicker{font-size:13px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--blue)}
.kicker.lt{color:#4db4e2}
.kicker.mut{color:var(--mut)}
.lead{font-size:clamp(15px,1.3vw,19px);line-height:1.6;max-width:52ch;color:var(--body)}
.num{font-size:13px;font-weight:700;color:var(--blue)}
.big{font-weight:700;font-size:clamp(44px,5vw,80px);line-height:1;color:inherit}
.sfx{color:var(--blue)}
.ph{background:var(--soft);color:var(--mut);display:flex;align-items:center;justify-content:center;text-align:center;font-size:13px;width:100%;height:100%;min-height:60px}
.ph-ico{width:52px;height:52px;min-height:52px;flex:0 0 52px;border-radius:50%}
.ph-ico.lg{width:72px;height:72px;flex:0 0 72px}

/* header — always visible/sticky on desktop (≥980px), unchanged from
   the original design: transparent by default so the hero/page content
   shows through behind it, switching to a solid --bg once scrolled
   (.is-scrolled, toggled by assets/js/site.js past a small threshold) or
   on hover/focus (so the nav is readable the moment you reach for it,
   even right at the top of the page before anything's scrolled). Below
   980px .burger replaces the mega-menus/text links and opens the
   full-screen template-parts/header/full-menu overlay instead — see its
   own docblock. */
.hdr{position:sticky;top:0;z-index:100;background:transparent;transition:background-color .25s ease}
.hdr:hover,.hdr:focus-within,.hdr.is-scrolled{background:var(--bg)}
/* Push the sticky header below the WP admin bar for logged-in users — the
   admin bar is position:fixed at top:0 with a higher z-index, so without
   this the header would stick under it once scrolled. Desktop only: below
   600px core switches the admin bar to position:absolute (it scrolls away
   with the page), so an offset there would leave a permanent gap instead. */
/* stylelint-disable-next-line custom-property-pattern -- WP core's own variable name (wp-includes/css/admin-bar.css), not ours to rename */
body.admin-bar .hdr{top:var(--wp-admin--admin-bar--height,32px)}
@media screen and (max-width:600px){body.admin-bar .hdr{top:0}}
.hdr-bar{position:relative;z-index:2}
.hdr-in{display:flex;align-items:stretch;justify-content:space-between;height:72px}
.brand{display:flex;align-items:center;font-weight:800;font-size:clamp(20px,1.8vw,26px);letter-spacing:.1em;text-transform:uppercase;color:#fff}
.brand span{color:var(--blue)}
.nav-desktop{display:flex;align-items:stretch}
.nlink{display:flex;align-items:center;gap:8px;padding:0 clamp(8px,1.1vw,20px);font-size:clamp(13px,1.05vw,15px);font-weight:500;color:#fff;background:transparent;border:none;cursor:pointer;font-family:inherit}
.nlink:hover{color:#fff}
.nlink.on{background:var(--blue)}
.roll{position:relative;overflow:hidden;display:inline-block;line-height:1.4}
.roll span{display:block;transition:transform .4s cubic-bezier(.76,0,.24,1)}
.roll span+span{position:absolute;left:0;top:100%}
a:hover .roll span,button:hover .roll span{transform:translateY(-100%)}
.hdr-cta{display:flex;align-items:center;margin-left:clamp(6px,1vw,16px)}
.hdr-cta a{display:flex;align-items:center;height:44px;padding:0 clamp(12px,1.4vw,22px);white-space:nowrap;background:var(--purple);border-radius:var(--radius-sm);color:#fff;font-size:14px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}
.hdr-cta a:hover{background:var(--purple-d);color:#fff}
/* mega */
.mi{display:flex;align-items:stretch}
.mi:hover>.mi-btn.mi-blue{background:var(--blue)}
.mi:hover>.mi-btn.mi-red{background:var(--red)}
.mega{display:none;position:absolute;left:0;right:0;top:100%;color:#fff;box-shadow:0 24px 48px rgb(5,27,63,.25)}
.mi:hover>.mega{display:block}
.mega-blue{background:var(--blue)}
.mega-red{background:var(--red)}
.mega-in{display:grid;grid-template-columns:minmax(220px,300px) 1fr;gap:clamp(24px,3vw,56px);padding-top:48px;padding-bottom:56px}
.mega a{color:#fff}
.mega a:hover{color:#fff}
.mega-kicker{font-size:12px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;opacity:.8}
.mega-intro h3{font-size:28px;line-height:1.2;font-weight:700;margin:12px 0 16px;text-wrap:pretty}
.mega-intro p{font-size:14px;line-height:1.6;margin:0 0 24px;opacity:.85}
.mega-all{display:inline-flex;gap:10px;font-size:14px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;border-bottom:2px solid #fff;padding-bottom:4px}
.mega-grid{display:grid;grid-template-columns:1fr 1fr;gap:6px}
.mega-grid a{display:block;padding:16px 18px}
.mega-grid a:hover{background:rgb(255,255,255,.14)}
.mega-grid .num{color:#fff;opacity:.75;margin-right:14px}
.mega-grid strong{font-size:16px}
.mega-grid em{display:block;font-style:normal;font-size:13px;line-height:1.5;opacity:.8;margin:6px 0 0 30px}
.ind-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr));gap:10px clamp(20px,3vw,56px)}
.ind-grid a{display:flex;align-items:center;gap:18px;padding:8px 10px}
.ind-grid a:hover{background:rgb(255,255,255,.14)}
.ind-grid strong{font-size:17px;letter-spacing:.03em;text-transform:uppercase}
/* mobile: .burger is the only nav control below 980px, opening
   template-parts/header/full-menu (below) instead of a dropdown of its
   own — no .mnav/.macc/.msub here anymore, that older off-canvas panel
   this used to open has been replaced outright, not just hidden. */
.burger{display:none;align-items:center;justify-content:center;width:48px;background:transparent;border:none;cursor:pointer;color:#fff}
@media (max-width:980px){.nav-desktop{display:none}.burger{display:flex}}

/* full-screen menu — the only nav surface below 980px, opened by
   .burger above (desktop keeps its original hover mega-menus untouched,
   .burger is hidden there so this never gets triggered). */
/* .full-menu-burst is a plain clip-path:circle() (not the SVG+filter
   .proj-feat-burst technique from the featured cards) growing from the
   top-right, matching roughly where .burger sits — see the comment atop
   template-parts/header/full-menu.php for why: that technique needs an
   SVG viewBox matching a FIXED aspect ratio for an undistorted stretch,
   which the viewport this covers doesn't have (portrait phone vs. wide
   desktop, no single ratio fits both). .full-menu-in (the actual nav
   content) fades in after, once the burst has mostly covered, the same
   "wait for the cover" sequencing .proj-feat-logo uses on the cards. */
.full-menu{position:fixed;inset:0;z-index:200;visibility:hidden;pointer-events:none}
.full-menu.open{visibility:visible;pointer-events:auto}
.full-menu-burst{position:absolute;inset:0;background:var(--bg);clip-path:circle(0% at 100% 0%);transition:clip-path .7s cubic-bezier(.65,0,.35,1)}
.full-menu.open .full-menu-burst{clip-path:circle(150% at 100% 0%)}
.full-menu-in{position:relative;z-index:1;height:100%;overflow-y:auto;padding:100px clamp(24px,6vw,120px) 48px;opacity:0;transition:opacity .4s ease .3s}
.full-menu.open .full-menu-in{opacity:1}
.full-menu-close{position:absolute;top:18px;right:clamp(16px,2.5vw,40px);width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:transparent;border:1px solid var(--line);color:var(--ink);cursor:pointer}
.full-menu-close:hover{background:var(--soft)}
.full-menu-nav{display:flex;flex-direction:column;gap:4px;max-width:900px}
.full-menu-nav>a,.full-menu-acc{display:flex;align-items:center;gap:24px;width:100%;padding:18px 0;border:none;border-bottom:1px solid var(--line);background:none;font-family:inherit;font-size:clamp(26px,4.6vw,52px);font-weight:700;text-transform:uppercase;color:var(--ink);text-align:left;cursor:pointer}
.full-menu-nav>a:hover,.full-menu-acc:hover{color:var(--blue)}
.full-menu-num{min-width:34px;font-size:13px;font-weight:600;color:var(--mut)}
.full-menu-acc-ic{margin-left:auto;font-size:26px;font-weight:400;color:var(--blue);transition:transform .3s ease}
.full-menu-acc.open .full-menu-acc-ic{transform:rotate(45deg)}
.full-menu-sub{display:none;flex-direction:column;padding:6px 0 18px 58px}
.full-menu-sub.open{display:flex}
.full-menu-sub a{padding:9px 0;font-size:16px;color:var(--body)}
.full-menu-sub a:hover{color:var(--blue)}
.full-menu-foot{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:20px 28px;margin-top:40px;padding-top:28px;border-top:1px solid var(--line)}
.full-menu-cta{display:inline-flex;align-items:center;height:52px;padding:0 26px;background:var(--purple);border-radius:var(--radius-sm);color:#fff;font-size:14px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}
.full-menu-cta:hover{background:var(--purple-d);color:#fff}
.full-menu-social{display:flex;flex-wrap:wrap;gap:10px 24px}
.full-menu-social a{font-size:13px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--mut)}
.full-menu-social a:hover{color:var(--ink)}
body.full-menu-open{overflow:hidden}
@media (prefers-reduced-motion:reduce){.hdr,.full-menu-burst,.full-menu-in{transition:none}}

/* sections */
main h1{font-weight:700;font-size:clamp(40px,5.5vw,88px);line-height:1;margin:16px 0 20px;max-width:16ch;text-wrap:pretty}
.sec-hero{padding-top:100px;padding-bottom:64px}
.sec-hero.two{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:clamp(32px,5vw,80px);align-items:start;padding-bottom:120px}
.sec{padding-bottom:96px}
.sec-head{display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:16px;padding-bottom:16px;border-bottom:1px solid var(--line);margin-bottom:8px}
.sec-head h2,.sec-t{font-weight:700;font-size:clamp(28px,3.5vw,52px);margin:0}
.sec-t{margin:16px 0 40px}
.all{font-size:13px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;border-bottom:2px solid var(--blue);padding-bottom:2px}
.cards2{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,420px),1fr));gap:24px}
.cards3{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));gap:24px;margin-top:32px}
.cards4{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,230px),1fr));gap:20px;margin-top:32px}
.card{display:block;background:var(--soft);border-radius:var(--radius-md);padding:28px 24px;transition:box-shadow .3s ease}
.card h3{font-size:20px;margin:14px 0 10px;line-height:1.2}
.card p{font-size:14px;line-height:1.6;margin:0;color:var(--body)}
a.card:hover{background:rgb(255,255,255,.06);color:var(--ink);box-shadow:var(--shadow-card-sm)}
.quote{background:var(--soft);border-radius:var(--radius-md);padding:36px 32px;margin:0;display:flex;flex-direction:column;gap:24px;transition:box-shadow .3s ease}
.quote:hover{box-shadow:var(--shadow-card-sm)}
.quote blockquote{font-weight:600;font-size:18px;line-height:1.5;margin:0;text-wrap:pretty}
.quote figcaption{margin-top:auto}
.quote b{display:block;font-size:15px}
.quote span{font-size:13px;color:var(--mut)}
.qtag{display:inline-block;background:rgb(255,255,255,.08);padding:5px 12px;border-radius:var(--radius-pill);font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;width:max-content}

/* home */
.loader{position:fixed;inset:0;background:var(--navy);z-index:1000;display:flex;align-items:flex-end;justify-content:space-between;padding:40px clamp(16px,2.5vw,40px);transform:translateY(0);transition:transform .8s cubic-bezier(.76,0,.24,1)}
.loader.exit{transform:translateY(-101%)}
.loader .pct{font-weight:800;font-size:clamp(64px,10vw,140px);line-height:.85;color:#fff}
.loader .pct span{color:var(--blue)}
.hero{position:relative;overflow:hidden;padding-top:110px;padding-bottom:80px}
.hero>*{position:relative;z-index:1}
.blob{position:absolute;left:0;top:0;width:520px;height:520px;border-radius:50%;background:radial-gradient(circle at 35% 35%,rgb(0,150,214,.28),rgb(133,82,156,.2) 45%,rgb(228,82,87,.14) 68%,transparent 74%);filter:blur(56px);pointer-events:none;will-change:transform;z-index:0 !important}
.hero-h{font-weight:800;font-size:clamp(56px,9vw,150px);line-height:.95;letter-spacing:-.01em;text-transform:uppercase;margin:20px 0 0;max-width:none}
.hero-h.xl{font-size:clamp(72px,13vw,220px);line-height:.9}
.hero-h>div{overflow:hidden;padding-bottom:.06em}
.hero-h span{display:inline-block;transform:translateY(115%);animation:rise .9s cubic-bezier(.22,1,.36,1) forwards}
.hero .lead{margin:32px 0 0}
.hero .kicker,.hero .lead{opacity:0;animation:fadeup .7s ease forwards}
.hero .kicker{animation-delay:1.5s}
.hero .lead{animation-delay:2.1s}
.hero-sub{display:flex;justify-content:space-between;align-items:baseline;gap:24px;flex-wrap:wrap;margin-top:24px}
.count{font-weight:800;font-size:clamp(24px,2.5vw,40px)}
.count b{color:var(--blue)}
.fadeup{opacity:0;animation:fadeup .7s ease forwards}
@keyframes rise{to{transform:translateY(0)}}
@keyframes fadeup{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:translateY(0)}}
.marquee{background:var(--soft);overflow:hidden;padding:22px 0;margin-bottom:80px}
.mq-track{display:flex;width:max-content;animation:marquee 24s linear infinite}
.mq-track span{display:flex;align-items:center;gap:30px;padding-right:30px;font-weight:700;font-size:24px;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap}
.mq-track b{color:var(--blue)}
@keyframes marquee{to{transform:translateX(-50%)}}
/* featured project cards — title/category (.proj-feat-meta) render at
   full opacity immediately, never touched by scroll-reveal or hover; only
   the screenshot box (.proj-feat-media) fades up on scroll, via the same
   generic .rev/.rev.on scroll-reveal class every other section on the
   site uses (see revCheck() in assets/js/site.js) — no bespoke CSS needed
   for that part.
   The hover sequence itself is a 2-stage timeline (rest -> mock+logo,
   then mock+logo -> video), orchestrated by assets/js/site.js via
   stage-mock/stage-video classes on .proj-feat (not pure CSS :hover — the
   crossfade to video needs a real delay so the logo gets to register).
   See template-parts/work/featured-card.php for the full breakdown.
   Rest: full-bleed screenshot, rounded corners, no border, 2-column grid
   with every 2nd card offset down (.proj-feat-grid below).
   stage-mock, all starting together (not staggered layer reveals): a
   black "burst" expands from the top-left corner via an animated
   clip-path circle until it fully covers the screenshot — since the
   burst div's own border gets clipped along with its background, the
   growing circle's edge is naturally traced by a white ring for free, no
   extra element needed; the frame border draws itself in at the same
   time (SVG stroke-dashoffset — vector-effect="non-scaling-stroke" keeps
   it hairline-thin regardless of card size, since a plain viewBox
   stretch would otherwise scale the stroke width along with the
   geometry); the 3 dots pop in one after another, not simultaneously;
   the tab draws in and shows the title; the project logo fades in
   centred on the burst. The video (if any) starts loading/playing muted
   at this point too, so it's already rolling once revealed.
   stage-video: the logo crossfades out, the video crossfades in, in
   place, inside the same drawn frame — the frame itself stays put the
   whole time the card is hovered.
   On mouseleave the JS just removes stage-mock/stage-video — every rule
   above is a plain CSS transition, so removing the class runs the exact
   same transition backwards, back to the rest state, with no separate
   "un-hover" styles needed. */
.proj-feat-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(24px,4vw,56px) clamp(18px,3vw,40px);margin-top:32px}
.proj-feat-grid .proj-feat:nth-child(2n){margin-top:clamp(32px,6vw,72px)}
@media (max-width:700px){.proj-feat-grid{grid-template-columns:1fr}.proj-feat-grid .proj-feat:nth-child(2n){margin-top:0}}
.proj-feat{display:block;color:var(--ink)}
.proj-feat:hover{color:var(--ink)}
/* Percentage radius, not a fixed px like --radius-lg elsewhere: the SVG
   border below is stretched to fill this box (preserveAspectRatio="none"
   on a viewBox with the same aspect ratio as this box, so the stretch
   itself is uniform), and its rx/ry are set to the EXACT same percentage
   of the viewBox (18/596=3.0201%, 13/423=3.0733%, not the rounded "3%"
   this used to be) — since cards in the 2-col grid render at different
   real pixel widths, only a percentage on both sides guarantees the drawn
   corner curve and .proj-feat-media's own overflow:hidden clip land on
   literally the same curve. Even a small mismatch between the two (the
   rounded "3%" vs the SVG's actual 3.02%/3.07%) shows up as a visible
   seam right at the corner, since the browser rasterizes the CSS clip and
   the SVG stroke independently — that mismatch, not the SVG itself, was
   the "mất rounded ở góc" corner artifact. The 596:423 ratio (~1.41:1, a
   touch wider than 4:3) matches the card proportions the hover-mock
   browser frame below is designed around. */
.proj-feat-media{position:relative;overflow:hidden;aspect-ratio:596/423;background:var(--navy);border-radius:3.0201% / 3.0733%}
/* Two states, not one: full-bleed at rest (the plain "no border" spec),
   then shrinks to fit the exact same inner content viewport .proj-feat-
   video/-logo/-burst/-border-inner below all already use, once hovering
   — a real browser window's page content sits inside its own chrome,
   never sprawling underneath the title bar, and this card's screenshot
   should behave the same way once that mockup frame is what's actually
   being shown, not stay full-bleed underneath it. top/left/width/height,
   not "inset" alone — <img> is a replaced element (same rule already
   worked around for .proj-feat-video below): absolutely positioned with
   inset but width/height left auto sizes from the image's own intrinsic
   dimensions instead of stretching to fill, calc() avoids that. */
.proj-feat-img{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;border-radius:0;transition:top .6s cubic-bezier(.65,0,.35,1),left .6s cubic-bezier(.65,0,.35,1),width .6s cubic-bezier(.65,0,.35,1),height .6s cubic-bezier(.65,0,.35,1),border-radius .6s ease}
.proj-feat.stage-mock .proj-feat-img,.proj-feat.stage-video .proj-feat-img{top:11%;left:1.5%;width:calc(100% - 3%);height:calc(100% - 13%);border-radius:2.0761% / 1.9022%}

/* Same inner-viewport bounds as .proj-feat-img's hover state above (and
   -video/-logo/-burst/-border-inner below) — not the full outer card
   like this used to be, which let it paint straight through the
   titlebar strip: on a light/custom "background behind logo" color that
   turned the titlebar's own white dots/tab invisible (white-on-now-
   white), the exact "phủ toàn bộ màu trắng lên cả card, không thấy
   browser frame đâu nữa" bug. Scoped to the inner box instead, the
   titlebar area is never touched by this at all — .proj-feat-media's own
   background (var(--navy)) shows through there regardless of which
   burst color is chosen, so the frame chrome stays visible no matter
   what. What actually guarantees the screenshot is fully hidden within
   THIS box once covered; .proj-feat-burst below (the organic wave) sits
   on top of this (z-index:2 vs 1) and shares the exact same bounds now,
   so the two read as one motion with no visible mismatch between "plain
   edge covers" and "detailed wave covers". */
.proj-feat-burst-fill{position:absolute;top:11%;left:1.5%;width:calc(100% - 3%);height:calc(100% - 13%);z-index:1;background:var(--proj-burst-bg,#0a0a0a);border-radius:2.0761% / 1.9022%;clip-path:circle(0% at 0% 0%);transition:clip-path .7s cubic-bezier(.65,0,.35,1);pointer-events:none}
.proj-feat.stage-mock .proj-feat-burst-fill,.proj-feat.stage-video .proj-feat-burst-fill{clip-path:circle(150% at 0% 0%)}

/* Sized/positioned to match the INNER content box exactly (same top/
   left/width/height as .proj-feat-video-wrap/-border-inner below, not
   .proj-feat-media's full outer bounds like this used to be) — the wave
   only ever covers the content viewport, never spreading up into the
   titlebar or out to the outer frame edge, since the <svg> root's own
   default overflow:hidden clips the circle (and the filter's displaced
   edge with it) to exactly this smaller box regardless of how large r
   grows. viewBox 578x368 mirrors .proj-feat-video-wrap's own computed
   inner-box ratio (11%/1.5%/2% insets on 596x423), so the
   preserveAspectRatio="none" stretch stays uniform here too.
   A single circle, not 3 offset ones like this used to be: the
   #proj-feat-wave filter (header.php — feTurbulence+feDisplacementMap,
   shared by every card on the page, defined once) pushes its edge around
   per-pixel as it grows, which reads as an irregular, bleeding/lapping
   boundary on its own — stacking multiple copies doesn't add anything
   the filter isn't already doing. Growing an SVG circle's r (not a CSS
   clip-path:circle(), which can't run through an SVG filter and keep the
   distortion — filter is applied, THEN clip-path recuts a clean edge
   over top of it, discarding exactly the effect this is for). r animates
   0 -> 700, comfortably past this viewBox's own diagonal (~685) so it
   fully covers from the top-left corner. Its own thin stroke (not a
   separate border like this used to have — that got clipped to a
   visibly notched line by .proj-feat-media's ROUNDED overflow:hidden
   corner) rides along with the same filter distortion as the fill, so
   it traces the wavy edge instead of a clean arc — the "viền sóng
   trắng" effect. Duration is short (.7s) specifically to land close to
   .proj-feat-border-outer's own .6s draw-in below, rather than dragging
   on well past it the way a slower reveal did. .proj-feat-logo further
   below is delayed to only start fading in once this finishes covering. */
/* border-radius here too, matching .proj-feat-burst-fill above and
   -border-inner/-video/-img's hover state below: an <svg> root clips its
   own content to its own viewport rectangle by default (the fully-grown
   circle would otherwise show sharp corners poking past the rounded
   border-inner line drawn on top of it — barely visible in the dark-mode
   default since a dark corner blends into everything else nearby, much
   more obvious as a stray white square corner on a light background). */
.proj-feat-burst{position:absolute;top:11%;left:1.5%;width:calc(100% - 3%);height:calc(100% - 13%);z-index:2;border-radius:2.0761% / 1.9022%;pointer-events:none}
/* fill/stroke here, not as SVG attributes on the <circle> in the
   template — need to react to --proj-burst-bg/--proj-burst-stroke
   (inc/cpt-work.php's "Background behind logo" field, read in
   featured-card.php), and a plain CSS rule is simpler than templating
   the attribute values in PHP for the same result. stroke is a
   CONTRASTING color computed alongside the fill (jungdev_readable_on()
   in inc/template-tags.php), not a plain #fff, specifically so the wave
   edge this stroke draws stays visible on a light/white fill too — a
   fixed white stroke would disappear against a same-colored fill, losing
   the "sóng loang" motion this whole hover sequence is built around.
   Everything ELSE in the card (frame border/dots/tab) stays a plain #fff
   regardless — only this one element's own colors are configurable. */
.proj-feat-burst-circle{fill:var(--proj-burst-bg,#0a0a0a);stroke:var(--proj-burst-stroke,#fff);stroke-width:1.5;transition:r .7s cubic-bezier(.65,0,.35,1)}
.proj-feat.stage-mock .proj-feat-burst-circle,.proj-feat.stage-video .proj-feat-burst-circle{r:700px}

/* A real CSS border, not an SVG stroke: SVG stroke-dashoffset drawn
   corners on this card kept misrendering — .proj-feat-border-inner had
   to be rebuilt away from it after 3 attempts, and once .proj-feat-burst
   stopped covering for it with its own (unrelated) border, this element
   turned out to have the same class of bug — part of the top-left corner
   region vanishing after the reveal animation, most likely the exact
   dasharray/vector-effect/anisotropic-scaling interaction called out
   below. Same technique as .proj-feat-border-inner now: border-radius
   matching .proj-feat-media's own exactly (3.0201%/3.0733%, so this
   traces literally the same curve as the overflow:hidden clip on the
   content underneath), revealed via an animated clip-path instead of a
   stroke. */
.proj-feat-border-outer{position:absolute;inset:0;z-index:4;border:1px solid #fff;border-radius:3.0201% / 3.0733%;clip-path:inset(0 100% 100% 0);transition:clip-path .6s ease;pointer-events:none}
.proj-feat.stage-mock .proj-feat-border-outer,.proj-feat.stage-video .proj-feat-border-outer{clip-path:inset(0 0 0 0)}

/* top/height are % of .proj-feat-media, not px: this bar's bottom edge
   (2% + 9% = 11%) has to land on EXACTLY the same number as
   .proj-feat-logo/-video/-border-inner's top inset below for the tab to
   sit flush against the inner frame line with no gap — only possible if
   both are expressed as a percentage of the same reference box, since a
   px value and a stretched-SVG percentage drift apart at different card
   sizes the same way the outer radius mismatch above did. No fill
   anywhere in this chrome bar — same as the outer frame/tab, it's pure
   white line art (SVG stroke-dashoffset draw-in), never a solid shape
   with a background. */
.proj-feat-titlebar{position:absolute;top:2%;left:0;right:0;height:9%;z-index:4;display:flex;align-items:center;gap:8px;padding:0 10px}
.proj-feat-dots{display:block;width:48px;height:12px;fill:none;stroke:#fff;stroke-width:1.2;shape-rendering:geometricprecision}
/* real circumference (2×π×5≈31.42, rounded up to 32), not pathLength="100"
   — same seam-precision reasoning as .proj-feat-border-outer above */
.proj-feat-dots circle{stroke-dasharray:32;stroke-dashoffset:32;transition:stroke-dashoffset .35s ease}
.proj-feat.stage-mock .proj-feat-dots circle,.proj-feat.stage-video .proj-feat-dots circle{stroke-dashoffset:0}
.proj-feat.stage-mock .proj-feat-dots circle:nth-child(1){transition-delay:.45s}
.proj-feat.stage-mock .proj-feat-dots circle:nth-child(2){transition-delay:.58s}
.proj-feat.stage-mock .proj-feat-dots circle:nth-child(3){transition-delay:.71s}
/* natural 110:30 aspect ratio, no squish, so the tab's rounded corners
   don't distort. align-self:flex-end, not the titlebar row's own center:
   the tab shape's own path is open at the bottom (no bottom edge drawn —
   see the <path> in the template) so it's meant to sit flush against
   .proj-feat-border-inner below with no seam — centering it in the
   (taller) titlebar row left empty space under it instead, which is what
   actually looked like a gap between the tab and the inner frame line;
   the two containers were already touching exactly, only the visible tab
   graphic wasn't reaching down to its own container's bottom edge. */
.proj-feat-tab-wrap{position:relative;align-self:flex-end;margin-left:2px;width:110px;height:30px}
.proj-feat-tab-text{position:absolute;top:6px;left:0;right:0;text-align:center;font-size:11px;font-weight:600;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 10px;opacity:0;transition:opacity .3s ease}
.proj-feat.stage-mock .proj-feat-tab-text,.proj-feat.stage-video .proj-feat-tab-text{opacity:1;transition-delay:1.05s}
.proj-feat-tab-shape{position:absolute;inset:0;width:100%;height:100%;fill:none;stroke:#fff;stroke-width:1.2;shape-rendering:geometricprecision}
/* real path length (≈162.46, rounded up to 165), not pathLength="100" —
   this path is open (no seam to begin with) but kept consistent with the
   other 2 shapes above rather than leaving it as the one exception */
.proj-feat-tab-shape path{stroke-dasharray:165;stroke-dashoffset:165;transition:stroke-dashoffset .45s ease .85s}
.proj-feat.stage-mock .proj-feat-tab-shape path,.proj-feat.stage-video .proj-feat-tab-shape path{stroke-dashoffset:0}

/* inset:11% 1.5% 2% below the titlebar (see above) and a small margin on
   the other 3 sides — a real browser's page content sits inside the
   chrome bar, never underneath/behind it. .proj-feat-logo/-video and
   .proj-feat-border-inner (below) all share this exact inset AND this
   exact border-radius, so the drawn line, the video's own rounded
   corners and the logo's own rounded corners are always the same curve —
   no separate SVG to drift out of sync with any of them. */
.proj-feat-logo{position:absolute;inset:11% 1.5% 2%;z-index:3;display:flex;align-items:center;justify-content:center;padding:14%;border-radius:2.0761% / 1.9022%;opacity:0;transform:scale(.7);transition:opacity .4s ease .7s,transform .5s cubic-bezier(.22,1,.36,1) .7s;pointer-events:none}
.proj-feat.stage-mock .proj-feat-logo{opacity:1;transform:scale(1)}
/* transition-delay:0s override: the fade-OUT to video should react right
   away when stage-video is added, not inherit the .7s entrance delay
   above (which is only there so the fade-IN waits for the burst) */
.proj-feat.stage-video .proj-feat-logo{opacity:0;transform:scale(.85);transition-delay:0s}
.proj-feat-logo img{max-width:60%;max-height:50%;object-fit:contain}
.proj-feat-logo span{font-weight:700;font-size:15px;color:#fff;text-align:center}

/* top/left/width/height, not "inset:11% 1.5% 2%" like .proj-feat-logo
   above: <video> is a replaced element (same category as <img>/<svg>),
   and for an absolutely positioned replaced element with width left as
   auto, CSS resolves it from the video's OWN intrinsic size rather than
   stretching to fill the space between left/right — a <div> like
   .proj-feat-logo doesn't have this rule and fills the inset correctly,
   which is why comparing the two earlier looked fine but wasn't actually
   the same case. Explicit width/height here forces the fill instead.
   overflow:hidden + the border-radius (not on .proj-feat-video itself
   anymore) is what actually keeps the video inside the inner frame: the
   video's own transform:scale(1.05) "hidden" state is bigger than this
   box by design (a small zoom-settle on reveal), and since object-fit:
   cover already fills the box edge to edge with no margin to grow into,
   that 5% overshoot has to go SOMEWHERE — without a clipping parent it
   spilled out past the inner border and into the outer frame on
   mouseleave, which is what "vượt khỏi lòng bên trong" was. */
.proj-feat-video-wrap{position:absolute;top:11%;left:1.5%;width:calc(100% - 3%);height:calc(100% - 13%);z-index:3;overflow:hidden;border-radius:2.0761% / 1.9022%}
.proj-feat-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;opacity:0;transform:scale(1.05);transition:opacity .5s ease,transform .7s cubic-bezier(.22,1,.36,1)}
.proj-feat.stage-video .proj-feat-video{opacity:1;transform:scale(1)}

/* A real CSS border + border-radius, not an SVG rect: rounding it to
   match .proj-feat-logo/-video above is then automatic (same box, same
   border-radius value — nothing to keep in sync by hand), and there's no
   viewBox/stroke-dasharray geometry left to mis-render the way the SVG
   version of this element did twice in a row. "Drawn in" via an animated
   clip-path instead of stroke-dashoffset: inset(0 100% 100% 0) collapses
   the visible area to a single point at the top-left corner (right and
   bottom both pulled in 100%), inset(0 0 0 0) is the full box — since
   this element has no fill, only its border, whatever the clip currently
   covers is the only part of the border you see, so it reads as the
   frame drawing itself in from that corner exactly like the burst/outer
   frame do, just via a filled-rectangle reveal instead of a 1D stroke. */
.proj-feat-border-inner{position:absolute;inset:11% 1.5% 2%;z-index:4;border:1px solid #fff;border-radius:2.0761% / 1.9022%;clip-path:inset(0 100% 100% 0);transition:clip-path .6s ease .1s;pointer-events:none}
.proj-feat.stage-mock .proj-feat-border-inner,.proj-feat.stage-video .proj-feat-border-inner{clip-path:inset(0 0 0 0)}

.proj-feat-meta{display:flex;justify-content:space-between;align-items:baseline;gap:16px;margin-top:20px}
.proj-feat-title{display:inline-block;font-weight:700;font-size:clamp(18px,1.8vw,24px)}
.proj-feat-cat{font-size:13px;color:var(--mut)}
@media (prefers-reduced-motion:reduce){.proj-feat-img,.proj-feat-burst-fill,.proj-feat-burst-circle,.proj-feat-border-outer,.proj-feat-border-inner,.proj-feat-dots circle,.proj-feat-tab-text,.proj-feat-tab-shape path,.proj-feat-logo,.proj-feat-video{transition:none}}
.band{background:var(--bg);color:#fff;padding:88px 0;margin-bottom:96px}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));gap:clamp(24px,4vw,56px);margin-top:44px}
.stats p{font-size:14px;color:#b9c4d6;margin:10px 0 0}
.stat .big{color:var(--ink)}
.stat p{margin-top:10px}
.stat-item{position:relative;border-radius:var(--radius-md);padding:16px;margin:-16px;transition:background .3s ease}
.stat-item:hover{background:rgb(255,255,255,.05)}
.stat-bar{display:block;height:3px;width:0;margin-top:16px;background:linear-gradient(90deg,var(--blue),var(--purple));transition:width 1.4s cubic-bezier(.22,1,.36,1)}
.stat-item.counted .stat-bar{width:70%}
@keyframes statpop{0%{transform:scale(1)}45%{transform:scale(1.14)}100%{transform:scale(1)}}
.stat-item.counted .big{animation:statpop .5s cubic-bezier(.22,1,.36,1)}

/* Fiverr credential card — deliberately rounded/badge-like, unlike the
   rest of the site's sharp rectangular language, because it's meant to
   read as a distinct, embedded third-party rating widget. Green is
   Fiverr's own brand color, scoped only to this component. */
.fvr-card{--fvr-green:#1dbf73;--fvr-green-bg:#e9f9f0;position:relative;margin-top:32px;background:#fff;border:1px solid var(--line-lt);border-radius:20px;box-shadow:0 20px 50px rgb(5,27,63,.1);padding:clamp(24px,3vw,36px) clamp(20px,3.5vw,40px)}
.fvr-top{display:flex;align-items:center;gap:clamp(18px,3vw,32px);flex-wrap:wrap}
.fvr-brand-badge{position:relative;width:88px;height:88px;flex:0 0 auto;border-radius:50%;background:var(--fvr-green-bg);display:flex;align-items:center;justify-content:center}
.fvr-logo{width:56px;height:auto}
.fvr-check{position:absolute;right:0;bottom:0;width:28px;height:28px;border-radius:50%;background:var(--fvr-green);border:3px solid #fff;display:flex;align-items:center;justify-content:center;color:#fff}
.fvr-check svg{width:13px;height:13px}
.fvr-divider{width:1px;align-self:stretch;background:var(--line-lt)}
.fvr-score{flex:0 0 auto;text-align:center}
.fvr-score-num{font-weight:800;font-size:clamp(38px,4vw,50px);line-height:1;color:var(--ink-lt)}
.fvr-score-label{font-size:13px;color:var(--mut-lt);margin-top:2px}
.fvr-pill{display:inline-flex;align-items:center;gap:6px;margin-top:12px;padding:6px 14px;border-radius:999px;background:var(--fvr-green-bg);color:var(--fvr-green);font-size:13px;font-weight:700}
.fvr-pill svg{width:13px;height:13px}
.fvr-detail{flex:1;min-width:240px}
.fvr-stars{display:flex;gap:4px;color:var(--gold);margin-bottom:10px}
.fvr-stars svg{width:20px;height:20px}
.fvr-detail p{margin:0;font-size:clamp(15px,1.3vw,17px);line-height:1.6;color:var(--body-lt);max-width:48ch}
.fvr-detail b{color:var(--fvr-green)}
.fvr-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,180px),1fr));margin-top:28px;padding-top:26px;border-top:1px solid var(--line-lt)}
.fvr-stat{display:flex;align-items:center;gap:14px;padding:4px clamp(10px,2vw,20px);border-left:1px solid var(--line-lt)}
.fvr-stat:first-child{border-left:none;padding-left:0}
.fvr-ic{flex:0 0 auto;width:38px;height:38px;border-radius:50%;background:var(--fvr-green-bg);color:var(--fvr-green);display:flex;align-items:center;justify-content:center}
.fvr-ic svg{width:18px;height:18px}
.fvr-stat strong{display:block;font-size:14px;color:var(--ink-lt)}
.fvr-stat span{font-size:12px;color:var(--mut-lt)}
@media (max-width:560px){.fvr-stat{border-left:none;padding-left:0}}

/* trust mosaic — free-form parallax boxes, "Why work with me" */
.trust-grid{list-style:none;margin:40px 0 0;padding:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));gap:22px}
.trust-card{background:var(--soft);border-radius:var(--radius-md);padding:30px 26px;transform:translateY(var(--py,0)) rotate(var(--r,0deg));transition:box-shadow .3s ease;will-change:transform}
.trust-card:hover{box-shadow:0 20px 44px rgb(5,27,63,.14)}
.trust-card:nth-child(2n){--r:1.4deg}
.trust-card:nth-child(3n){--r:-1.1deg}
.trust-card:nth-child(4n){--r:.8deg}
.trust-num{display:block;font-weight:800;font-size:34px;color:var(--blue);margin-bottom:16px}
.trust-card p{margin:0;font-size:15px;line-height:1.6;color:var(--body)}
@media (prefers-reduced-motion:reduce){.trust-card{transform:rotate(var(--r,0deg)) !important}}

/* service tiles — ghost-number cards, "What I do" */
.svc-tile{position:relative;overflow:hidden;display:block;background:var(--soft);border-radius:var(--radius-md);padding:32px 26px 30px;min-height:200px}
.svc-tile-num{position:absolute;top:-10px;right:6px;font-weight:800;font-size:76px;line-height:1;color:rgb(255,255,255,.06);pointer-events:none}
.svc-tile h3{position:relative;font-size:19px;margin:0 0 10px;line-height:1.25}
.svc-tile p{position:relative;font-size:14px;line-height:1.6;color:var(--body);margin:0}
.svc-tile-arrow{position:absolute;bottom:22px;right:24px;font-size:20px;font-weight:800;color:var(--blue);opacity:0;transform:translateX(-8px);transition:all .3s cubic-bezier(.22,1,.36,1)}
.svc-tile:hover{background:rgb(255,255,255,.06)}
.svc-tile:hover .svc-tile-arrow{opacity:1;transform:translateX(0)}

/* global network CTA band — canvas particle animation on navy */
.band-net{position:relative;overflow:hidden;background:var(--bg);color:#fff;padding:88px 0;margin-bottom:96px}
.band-net>.wrap{position:relative;z-index:1}
.band-net .lead{color:#b9c4d6;margin:16px 0 28px}
.net-bg{position:absolute;inset:0;z-index:0}

/* testimonials — drag-to-scroll, loosely scattered cards */
/* Full-bleed now (its parent <section> dropped the .wrap max-width, only
   .sec-head above keeps it — see front-page.php), so the side padding
   here does what .wrap's own padding used to: a matching gutter before
   the first/after the last card, instead of them touching the viewport
   edge, while the carousel itself can still scroll edge to edge. */
.rev-carousel{display:flex;gap:24px;overflow-x:auto;scroll-snap-type:x proximity;padding:10px clamp(16px,2.5vw,40px) 28px;cursor:grab;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.rev-carousel::-webkit-scrollbar{display:none}
.rev-carousel.dragging{cursor:grabbing;scroll-snap-type:none}
.rev-carousel .quote{flex:0 0 min(85vw,380px);scroll-snap-align:start;transform:rotate(var(--r,0deg));transition:transform .3s ease}
.rev-carousel .quote:nth-child(2n){--r:1deg}
.rev-carousel .quote:nth-child(3n){--r:-1deg}
.rev-carousel .quote:hover{transform:rotate(0deg) translateY(-4px)}
.rev{opacity:0;transform:translateY(48px);transition:opacity .9s cubic-bezier(.22,1,.36,1),transform .9s cubic-bezier(.22,1,.36,1)}
.rev.on{opacity:1;transform:translateY(0)}
.faq-list{display:flex;flex-direction:column;gap:14px;margin-top:8px;max-width:760px}
.faq-item{background:var(--soft);border-radius:var(--radius-md);padding:22px 26px;transition:box-shadow .3s ease}
.faq-item[open]{box-shadow:var(--shadow-card-sm)}
.faq-item summary{display:flex;justify-content:space-between;align-items:center;gap:16px;cursor:pointer;font-weight:700;font-size:17px;list-style:none}
.faq-item summary::-webkit-details-marker{display:none}
.faq-ic{flex:0 0 auto;width:28px;height:28px;border-radius:50%;background:#fff;font-size:19px;font-weight:400;line-height:1;color:var(--blue);display:flex;align-items:center;justify-content:center;transition:transform .3s ease}
.faq-item[open] .faq-ic{transform:rotate(45deg)}
.faq-a{font-size:15px;line-height:1.6;color:var(--body);margin-top:14px;max-width:64ch}
.faq-a a{color:var(--blue-d);text-decoration:underline}

/* work page */
.fstats{display:flex;gap:12px;flex-wrap:wrap}
.fstats div{background:var(--soft);border-radius:var(--radius-sm);padding:14px 20px}
.fstats b{display:block;font-weight:800;font-size:24px}
.fstats span{font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--mut);margin-top:2px}
.feat-im{aspect-ratio:4/3;overflow:hidden;border-radius:var(--radius-lg);box-shadow:0 16px 40px rgb(5,27,63,.14)}
.pcard{display:block;background:var(--soft);border-radius:var(--radius-md);padding:20px;transition:box-shadow .3s ease}
.pcard:hover{box-shadow:var(--shadow-card-sm)}
.pcard .pimg{aspect-ratio:3/2;border-radius:var(--radius-sm);overflow:hidden;transition:transform .6s cubic-bezier(.22,1,.36,1)}
.pcard:hover .pimg{transform:scale(1.02)}
.pline{display:flex;justify-content:space-between;align-items:baseline;margin-top:18px}
.pt{font-weight:800;font-size:20px;text-transform:uppercase;transition:transform .45s cubic-bezier(.22,1,.36,1);display:inline-block}
.pcard:hover .pt{transform:translateX(12px);color:var(--blue-d)}
.pcard b{color:var(--blue);font-weight:800}
.pcard em{display:block;font-style:normal;font-size:13px;color:var(--mut);margin-top:4px}

/* about */
.portrait{aspect-ratio:4/5;overflow:hidden;border-radius:var(--radius-lg);box-shadow:0 16px 40px rgb(5,27,63,.14)}

/* services */
.svcrow{display:grid;grid-template-columns:80px 1.2fr 1.6fr auto;gap:clamp(16px,3vw,48px);align-items:baseline;padding:36px 0;border-bottom:1px solid var(--line);scroll-margin-top:96px;color:var(--ink)}
.svcrow:hover{background:rgb(255,255,255,.06);color:var(--ink)}
.svc-name{font-weight:800;font-size:clamp(22px,2.4vw,36px);text-transform:uppercase;line-height:1.05}
.svc-long{font-size:15px;line-height:1.6;color:var(--body)}
.svcrow b{color:var(--blue);font-weight:800;font-size:22px}
@media (max-width:860px){.svcrow{grid-template-columns:48px 1fr}.svc-long{grid-column:1/-1}.svcrow b{display:none}}
.plan{display:flex;flex-direction:column;gap:14px;padding:32px 28px}
.plan h3{font-size:22px;text-transform:uppercase;margin:0}
.price{font-weight:800;font-size:15px;color:var(--blue)}
.plan p{flex:1}
.btn-navy{display:inline-flex;align-items:center;width:max-content;height:44px;padding:0 20px;background:var(--blue);border-radius:var(--radius-sm);color:#fff;font-size:13px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}
.btn-navy:hover{background:var(--blue-d);color:#fff}

/* blog */
.bfeat{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:clamp(28px,4vw,72px);align-items:center;padding:56px 0;color:var(--ink)}
.bfeat h2{font-weight:800;font-size:clamp(30px,3.4vw,54px);line-height:1;text-transform:uppercase;margin:18px 0 16px;text-wrap:pretty}
.bfeat p{font-size:15px;line-height:1.6;max-width:50ch;margin:0;color:var(--body)}
.bfeat-im{aspect-ratio:3/2;overflow:hidden;border-radius:var(--radius-lg);box-shadow:0 16px 40px rgb(5,27,63,.14)}
.bmeta{display:flex;gap:14px;font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--mut)}
.bmeta .hl{display:inline-block;background:#e6f4fb;padding:4px 12px;border-radius:var(--radius-pill);color:#00688f}
.bcard{display:block;background:var(--soft);border-radius:var(--radius-md);padding:20px;color:var(--ink);transition:box-shadow .3s ease}
.bcard:hover{box-shadow:var(--shadow-card-sm)}
.bcard .pimg{aspect-ratio:3/2;border-radius:var(--radius-sm);overflow:hidden;margin-bottom:22px}
.bt{font-weight:800;font-size:20px;line-height:1.15;text-transform:uppercase;margin:10px 0 0;transition:transform .45s cubic-bezier(.22,1,.36,1)}
.bcard:hover .bt{transform:translateX(10px);color:var(--blue-d)}
.sec-last{padding-bottom:140px}

/* contact */
.cinfo{display:flex;flex-direction:column;gap:20px;border-top:1px solid var(--line);padding-top:32px;margin-top:40px}
.cinfo .ft-h{color:var(--mut)}
.cbig{display:block;font-weight:800;font-size:clamp(20px,2vw,28px);margin-top:4px}
.cbig b{color:var(--blue)}
.cinfo p{font-size:15px;color:var(--body);margin:4px 0 0}
.cform{background:var(--soft);border-radius:var(--radius-lg);padding:clamp(24px,3vw,44px)}
.cform .frow{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:22px}
.cform label{display:flex;flex-direction:column;gap:8px;font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase}
.cform input,.cform select,.cform textarea{width:100%;box-sizing:border-box;min-width:0;height:48px;padding:0 14px;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg);font-size:15px;font-family:inherit;color:var(--ink)}
.cform input::placeholder,.cform textarea::placeholder{color:var(--mut)}
.cform textarea{height:auto;padding:12px 14px;resize:vertical}
.cform input:focus,.cform select:focus,.cform textarea:focus{outline:2px solid var(--blue);outline-offset:0;border-color:var(--blue)}
.cform button,.cform input[type="submit"]{display:flex;align-items:center;height:54px;padding:0 28px;width:max-content;background:var(--blue);border-radius:var(--radius-sm);color:#fff;border:none;cursor:pointer;font-size:15px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;font-family:inherit}
.cform button:hover,.cform input[type="submit"]:hover{background:var(--blue-d)}
/* Contact Form 7 mapping onto .cform */
.cform .wpcf7-form{display:flex;flex-direction:column;gap:22px}
.cform .wpcf7-form-control-wrap.your-hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}
.cform .wpcf7-response-output{margin:0;padding:12px 16px;font-size:14px;border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--bg)}
.cform .wpcf7-form.invalid .wpcf7-response-output{border-color:var(--red);color:var(--red)}
.cform .wpcf7-form.sent .wpcf7-response-output{border-color:var(--blue);color:var(--blue-d)}
.cform .wpcf7-not-valid-tip{display:block;margin-top:6px;font-size:12px;font-weight:600;text-transform:none;letter-spacing:0;color:var(--red)}

/* industries */
.indcard{scroll-margin-top:96px;background:var(--soft);border-radius:var(--radius-md);padding:32px;display:flex;gap:24px;align-items:flex-start;transition:box-shadow .3s ease}
.indcard:hover{box-shadow:var(--shadow-card-sm)}
.indcard h2{font-weight:700;font-size:22px;margin:0 0 10px}
.indcard p{font-size:14px;line-height:1.6;margin:0 0 16px;color:var(--body)}

/* footer */
.ft{background:var(--navy);color:#b9c4d6}
.ft .wrap{padding-top:72px;padding-bottom:40px}
.ft a{color:#b9c4d6}
.ft a:hover{color:#4db4e2}
.ft-cta{display:flex;justify-content:space-between;align-items:center;gap:28px;flex-wrap:wrap;padding:clamp(28px,4vw,48px);margin-bottom:48px;background:rgb(255,255,255,.04);border-radius:var(--radius-lg)}
.ft-cta h2{font-weight:700;font-size:clamp(26px,3.4vw,44px);line-height:1.15;color:#fff;margin:0;text-wrap:pretty}
.ft-cta a{display:inline-flex;align-items:center;gap:10px;height:56px;padding:0 30px;background:var(--blue);border-radius:var(--radius-sm);color:#fff !important;font-size:15px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}
.ft-cta a:hover{background:var(--blue-d)}
.ft-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));gap:clamp(24px,4vw,64px);padding-bottom:48px}
.ft-grid p{font-size:14px;line-height:1.6;max-width:34ch;margin:16px 0 0}
.ft-mail{display:inline-block;margin-top:20px;font-weight:600;font-size:17px;color:#fff !important;border-bottom:2px solid var(--blue);padding-bottom:3px}
.ft-col{display:flex;flex-direction:column;gap:12px;font-size:14px}
.ft-h{font-size:12px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:#7e8ca6;margin-bottom:4px;display:block}
.ft-bot{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;padding-top:24px;border-top:1px solid rgb(255,255,255,.12);font-size:13px;color:#7e8ca6}
.ft-bot .av{color:#4db4e2}
