@font-face {
  font-family: 'SonySketchGO';
  src: url('../fonts/SonySketchGO.0b6e00d0.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
}

/* ===== Reset & base ===== */
* { margin: 0; padding: 0; box-sizing: border-box; }
html {
  scroll-behavior: smooth;
  /* Scroll-snap chain: hero -> tile-row (danh mục sản phẩm) -> dark-banner 1 -> dark-banner 2.
     Each of these is exactly one full screen (100vh/100dvh), so one mouse-wheel drag
     jumps to the next/previous block instead of scrolling freely mid-block. */
  scroll-snap-type: y mandatory;
  /* Safety net: absolutely-positioned elements (nav dropdowns, decorative
     logo rings, etc.) can extend past the right edge while still hidden
     (opacity/visibility, not display:none), which otherwise adds a
     horizontal scrollbar to the whole page even though nothing is
     actually visible out there. */
  overflow-x: hidden;
}
body {
  font-family: 'Be Vietnam Pro', 'Inter', system-ui, sans-serif;
  color: #2b2f36;
  line-height: 1.6;
  background: var(--gray-light);
}
img { max-width: 100%; display: block; }
a { text-decoration: none; color: inherit; }
ul { list-style: none; }

:root {
  --dark: #0d1b30;
  --dark-2: #0a1424;
  --accent: #13cc96;
  --accent-dark: #0fa378;
  --purple: #6a5cf5;
  --purple-dark: #5647d6;
  --gray-light: #f4f5f7;
  --gray-border: #e3e5e9;
  --text-muted: #6b7280;
  --radius: 10px;
  --transition: .25s ease;
  --header-overlap: 129px;
}

/* ===== Scrollbar ===== */
/* Firefox: no gradient support on scrollbar thumbs, so --accent stands in
   as a flat approximation of the gradient below. */
html {
  scrollbar-width: thin;
  scrollbar-color: #005072 var(--gray-light);
}
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background: var(--gray-light);
}
::-webkit-scrollbar-thumb {
  /* A gradient here tested correctly in plain Chromium, but GPU-accelerated
     scrollbar compositing on Windows (confirmed in both Edge and Cốc Cốc)
     flattens it to a solid first-stop color anyway — a rendering-path
     limitation outside this stylesheet's control, not a CSS bug. Flat
     --accent instead, which is reliable everywhere. */
  background: #005072;
  border-radius: 0;
}
::-webkit-scrollbar-thumb:hover {
  background: #003a52;
}

.container {
  max-width: 1350px;
  margin: 0 auto;
  padding: 0 20px;
}

/* ===== Top bar ===== */
.topbar {
  background: transparent;
  color: #cfd3da;
  font-size: clamp(12px, 1vw, 15px);
  position: relative;
  z-index: 101;
  font-family: 'Plus Jakarta Sans', 'Be Vietnam Pro', sans-serif; /* trial: thin-stroke header font, see .header below */
  font-weight: 300;
}
.topbar-inner {
  display: flex;
  justify-content: flex-end;
  /* Bottom-anchored instead of centered, so the icon row sits closer to
     the nav row right below it in .header — same 52px topbar height,
     just where the content sits within it. */
  align-items: flex-end;
  height: 52px;
}
.topbar-right {
  display: flex;
  align-items: center;
  gap: 18px;
}
.topbar-social {
  display: flex;
  align-items: center;
  gap: 8px;
}
.topbar-social-ico {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0; /* inside a flex row with a capped max-width — without this the fixed-size circle gets squeezed into an oval when content is close to that cap */
  border-radius: 50%;
  background: rgba(255,255,255,.16);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  transition: background var(--transition);
}
.topbar-social-ico:hover { background: var(--accent); }
.topbar-social-ico--fb:hover { background: #1877f2; }
.topbar-social-ico--yt:hover { background: #ff0000; }
.topbar-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 13px;
  flex-shrink: 0;
  border-radius: 8px;
  background: rgba(255,255,255,.16);
  color: #fff;
  font-size: 14px;
  font-weight: 300;
  white-space: nowrap;
  transition: background var(--transition), transform var(--transition);
}
.topbar-pill:hover {
  transform: translateY(-1px);
}
.topbar-pill--zalo:hover { background: #0068ff; }
.topbar-pill--wa:hover { background: #25d366; }
/* Facebook/YouTube/Zalo/WhatsApp collapse behind a "LIÊN HỆ VỚI CHÚNG
   TÔI" toggle instead of always sitting in the topbar — click it to pull
   the icon row out (same width-transition trick as .topbar-search below),
   with a "..." circle at the end of that row linking to the standalone
   contact page. */
.topbar-contact-group {
  display: flex;
  align-items: center;
}
.topbar-contact-toggle {
  all: unset;
  cursor: pointer;
  color: #fff;
  font-size: 13px;
  font-weight: 300;
  letter-spacing: .02em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: opacity var(--transition);
  /* all:unset resets line-height to the button's own UA-default metrics,
     which sit a bit higher than the "VI" text next to it even with
     align-items:center on the shared flex row — nudge down to match. */
  position: relative;
  top: 2px;
}
.topbar-contact-toggle:hover { opacity: .8; }
.topbar-contact-group.open .topbar-contact-toggle {
  /* The icon row replaces it once expanded, instead of both showing at
     once side by side. */
  display: none;
}
.topbar-contact-icons {
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-width .35s ease, opacity .25s ease, margin-left .35s ease;
}
.topbar-contact-group.open .topbar-contact-icons {
  max-width: 400px; /* comfortably above the ~324px the fb/yt/zalo/wa/more row actually needs, now that flex-shrink:0 stops them from fighting over the cap */
  opacity: 1;
  margin-left: 12px;
}
.topbar-contact-more {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .16);
  color: #fff;
  transition: background var(--transition);
}
.topbar-contact-more:hover { background: var(--accent); }
.topbar-lang {
  /* Stays visible as a placeholder for when English content is ready —
     see js/i18n.js: clicking it does nothing for now (English mode is
     temporarily disabled, always Vietnamese on load regardless of any
     previously-stored language preference). */
  /* Without this, the two inline <a> links can wrap onto separate lines —
     a flex item with inline content sizes to a "min-content" width that
     assumes internal wrapping is fine, so it can end up narrower than
     "VI" + "EN" together even though the flex row has plenty of room. */
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 5px;
  cursor: pointer;
}
.topbar-lang::before {
  /* Earth icon — user-supplied image (images/icon_ball_transparent.png,
     background removed from the original icon_ball.jpg), used as a mask
     so it still recolors via background-color like the rest of the
     topbar icons instead of staying a flat raster color. */
  content: '';
  width: 1.4em; /* scales with the "VI"/"EN" text next to it instead of a fixed px size */
  height: 1.4em;
  flex-shrink: 0;
  background-color: #cfd3da;
  -webkit-mask: url('../images/icon_ball_transparent.378347b1.png') center / contain no-repeat;
  mask: url('../images/icon_ball_transparent.378347b1.png') center / contain no-repeat;
}
.topbar-lang .lang {
  padding: 0;
  opacity: .6;
  display: none; /* only the active language shows, next to the globe icon — clicking it switches to the other one */
}
.topbar-lang .lang.active { display: inline; opacity: 1; font-weight: 600; color: var(--accent); }
/* Collapsed to just its icon by default, same as the mobile version —
   tapping it reveals the input by growing width in normal flex flow
   (not an absolute overlay like mobile's .open below), so since
   .topbar-inner is right-anchored (justify-content:flex-end), the
   growing search box pushes the icons before it further left instead of
   covering them. */
.topbar-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px;
  border-left: none;
  border-radius: 8px;
  color: #cfd3da;
  cursor: pointer;
  transition: background var(--transition), padding var(--transition), border-color var(--transition);
}
.topbar-search:hover {
  background: rgba(255,255,255,.25);
}
.topbar-search.open {
  padding: 8px 10px 8px 14px;
  border: 1px solid rgba(255, 255, 255, .6);
  cursor: default;
}
.search-highlight {
  animation: searchFlash 1.8s ease;
  border-radius: 4px;
}
@keyframes searchFlash {
  0%, 100% { background: transparent; }
  15%, 60% { background: rgba(0, 217, 163, .35); }
}
.topbar-search input {
  background: transparent;
  border: none;
  outline: none;
  color: #fff;
  font-size: 14.5px;
  width: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
  transition: width .25s ease, opacity .2s ease;
}
.topbar-search.open input {
  width: 260px; /* fits the full "Tìm kiếm giải pháp, tin tức & sự kiện" placeholder */
  opacity: 1;
  pointer-events: auto;
}
.topbar-search input::placeholder { color: rgba(255, 255, 255, .6); }

/* ===== Header ===== */
.header {
  background: transparent;
  border-bottom: 1px solid transparent;
  position: sticky;
  top: 0;
  z-index: 100;
  transition: background .4s ease, border-color .4s ease, box-shadow .4s ease;
  font-family: 'Plus Jakarta Sans', 'Be Vietnam Pro', sans-serif; /* trial — see matching note on .topbar */
}
.header.is-scrolled {
  /* Matches dfe-tech.com/vi's header: #0B0E15 at 40% opacity plus a very
     heavy backdrop blur, rather than a plain translucent black. */
  background: rgba(11, 14, 21, .4);
  backdrop-filter: blur(100px);
  -webkit-backdrop-filter: blur(100px);
}
/* Slow, gentle diagonal light wave while hovering, everywhere the header
   is NOT over the first hero block (over-hero-intro is transparent
   there, with no dark backdrop for a light wave to read against). Only
   declared inside :hover so it starts fresh each time the cursor enters
   and stops the moment it leaves. */
.header:not(.over-hero-intro):hover::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 38%, rgba(255, 255, 255, .13) 50%, transparent 62%);
  background-size: 260% 100%;
  animation: header-shine 3.6s ease-in-out infinite;
}
@keyframes header-shine {
  0%, 10% { background-position: -160% 0; }
  55% { background-position: 160% 0; }
  100% { background-position: 160% 0; }
}
.header-inner {
  display: flex;
  align-items: center;
  gap: 24px;
  height: 86px;
}
.nav {
  margin-left: auto; /* pushes the menu to the far right of the header, next to the logo on the left — previously the (now-removed) CTA button did this job */
}
@media (min-width: 901px) {
  /* .container is centered with a 1350px cap, which leaves a gap before
     the true right edge on wide screens. The topbar/header rows are
     right-aligned (flex-end / margin-left:auto on .nav), so let their
     own containers stretch full-width and sit flush against the real
     edge instead — mirroring the logo's fixed 40px left anchor. */
  .topbar .container,
  .header .container {
    max-width: 100%;
    padding-right: 60px;
  }
}
.logo {
  /* Anchored to .header itself (position:sticky, a valid containing
     block) at a fixed distance from the true viewport edge, instead of
     flowing inside .container's own padding — .container is centered
     with a max-width, so an in-flow logo would drift inward on wide
     screens instead of staying a constant distance from the edge. Taken
     out of the header-inner flex flow entirely; .nav's own
     margin-left:auto still pushes it to the far right regardless. */
  position: absolute;
  left: 40px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  /* top/transform both get concrete, interpolatable values in every state
     (see .header.over-hero-intro .logo below) instead of switching to
     top:auto so the move between them can actually animate. */
  transition: top .4s ease, transform .4s ease;
}
.logo-placeholder {
  width: 46px;
  height: 46px;
  border: 1.5px dashed #b9bdc6;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  color: #9aa0ab;
  text-align: center;
  flex-shrink: 0;
  background: #fafafa;
}
.logo-img-wrap {
  position: relative;
  display: inline-block;
  line-height: 0;
  flex-shrink: 0;
  perspective: 400px;
  transition: transform .4s ease;
}
/* While the new intro hero is on screen, blow the logo up to the combined
   height of the topbar + header bars (var(--header-overlap), ~2x the
   normal 64px) — toggled by the IntersectionObserver in script.js.
   transform:scale() on the WRAP (not a height change on .logo-img itself)
   so the decorative rings — hand-tuned to the 64px image — scale up
   together with it instead of drifting out of alignment. Growing from the
   bottom-left corner (not the center) means it grows purely UPWARD from
   the header's own bottom edge — paired with .logo's align-self:flex-end
   below so that edge lines up with the header's actual bottom, the scaled
   height then lands exactly on the topbar's own top edge with no overshoot
   past either bar, instead of overflowing both above the topbar and below
   the header the way center-growth would. */
.header.over-hero-intro .logo-img-wrap {
  transform: scale(calc(var(--header-overlap) / 64px * 0.85));
  transform-origin: left bottom;
}
.header.over-hero-intro .logo {
  /* left is the same 40px as the normal state now, so only the vertical
     positioning needs overriding. .logo is positioned relative to .header
     itself though, which is only the BOTTOM part of --header-overlap (the
     topbar above it, fixed at 52px, isn't part of this containing block)
     — so centering the scaled logo within the full topbar+header band
     means its top can legitimately go negative, reaching up into the
     topbar's own area. Derivation: the scaled logo is var(--header-overlap)
     * 0.85 tall (see .logo-img-wrap below) and grows upward from a fixed
     bottom edge (transform-origin left bottom, so that edge doesn't move
     when scaling) — centering that visual box within the 139px band and
     converting back to "top, relative to .header's own top" (i.e.
     subtracting the 52px topbar) gives this formula. */
  top: calc(var(--header-overlap, 138px) * 0.925 - 116px);
  transform: translateY(0%);
}
.logo-img {
  position: relative;
  z-index: 1;
  height: 64px;
  width: auto;
  display: block;
}
.logo-text {
  display: flex;
  flex-direction: column;
  gap: 3px;
  line-height: 1.25;
  white-space: nowrap;
  /* Both get overridden by .header.over-hero-intro below (margin-left to
     clear the enlarged logo, transform to re-center against it) — without
     transitioning here too, that move would snap instead of easing in
     alongside the logo's own scale animation. */
  transition: margin-left .4s ease, transform .4s ease;
}
@media (max-width: 1300px) {
  /* The 6-item nav (now with longer labels like "GIẢI PHÁP CAD/CAM") runs
     into the logo wordmark starting around 1280-1290px — common laptop
     width — well above the 760px mobile breakpoint that already hides it.
     Icon-only logo below this width instead of letting them overlap. */
  .logo-text { display: none; }
}
.logo-text-main {
  font-family: 'Michroma', 'Plus Jakarta Sans', sans-serif;
  font-size: 15px;
  font-weight: 400; /* Michroma only ships a single 400 weight */
  color: #fff;
  transition: font-size .4s ease;
}
.logo-text-brand {
  /* em-based so it scales right along with .logo-text-main's own
     font-size (including the enlarged hero-intro state below). */
  font-size: 1.2em;
  /* Michroma has no bold weight — fake it with the browser's synthetic
     bold plus a thin stroke on top, so "EZTEK" still reads heavier than
     "Technology" right next to it. */
  font-weight: 700;
  -webkit-text-stroke: 0.6px #fff;
}
.logo-text-sub {
  /* Smaller than the company name so that line stays the one that pops. */
  font-family: 'Michroma', 'Plus Jakarta Sans', sans-serif;
  font-size: 11px;
  font-weight: 400;
  color: rgba(255, 255, 255, .65);
  letter-spacing: .3px;
  transition: font-size .4s ease;
}
.header.over-hero-intro .logo {
  /* The big logo grows upward from the header's bottom edge (see
     .logo-img-wrap above), so the text needs to bottom-align with it
     instead of the default vertical centering. */
  align-items: flex-end;
}
.header.over-hero-intro .logo-text {
  /* .logo-img-wrap is scaled up with transform, which doesn't affect
     flex layout — the text's flow position is still based on the
     image's un-scaled ~103px width, so without this it sits overlapping
     the visually-enlarged logo. Pushes right by exactly the extra width
     the scaling adds (unscaled-width * (scale-1)) so it clears it. */
  margin-left: calc(103px * (var(--header-overlap, 64px) / 64px * 0.85 - 1));
  /* align-items:flex-end above only lines up the text's bottom edge with
     the logo's bottom edge — correct as a stable anchor, but it then
     reads as glued to the bottom of the much-taller scaled logo instead
     of sitting level with its visual middle. Nudges it back up toward
     that middle. */
  transform: translateY(calc(-0.21 * var(--header-overlap, 64px)));
}
.header.over-hero-intro .logo-text-main {
  /* Scales up by the same factor as .logo-img-wrap so the text stays in
     proportion with the enlarged logo instead of looking tiny next to it. */
  font-size: calc(15px * var(--header-overlap, 64px) / 64px * 0.85);
}
.header.over-hero-intro .logo-text-sub {
  font-size: calc(11px * var(--header-overlap, 64px) / 64px * 0.85);
}
/* Decorative 3D ring that orbits behind the icon's own circle — the
   artwork's ring can't be isolated and rotated on its own (the E/Z
   letters are baked into the same flat PNG, overlapping it), so this is a
   separate CSS-drawn halo sized/positioned to frame that circle instead.
   Two layers: the outer one tumbles in 3D (rotateX tilt + rotateY spin,
   via the wrap's perspective) so it reads as a tilted disc turning in
   space; the inner one spins its own light trail independently, faster,
   so the highlight chases around the rim as the disc tumbles. Sizing is
   hand-tuned to the icon's circle within the current 64px-tall logo
   image — re-check the numbers here if .logo-img's height changes. */
.logo-ring-3d {
  position: absolute;
  transform-style: preserve-3d;
  animation: logo-ring-tumble 7s ease-in-out infinite;
  pointer-events: none;
}
/* Three nested rings at different sizes/speeds/phases (instead of one)
   read as layered 3D orbits rather than a single flat band — same trick
   as Saturn-with-multiple-rings. Sizes are centered on the icon's circle
   within the wrap (see the note above); r1 is innermost/fastest/brightest,
   r3 outermost/slowest/faintest, giving a sense of depth. */
.logo-ring-3d.r1 {
  /* Box sized so the visible thin ring (see .logo-ring's percentage-based
     mask below) lands exactly on the logo artwork's own circle — not the
     box itself, which is slightly larger to account for the mask's inward
     inset. */
  top: 2px;
  left: 26px;
  width: 47px;
  height: 47px;
  animation-duration: 5s;
}
.logo-ring-3d.r2 {
  top: -5px;
  left: 19px;
  width: 62px;
  height: 62px;
  animation-duration: 7s;
  animation-delay: -2.3s;
}
.logo-ring-3d.r3 {
  top: -9px;
  left: 15px;
  width: 70px;
  height: 70px;
  animation-duration: 9s;
  animation-delay: -4.6s;
  animation-direction: reverse;
}
.logo-ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 0deg,
    rgba(230,180,90,.55) 0deg,
    rgba(255,240,205,1) 30deg,
    rgba(230,180,90,.55) 80deg,
    rgba(230,180,90,.55) 200deg,
    rgba(250,220,165,.95) 260deg,
    rgba(230,180,90,.55) 320deg,
    rgba(230,180,90,.55) 360deg);
  box-shadow: 0 0 7px 1px rgba(230,180,90,.45);
  /* Very thin line, soft on both edges — a hairline full-opacity core
     flanked by a short fade in/out, no crisp inner/outer rim. Percentage
     insets (not fixed px) so the ring sits at a consistent fraction of
     each box's own radius regardless of r1/r2/r3's different sizes —
     fixed-px insets ate up a much bigger share of r1's smaller radius,
     making its visible ring shrink far below the box size it was given. */
  -webkit-mask: radial-gradient(closest-side,
    transparent 80%,
    #000 88%,
    #000 92%,
    transparent 100%);
  mask: radial-gradient(closest-side,
    transparent 80%,
    #000 88%,
    #000 92%,
    transparent 100%);
  animation: logo-ring-spin 3s linear infinite;
  pointer-events: none;
}
.r1 .logo-ring { opacity: .95; }
.r2 .logo-ring { opacity: .7; animation-duration: 4s; animation-direction: reverse; }
.r3 .logo-ring { opacity: .5; animation-duration: 5s; }
/* Multi-axis tumble — X, Y and Z all drift on their own offset paths
   instead of one fixed tilt spinning around a single axis, so the disc
   turns in several directions rather than one flat plane of rotation. */
@keyframes logo-ring-tumble {
  0%   { transform: rotateX(55deg) rotateY(0deg)   rotateZ(0deg); }
  25%  { transform: rotateX(30deg) rotateY(90deg)  rotateZ(20deg); }
  50%  { transform: rotateX(65deg) rotateY(180deg) rotateZ(-15deg); }
  75%  { transform: rotateX(40deg) rotateY(270deg) rotateZ(10deg); }
  100% { transform: rotateX(55deg) rotateY(360deg) rotateZ(0deg); }
}
@keyframes logo-ring-spin {
  to { transform: rotate(360deg); }
}
/* Light sweep across the logo: a soft, wide glow that drifts slowly
   left-to-right like a wave, masked to the logo's own PNG so it only
   lights up the actual icon/wordmark pixels instead of a plain
   rectangle. Wide gradient + slow ease-in-out = a gentle roll rather
   than a quick flash. */
.logo-shine {
  position: absolute;
  z-index: 2;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 10%, rgba(255,255,255,.55) 50%, transparent 90%);
  background-size: 300% 100%;
  background-position: -150% 0;
  -webkit-mask-image: url('../images/Eztek3.eb1ad321.png');
  -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-image: url('../images/Eztek3.eb1ad321.png');
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
  animation: logo-shine 6s ease-in-out infinite;
}
@keyframes logo-shine {
  0%, 15% { background-position: -150% 0; }
  65% { background-position: 150% 0; }
  100% { background-position: 150% 0; }
}
.logo-img-footer {
  height: 40px;
  margin-bottom: 14px;
}
.nav ul {
  display: flex;
  gap: 26px;
}
.nav > ul > li {
  position: relative;
}
.nav > ul > li > a {
  position: relative;
  display: inline-block;
  font-weight: 400; /* lighter, to match the thin-stroke Plus Jakarta Sans trial on .header */
  font-size: 16px; /* matches dfe-tech.com/vi's header menu text size */
  text-transform: uppercase;
  letter-spacing: .03em;
  padding: 8px 0;
  color: rgba(255, 255, 255, .7); /* matches dfe-tech.com/vi's header menu text color */
  transition: color .3s ease, transform .3s ease;
}
.nav > ul > li > a:hover {
  color: #fff;
  transform: scale(1.1);
}

/* Simple one-column dropdown under "Cộng đồng" — flat list, no sub-items
   (unlike the two-column .megamenu used for "Giải pháp CAD/CAM" / "Dịch vụ"). */
.has-dropdown { position: relative; }
/* .has-dropdown .dropdown (two classes) is needed, not plain .dropdown
   (one class) — the generic ".nav ul { gap: 26px }" rule is a class +
   element selector, which outranks a single-class selector regardless of
   source order, so a bare ".dropdown { gap: 0 }" loses the cascade. */
.has-dropdown .dropdown {
  position: absolute;
  top: 100%;
  left: -15px;
  min-width: 230px;
  display: flex;
  flex-direction: column;
  gap: 0;
  list-style: none;
  margin: 0;
  background: #fff;
  box-shadow: 0 16px 40px rgba(0,0,0,.16);
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: all var(--transition);
  padding: 8px 0;
}
.has-dropdown:hover .dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.dropdown-item {
  display: block;
  padding: 10px 22px;
  font-size: 16px;
  font-weight: 500;
  color: var(--dark-2);
  white-space: nowrap;
  border-bottom: 1px solid var(--gray-light);
  text-transform: uppercase;
  letter-spacing: .02em;
}
.dropdown-item:hover {
  background: var(--gray-light);
  color: var(--accent);
}
.dropdown-item.dropdown-all {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-weight: 700;
  color: #fff;
  background: var(--accent);
  margin-bottom: 6px;
}
.dropdown-item.dropdown-all:hover {
  background: var(--accent);
  color: #fff;
}

/* Two-column mega menu under "Giải pháp CAD/CAM" — left: brand list,
   right: that brand's sub-items, switched by hovering a left item (see
   the mouseenter wiring in script.js / each page's inline script). */
.has-megamenu { position: relative; }
.megamenu {
  position: absolute;
  top: 100%;
  /* Right-anchored (hangs leftward from the trigger) instead of
     left-anchored — "Giải pháp CAD/CAM" sits well into the nav row, so a
     left-anchored, rightward-growing 570px-wide panel was running off the
     edge of the viewport on ordinary desktop widths. -200px (instead of a
     bare -14px) pulls it roughly centered under the trigger rather than
     hanging almost entirely to its left. */
  right: -228px;
  display: flex;
  align-items: stretch;
  background: #fff;
  box-shadow: 0 16px 40px rgba(0,0,0,.16);
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  transform: translateY(6px);
  transition: all var(--transition);
}
.has-megamenu:hover .megamenu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.megamenu-left {
  width: 230px;
  flex-shrink: 0;
  background: var(--gray-light);
  border-right: 1px solid var(--gray-border);
  padding: 10px 0;
  display: flex;
  flex-direction: column;
}
.megamenu-left-item {
  all: unset;
  box-sizing: border-box;
  cursor: pointer;
  padding: 12px 22px;
  font-size: 16px;
  font-family: inherit;
  font-weight: 500;
  color: var(--dark-2);
  border-left: 3px solid transparent;
  white-space: nowrap;
  /* No text-transform — the reference (dfe-tech.com) keeps "All Solutions"
     in natural title case; only the brand names themselves (GO2CAM,
     CREO PTC...) are typed in caps directly in the HTML. */
}
/* "Dịch vụ" mega menu specifically wants its categories in caps, unlike
   the CAD/CAM Solutions one above — scoped via .megamenu-services so it
   doesn't affect that other menu's left column. */
.megamenu-services .megamenu-left-item {
  text-transform: uppercase;
  letter-spacing: .02em;
}
.megamenu-left-item:hover,
.megamenu-left-item.active {
  background: #fff;
  border-left-color: var(--accent);
  color: var(--accent);
  font-weight: 600;
}
.megamenu-left-item.megamenu-all {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-weight: 700;
  color: #fff;
  background: var(--accent);
  border-bottom: none;
  margin-bottom: 6px;
}
.megamenu-left-item.megamenu-all:hover {
  background: var(--accent);
  color: #fff;
  border-left-color: transparent;
}
.megamenu-right {
  width: 340px;
  min-height: 360px;
}
.megamenu-panel { display: none; flex-direction: column; }
.megamenu-panel.active { display: flex; }
.megamenu-panel-head { display: none; }
.megamenu-panel-item {
  display: block;
  padding: 10px 22px;
  font-size: 16px;
  font-weight: 500;
  color: var(--dark-2);
  border-bottom: 1px solid var(--gray-light);
}
.megamenu-panel-item:hover { background: var(--gray-light); color: var(--accent); }
.megamenu-panel-item.megamenu-placeholder {
  color: #9aa0ab;
  font-style: italic;
  cursor: default;
}
.megamenu-panel-item.megamenu-placeholder:hover { background: none; color: #9aa0ab; }

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px;
  /* .logo is position:absolute (out of flow) and .nav becomes
     position:fixed on mobile (also out of flow), so this is the only
     in-flow child left in .header-inner on mobile — without this it
     defaults to flex-start and sits directly under/over the logo instead
     of on the opposite side of the header. */
  margin-left: auto;
}
.nav-toggle span {
  width: 24px;
  height: 2px;
  /* Mobile only (.nav-toggle is display:none until the 760px breakpoint
     turns it on) — white/70%, staying that way regardless of scroll since
     the header itself is now a dark translucent bar at every scroll
     position, not just over the hero. */
  background: rgba(255,255,255,.7);
  display: block;
  transition: background var(--transition);
}

/* ===== Buttons ===== */
.btn {
  display: inline-block;
  /* min(vw, vh) — not vw alone — so it shrinks whichever way the window
     gets smaller (these buttons live in 100dvh sections too). */
  padding: clamp(8px, min(1vw, 1.6vh), 12px) clamp(14px, min(2vw, 3.2vh), 26px);
  border-radius: 8px;
  font-weight: 300;
  font-size: clamp(14px, min(1.3vw, 2.1vh), 19px);
  white-space: nowrap;
  transition: all var(--transition);
  border: 2px solid transparent;
}
.btn-primary {
  background: var(--accent);
  color: #fff;
}
.btn-primary:hover {
  background: var(--accent-dark);
  transform: translateY(-2px);
}
.btn-outline {
  border-color: #fff;
  color: #fff;
}
.btn-outline:hover {
  background: #fff;
  color: var(--dark);
}
.btn-purple {
  background: linear-gradient(90deg, #29c6ea, var(--purple));
  color: #fff;
  transition: background .3s ease, transform var(--transition);
}
.btn-purple:hover {
  background: linear-gradient(90deg, var(--purple), #29c6ea);
  transform: translateY(-2px);
}
.btn-dark {
  background: var(--dark);
  color: #fff;
}
.btn-dark:hover {
  background: var(--dark-2);
  transform: translateY(-2px);
}

/* ===== Placeholder image blocks ===== */
.img-placeholder {
  background: repeating-linear-gradient(45deg, #eef0f3, #eef0f3 10px, #e6e8ec 10px, #e6e8ec 20px);
  border: 1.5px dashed #c3c7ce;
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #8a8f99;
  font-weight: 600;
  font-size: 14px;
  text-align: center;
}

/* ===== Hero intro (standalone static section above the main hero) ===== */
.hero-intro {
  position: relative;
  height: 100vh;
  height: 100svh;
  height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  margin-top: calc(-1 * var(--header-overlap)); /* sits flush at the very top, behind the transparent sticky header — this section is now first in the page, so it does the job .hero used to do */
  overflow: hidden;
  background-image: url('../images/Home-page.b977f851.jpg');
  background-size: cover;
  background-position: center 0%;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

/* ===== Hero ===== */
.hero {
  position: relative;
  height: 100vh;
  height: 100svh; /* matches the device's actual visible screen, not just the CSS viewport */
  height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  overflow: hidden;
  background: var(--dark);
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.hero-track {
  display: flex;
  width: 300%;
  height: 100%;
  flex-shrink: 0;
  transition: transform .9s cubic-bezier(.65,0,.35,1);
}
.hero-slide {
  position: relative;
  width: 33.3334%;
  height: 100%;
  box-sizing: border-box;
  padding-top: var(--header-overlap);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  background-size: cover;
  background-position: center;
}
.hero-slide-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* On a cold page load, a video still needs a beat to fetch/decode its
     first frame. index.html's early blocking script sets this element's
     [poster] from a cached frame (captured client-side the last time this
     exact video file was played — see that script) and adds .is-ready
     immediately when one exists, so there's nothing to fade: the poster
     shows instantly and the browser swaps it for the live frame on its
     own. Only an entirely new/never-played video file falls through to
     opacity:0 here, fading in once script.js's "playing" listener adds
     .is-ready — showing the slide's own --dark background meanwhile
     instead of the video popping in abruptly once it's finally ready. */
  opacity: 0;
  transition: opacity .35s ease;
}
.hero-slide-video.is-ready {
  opacity: 1;
}
.hero-slide-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(13,27,48,.92) 0%, rgba(13,27,48,.65) 55%, rgba(13,27,48,.35) 100%);
}
.hero-inner {
  position: relative;
  z-index: 2;
  color: #fff;
  max-width: 640px;
  padding-top: 60px;
  padding-bottom: 60px;
}
.hero-arrow {
  position: absolute;
  top: var(--header-overlap);
  bottom: 0;
  margin-top: auto;
  margin-bottom: auto;
  transform: translateY(-50px);
  z-index: 3;
  width: 48px;
  height: 48px;
  border-radius: 0;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.hero-arrow::before {
  content: '';
  width: 14px;
  height: 14px;
  border-top: 2.5px solid var(--accent);
  border-left: 2.5px solid var(--accent);
  opacity: .7;
  transition: border-color .2s ease, opacity .2s ease, transform .2s ease;
}
.hero-arrow:hover::before {
  opacity: 1;
}
.hero-arrow-left::before { transform: rotate(-45deg); }
.hero-arrow-right::before { transform: rotate(135deg); }
.hero-arrow:hover::before { border-color: #fff; }
.hero-arrow-left { left: 24px; }
.hero-arrow-right { right: 24px; }
.eyebrow {
  color: var(--accent);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  margin-bottom: 12px;
}
.eyebrow.center { text-align: center; }
.hero h1 {
  font-size: clamp(26px, 3.2vw, 42px);
  font-weight: 800;
  line-height: 1.2;
  margin-bottom: 18px;
}
.hero h1 span { color: var(--accent); }
.hero-desc {
  color: #cfd3da;
  font-size: 16px;
  margin-bottom: 28px;
  max-width: 540px;
}
.hero-actions { display: flex; gap: 14px; }

.hero-dots {
  position: absolute;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 8px;
  z-index: 2;
}
.dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgba(255,255,255,.4);
  cursor: pointer;
  transition: all var(--transition);
}
.dot.active { background: var(--accent); width: 22px; border-radius: 5px; }

/* ===== Tile row ===== */
.tile-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(2, minmax(0, 1fr));
  height: 100vh;
  height: 100svh; /* full screen of the device on desktop's 4x2 grid; reverts to natural stacked height once columns collapse on mobile (see responsive block) */
  height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  border-top: 1.5px solid var(--accent);
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.tile {
  position: relative;
  display: block;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  box-sizing: border-box;
  border-left: 2px solid #9aa0ab;
  border-right: 2px solid #9aa0ab;
  border-bottom: 2px solid #9aa0ab;
  transition: border-color .25s ease;
  -webkit-tap-highlight-color: transparent;
}
.tile:hover {
  border-color: var(--accent);
}
.tile-img {
  position: absolute;
  inset: 0;
  border-radius: 0;
  transition: transform .5s ease;
}
.tile-img-real {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
.tile-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
  background: linear-gradient(180deg, rgba(10,20,35,.25) 0%, rgba(10,20,35,.55) 100%);
}
.tile-overlay-dark {
  background: rgba(0, 0, 0, .4);
}
.tile-title {
  color: rgba(255, 255, 255, .6);
  font-family: 'Roboto', 'Arial', sans-serif;
  font-weight: 900;
  font-size: clamp(16px, 1.8vw, 28px);
  letter-spacing: .5px;
  text-transform: uppercase;
  text-align: center;
  transition: opacity .3s cubic-bezier(.22,1,.36,1);
}
.tile-title-stacked { line-height: 0.85; font-family: 'SonySketchGO', 'Roboto', 'Arial', sans-serif; }
.tile-logo-strip {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px 60px;
  background: transparent;
  clip-path: polygon(50% 0%, 100% 100%, 0% 50%);
  transition: opacity .3s cubic-bezier(.22,1,.36,1);
}
.tile-has-sublist:hover .tile-logo-strip {
  opacity: 0;
}
.tile-title-logo {
  max-width: 260px;
  width: 70%;
  height: auto;
  transition: opacity .3s cubic-bezier(.22,1,.36,1);
}
.tile-title-logo-go2cam {
  max-width: 80px;
  width: 40%;
}
.tile-title-stacked .go2-part {
  font-size: 1.1em;
  font-weight: 400;
  color: #00ffcc;
}
.tile-title-stacked .cam-part { color: #fff; font-weight: 400; }
.tile-sublist {
  position: absolute;
  text-align: center;
  visibility: hidden;
  transition: visibility 0s linear .3s;
}
.tile-sublist li {
  color: #fff;
  font-family: 'Roboto', 'Arial', sans-serif;
  font-size: 17px;
  font-weight: 700;
  padding: 5px 0;
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .35s cubic-bezier(.22,1,.36,1), transform .35s cubic-bezier(.22,1,.36,1);
}
.tile-sublist li:hover {
  color: var(--accent);
}
.tile-has-sublist:hover .tile-title,
.tile-has-sublist:hover .tile-title-logo {
  opacity: 0;
}
.tile-has-sublist:hover .tile-sublist {
  visibility: visible;
  transition-delay: 0s;
}
/* The title fades out first (.3s); only once it's gone does the list begin
   rising in, one line at a time, each waiting for the previous to settle. */
.tile-has-sublist:hover .tile-sublist li:nth-child(1) { opacity: 1; transform: translateY(0); transition-delay: .3s; }
.tile-has-sublist:hover .tile-sublist li:nth-child(2) { opacity: 1; transform: translateY(0); transition-delay: .48s; }
.tile-has-sublist:hover .tile-sublist li:nth-child(3) { opacity: 1; transform: translateY(0); transition-delay: .66s; }
.tile-has-sublist:hover .tile-sublist li:nth-child(4) { opacity: 1; transform: translateY(0); transition-delay: .84s; }
.tile-has-sublist:hover .tile-sublist li:nth-child(5) { opacity: 1; transform: translateY(0); transition-delay: 1.02s; }
.tile:hover .tile-img { transform: scale(1.07); }
/* Touch equivalent of the :hover rules above — a touch tap can't hover, so
   without this the first tap on a tile just follows the link straight to
   products.html instead of previewing the sub-list first like a mouse
   hover does. script.js toggles this class instead (see the "(hover:
   none)" check there); mirrors every :hover rule on .tile-has-sublist. */
.tile-has-sublist.touch-active .tile-logo-strip { opacity: 0; }
.tile-has-sublist.touch-active .tile-title,
.tile-has-sublist.touch-active .tile-title-logo {
  opacity: 0;
}
.tile-has-sublist.touch-active .tile-sublist {
  visibility: visible;
  transition-delay: 0s;
}
.tile-has-sublist.touch-active .tile-sublist li:nth-child(1) { opacity: 1; transform: translateY(0); transition-delay: .3s; }
.tile-has-sublist.touch-active .tile-sublist li:nth-child(2) { opacity: 1; transform: translateY(0); transition-delay: .48s; }
.tile-has-sublist.touch-active .tile-sublist li:nth-child(3) { opacity: 1; transform: translateY(0); transition-delay: .66s; }
.tile-has-sublist.touch-active .tile-sublist li:nth-child(4) { opacity: 1; transform: translateY(0); transition-delay: .84s; }
.tile-has-sublist.touch-active .tile-sublist li:nth-child(5) { opacity: 1; transform: translateY(0); transition-delay: 1.02s; }
.tile.touch-active .tile-img { transform: scale(1.07); }

/* ===== Why choose us (panel) =====
   Same full-bleed-image + translucent diagonal panel treatment as the
   .dark-banner sections above: photo fills the whole section, and a
   see-through two-tone panel (media on the left, panel on the right —
   the "alt" mirrored layout, matching this section's media-then-panel
   DOM order) carries the text, centered inside it. */
.whyus {
  position: relative;
  background: var(--dark);
  height: 100vh;
  height: 100svh; /* full screen of the device, same as .dark-banner */
  height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  overflow: hidden;
  /* Now a full-screen block like hero/tile-row/dark-banner, so it joins the
     hard-stop snap chain instead of just being a soft snap-align target. */
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.whyus-media { position: absolute; inset: 0; z-index: 1; }
.whyus-placeholder { position: absolute; inset: 0; border-radius: 0; width: 100%; height: 100%; object-fit: cover; }
.whyus-panel {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 56%;
  margin-left: auto;
  /* Mirrored vertically (not just left/right) from the .dark-banner-text
     cut below, so the seam alternates "/" then "\" then "/" then "\" down
     the page instead of every panel leaning the same way. */
  background: linear-gradient(245deg, rgba(13, 27, 48, .93) 0%, rgba(19, 204, 150, .5) 100%);
  clip-path: polygon(0% 0, 100% 0, 100% 100%, 20% 100%);
  padding: 64px 11vw;
  color: #fff;
  text-align: center;
  overflow: hidden;
}
.whyus-panel h2 {
  font-size: clamp(20px, 2.6vw, 30px);
  font-weight: 800;
  text-transform: uppercase;
  margin-bottom: 8px;
}
.eyebrow-light {
  font-weight: 700;
  font-size: clamp(12.5px, 1.1vw, 15.5px); /* matches the dark-banner eyebrow ("Gia công phức hợp bề mặt" etc.) */
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: #fff;
  opacity: .8;
  margin-bottom: 10px;
}
.whyus-panel .feature-list-light {
  text-align: left; /* the heading/button stay centered as a group, but each icon+text row reads better left-aligned within itself */
  align-self: stretch;
}
/* Higher specificity than the later .feature-list h4/p (dark-on-light)
   rules below — otherwise those win the cascade (equal specificity, but
   later in the file) and silently flip these back to the wrong colors. */
.whyus-panel .feature-list-light h4 { color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, .35); }
.whyus-panel .feature-list-light p { color: #e7ebee; text-shadow: 0 1px 3px rgba(0, 0, 0, .35); }
.whyus-panel .btn-primary { margin-top: 30px; }

/* ===== Trust bar ===== */
.trustbar {
  background: var(--gray-light);
  padding: 30px 0;
  border-bottom: 1px solid var(--gray-border);
}
.trustbar-inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
}
.logo-strip-item {
  width: 140px;
  height: 50px;
  font-size: 11px;
  border-radius: 6px;
}

/* ===== Sections ===== */
.section { padding: 90px 0; }
.section-alt { background: var(--gray-light); }
.section-title {
  font-size: 32px;
  font-weight: 800;
  color: var(--dark);
  margin-bottom: 14px;
}
.section-title.center, .section-desc.center { text-align: center; margin-left: auto; margin-right: auto; }
.section-desc {
  color: var(--text-muted);
  font-size: 16px;
  max-width: 620px;
  margin-bottom: 46px;
}

/* ===== Grid & cards ===== */
.grid { display: grid; gap: 28px; }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }

.card {
  position: relative;
  background: #fff;
  border: 1px solid var(--gray-border);
  border-radius: var(--radius);
  padding: 22px;
  transition: transform var(--transition), box-shadow var(--transition);
}
.card:hover {
  transform: translateY(-6px);
  box-shadow: 0 20px 40px rgba(20,22,30,.08);
}
.card-img { height: 170px; margin-bottom: 18px; font-size: 13px; }
.card h3 { font-size: 18px; margin-bottom: 10px; color: var(--dark); }
.card p { color: var(--text-muted); font-size: 14.5px; margin-bottom: 14px; }
/* Reserve room for .card-link, pinned absolutely at the bottom — same
   treatment as .post-readmore in the Latest News cards. Scoped to the
   products grid only, so .card-simple (training page, no link) isn't
   affected. */
#san-pham .card { padding-bottom: 70px; }
.card-link {
  position: absolute;
  left: 15px;
  bottom: 15px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--accent);
  color: #fff;
  font-weight: 300;
  font-size: clamp(15px, min(1.6vw, 2.6vh), 26px);
  padding: clamp(6px, min(0.8vw, 1.3vh), 9px) clamp(10px, min(1.4vw, 2.2vh), 18px);
  border-radius: 8px;
  white-space: nowrap;
  transition: background .2s ease, transform .2s ease;
}
.card-link:hover { background: var(--accent-dark); transform: translateY(-2px); }
.card-link svg { width: clamp(14px, min(1.5vw, 2.4vh), 24px); height: clamp(14px, min(1.5vw, 2.4vh), 24px); transform: translateY(-3px) rotate(-45deg); opacity: .8; }

.card-simple { text-align: center; }
.icon-circle {
  width: 56px; height: 56px;
  border-radius: 50%;
  background: rgba(255,122,26,.12);
  display: flex; align-items: center; justify-content: center;
  font-size: 24px;
  margin: 0 auto 16px;
}

.tech-solution-wrap {
  background: #e8eaec;
  padding: 0;
}
.tech-solution-wrap .split {
  margin-top: 25px;
}

/* ===== Dark banners (Mill-Turn / Automation) =====
   Full-bleed background media (image/video fills the whole section) with a
   translucent, diagonally-cut two-tone panel layered on top holding the
   text — the panel's own gradient is see-through enough that the photo
   underneath still shows through it, instead of the old solid-color text
   column sitting beside a separate media column. */
.dark-banner {
  position: relative;
  background: var(--dark);
  height: 100vh;
  height: 100svh; /* accounts for mobile browser UI bars, so it matches the device's actual visible screen, not just the CSS viewport */
  height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  overflow: hidden;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.dark-banner + .dark-banner {
  margin-top: 3px;
}
.tile-row + .dark-banner {
  margin-top: 3px;
}
.dark-banner + .whyus {
  margin-top: 3px;
}
.dark-banner-media {
  position: absolute;
  inset: 0;
  z-index: 1;
}
.dark-banner-placeholder {
  width: 100%;
  height: 100%;
  border-radius: 0;
  /* Without this, <img> defaults to object-fit:fill (stretches/distorts
     to match the box) and <video> defaults to "contain" (letterboxes) —
     neither keeps the picture's real proportions as the box's own aspect
     ratio keeps changing while the window is resized. "cover" crops
     instead of stretching, so the image never looks warped. */
  object-fit: cover;
}
/* Only the <video> placeholders need the fetch/decode-gap fade (see
   .hero-slide-video below and js/script.js) — <img> placeholders have no
   such gap and must stay opaque immediately. */
video.dark-banner-placeholder {
  opacity: 0;
  transition: opacity .35s ease;
}
video.dark-banner-placeholder.is-ready {
  opacity: 1;
}
/* The panel is a normal block box (not absolutely positioned), so it's the
   only thing left in flow once the media above is taken out via
   position:absolute — that's what lets it sit flush at the section's
   left edge by default, and "margin-left:auto" push it to the right edge
   for the alt (mirrored) banners below, with no grid/order bookkeeping. */
.dark-banner-text {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  /* A pure percentage (no px cap) — the diagonal's on-screen position is a
     fixed fraction of the viewport at any width, so it always lands near
     the centerline instead of drifting once padding-left outgrows a
     fixed-px panel. */
  width: 56%;
  /* Hexagon-style lime-to-teal, lifted straight off their "5-Axis Milling"
     banner reference — straight top-to-bottom (180deg), independent of the
     diagonal clip-path shape below, matching how their own panel reads:
     green up top, teal down low, regardless of which way the cut leans. */
  background: linear-gradient(180deg, rgba(159, 205, 112, .92) 0%, rgba(6, 133, 163, .8) 100%);
  clip-path: polygon(0 0, 100% 0, 80% 100%, 0% 100%);
  /* Equal left/right padding (both vw-based, same value) so the safe
     (non-clipped) box sits centered inside the panel by construction —
     justify-content:center then centers the content group inside that box,
     landing it in the middle of the panel instead of hugging one edge. */
  padding-left: 11vw;
  padding-right: 11vw;
}
.dark-banner-alt .dark-banner-text {
  margin-left: auto;
  /* Cut at the bottom-left corner instead of the top-left — mirrored
     vertically from a plain right-panel cut so this section's seam leans
     the opposite way ("\") from the non-alt sections' ("/"), alternating
     down the page. */
  background: linear-gradient(180deg, rgba(159, 205, 112, .92) 0%, rgba(6, 133, 163, .8) 100%);
  clip-path: polygon(0% 0, 100% 0, 100% 100%, 20% 100%);
}
.dark-banner-content {
  max-width: 560px;
  color: #fff;
  padding: 60px 0;
  text-align: center;
}
.dark-banner-content .eyebrow {
  letter-spacing: 3px;
}
.dark-banner-content h2 {
  font-size: clamp(20px, 2.5vw, 33px);
  font-weight: 800;
  font-style: italic;
  text-transform: uppercase;
  line-height: 1.2;
  margin: 10px 0 18px;
  white-space: nowrap; /* the longest title ("Giải pháp Phay - Tiện kết hợp") was landing a lone word on its own second line by a hair — sized to always fit one line within the panel's text box instead */
}
.dark-banner-content p {
  color: #cfd3da;
  font-size: clamp(12.5px, 1.1vw, 15.5px);
  margin-bottom: 26px;
}
.dark-banner-actions {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: center;
}
.dark-banner-actions .btn {
  border-radius: 4px;
}

@media (max-width: 900px) {
  .dark-banner {
    display: flex;
    flex-direction: column;
  }
  .dark-banner-media,
  .dark-banner-alt .dark-banner-media {
    position: relative;
    inset: auto;
    order: 2;
    flex: 1 1 auto;
    min-height: 0;
  }
  .dark-banner-text,
  .dark-banner-alt .dark-banner-text {
    position: relative;
    order: 1;
    flex: 0 0 auto;
    width: 100%;
    height: auto;
    margin-left: 0;
    justify-content: flex-start; /* the centered layout is a desktop-panel thing; mobile keeps the original left-aligned stack */
    background: var(--dark); /* solid on mobile — no transparency/diagonal cut, so text stays fully readable at small sizes */
    clip-path: none;
    padding: 32px 20px 16px;
  }
  .dark-banner-content {
    max-width: 100%;
    padding: 0;
    text-align: left;
  }
  .dark-banner-content h2 {
    /* The desktop nowrap sizing (tuned for the diagonal panel's available
       width) doesn't apply to mobile's plain solid-background stack — on a
       narrow phone the longest title genuinely needs 2 lines, and without
       this it overflows .dark-banner's overflow:hidden and gets silently
       cropped instead of wrapping. */
    white-space: normal;
    font-size: clamp(20px, 6vw, 30px);
  }
  .dark-banner-actions {
    justify-content: flex-start;
  }
}

/* ===== Split section ===== */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 420px;
}
.split-content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 50px 60px;
}
.split-media { position: relative; }
.split-placeholder { position: absolute; inset: 0; border-radius: 0; height: 100%; font-size: 14px; }
.feature-list { margin-top: 26px; display: flex; flex-direction: column; gap: 20px; }
.feature-list li { display: flex; gap: 14px; align-items: flex-start; }
.feature-icon {
  width: 30px; height: 30px;
  background: var(--accent);
  color: #fff;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800;
  flex-shrink: 0;
}
.feature-list h4 { font-size: 15.5px; margin-bottom: 4px; color: var(--dark); }
.feature-list p { color: var(--text-muted); font-size: 14px; }

/* ===== Blog cards ===== */
.post-card {
  position: relative;
  background: var(--gray-light);
  border: none;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 14px 32px rgba(0,0,0,.22);
  transition: transform .35s ease;
}
.post-card:hover { transform: translateY(-14px); }
.post-featured-badge {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 2;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 20px;
  text-transform: uppercase;
  letter-spacing: .4px;
}
.post-img {
  display: block;
  height: 160px;
  width: 100%;
  margin-bottom: 0;
  font-size: 13px;
  border-radius: 0; /* flush with the card's top edge, not a standalone rounded box */
  background: var(--gray-light);
  object-fit: contain; /* show the whole image, not cropped — only affects real <img> elements */
}
/* Extra bottom padding reserves room for .post-readmore, which is pinned
   absolutely and taken out of normal flow — otherwise the button would
   overlap the description text. */
.post-card-body { padding: 18px 20px 70px; }
.post-date { color: var(--accent); font-weight: 700; font-size: 12.5px; text-transform: uppercase; letter-spacing: .5px; }
.post-card h3 { font-size: 17px; margin: 10px 0; color: var(--dark); }
.post-card p { color: var(--text-muted); font-size: 14px; margin-bottom: 16px; }
.post-readmore {
  position: absolute;
  left: 15px;
  bottom: 15px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--accent);
  color: #fff;
  font-weight: 300;
  font-size: clamp(15px, min(1.6vw, 2.6vh), 26px);
  padding: clamp(6px, min(0.8vw, 1.3vh), 9px) clamp(10px, min(1.4vw, 2.2vh), 18px);
  border-radius: 8px;
  white-space: nowrap;
  transition: background .2s ease, transform .2s ease;
}
.post-readmore:hover { background: var(--accent-dark); transform: translateY(-2px); }
.empty-state { padding: 60px 0; text-align: center; color: var(--text-muted); font-size: 16px; }
.post-readmore svg { width: clamp(14px, min(1.5vw, 2.4vh), 24px); height: clamp(14px, min(1.5vw, 2.4vh), 24px); transform: translateY(-3px) rotate(-45deg); opacity: .8; }

.latest-news-more {
  text-align: center;
  margin-top: 40px;
}
.latest-news-more .btn {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  border-radius: 8px;
  /* min(vw, vh) — not vh alone — so this shrinks whichever way the window
     gets smaller, not just when it gets shorter. */
  font-size: clamp(17px, min(3.6vh, 2.2vw), 32px);
  padding: clamp(7px, min(1.4vh, 0.9vw), 9px) clamp(24px, min(5vh, 3.2vw), 52px);
}
.latest-news-more .btn svg {
  width: clamp(18px, min(3.4vh, 2vw), 30px);
  height: clamp(18px, min(3.4vh, 2vw), 30px);
  flex-shrink: 0;
}
#tin-tuc .section-title {
  font-size: clamp(28px, min(3.2vw, 6vh), 51px);
  margin-bottom: clamp(20px, min(2.5vw, 4vh), 44px);
}
/* Full-screen block, same pattern as the dark-banner sections — see
   feedback_fullscreen_block_pattern.md. A single centered child (unlike
   dark-banner's two columns), so no grid/flex min-height:0 fix is needed:
   align-items:center just overflows-and-clips symmetrically if content
   is taller than the screen, instead of a track blowing out. */
#tin-tuc {
  height: 100vh;
  height: 100svh;
  height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  overflow: hidden;
  display: flex;
  align-items: center;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
#tin-tuc .container {
  width: 100%;
  /* Wider than the site's usual 1350px-capped container: stretches out
     to a fixed 150px from the page edge instead, so the 4 cards get more
     room and stay evenly spaced across the full width. */
  max-width: 100%;
  height: 100%;
  min-height: 0;
  padding: 0 150px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* Same fix as .yt-section (see feedback_fullscreen_block_pattern.md): the
   card row and each card become a shrinkable flex chain down to the post
   image, so a shorter real viewport (e.g. a browser chrome that reserves
   more vertical space than usual) shrinks the images instead of
   overflowing top/bottom of the 100dvh section. */
#tin-tuc .grid-4 {
  flex: 1 1 auto;
  min-height: 0;
  grid-template-rows: minmax(0, 1fr);
}
#tin-tuc .post-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}
#tin-tuc .post-card-body {
  flex: 0 0 auto;
}
#tin-tuc .post-img {
  flex: 1 1 auto;
  min-height: 0;
}
#tin-tuc .post-card h3 {
  font-size: clamp(16px, min(1.6vw, 3vh), 26px);
}
#tin-tuc .post-card p {
  font-size: clamp(13px, min(1.3vw, 2.4vh), 21px);
}
#tin-tuc .post-date {
  font-size: 10px;
}
#tin-tuc .post-readmore {
  font-size: clamp(15px, min(1.3vw, 2.6vh), 21px);
}
@media (max-width: 900px) {
  /* Desktop-only enhancement — a fixed 100dvh with 4 stacked post cards
     would clip content on a short mobile screen. Fall back to normal flow. */
  #tin-tuc {
    display: block;
    height: auto;
    overflow: visible;
  }
  #tin-tuc .container {
    padding: 0 20px;
  }
  #tin-tuc .post-img {
    flex: none;
    height: 400px;
  }
}

/* Progressive degradation driven by data-news-stage, set in script.js
   (updateNewsStage) from whichever of window width/height is tighter.
   Stage 1 drops the excerpt; stages 2-4 additionally drop to 3/2/1
   visible articles. Never applies at/below 900px — script.js clears the
   attribute there and #tin-tuc's own mobile media query above takes over. */
@media (min-width: 901px) {
  #tin-tuc[data-news-stage="1"] .post-card p,
  #tin-tuc[data-news-stage="2"] .post-card p,
  #tin-tuc[data-news-stage="3"] .post-card p,
  #tin-tuc[data-news-stage="4"] .post-card p {
    display: none;
  }
  #tin-tuc[data-news-stage="2"] .grid-4 { grid-template-columns: repeat(3, 1fr); }
  #tin-tuc[data-news-stage="2"] .post-card:nth-child(n+4) { display: none; }
  #tin-tuc[data-news-stage="3"] .grid-4 { grid-template-columns: repeat(2, 1fr); }
  #tin-tuc[data-news-stage="3"] .post-card:nth-child(n+3) { display: none; }
  #tin-tuc[data-news-stage="4"] .grid-4 { grid-template-columns: 1fr; }
  #tin-tuc[data-news-stage="4"] .post-card:nth-child(n+2) { display: none; }
}

/* ===== YouTube Videos =====
   Full-screen block, same pattern as the dark-banner sections — see
   feedback_fullscreen_block_pattern.md. Dark background instead of the
   light gray/white used by #tin-tuc. */
.yt-section {
  height: 100vh;
  height: 100svh;
  height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  overflow: hidden;
  display: flex;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.yt-section .container {
  width: 100%;
  max-width: 100%;
  height: 100%;
  min-height: 0;
  padding: 0 150px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* Row of cards fills whatever height is left under the title, and the row
   track itself is allowed to shrink (minmax(0,...) instead of auto) — same
   fix as .tile-row — so a shorter real viewport (e.g. a browser chrome that
   reserves more vertical space, like Cốc Cốc's bookmarks bar + side icon
   rail) shrinks the thumbnails instead of overflowing top/bottom of the
   100dvh section. */
.yt-section .grid-4 {
  flex: 1 1 auto;
  min-height: 0;
  grid-template-rows: minmax(0, 1fr);
}
.yt-section-title {
  font-size: clamp(28px, min(3.2vw, 6vh), 51px);
  margin-bottom: clamp(20px, min(2.5vw, 4vh), 44px);
}
.yt-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  background: var(--gray-light);
  border-radius: var(--radius);
  overflow: hidden;
  padding-bottom: 70px;
  box-shadow: 0 14px 32px rgba(0,0,0,.22);
}
.yt-card-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 16px 16px 12px;
  flex: 0 0 auto;
}
.yt-card-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  background: #fff;
  padding: 0;
  flex-shrink: 0;
}
.yt-card-meta { flex: 1; min-width: 0; }
.yt-card-channel {
  color: var(--dark);
  font-weight: 600;
  font-size: 15px;
}
.yt-card-date { color: #8a90a0; font-size: 10px; margin-top: 2px; }
.yt-card-icon { width: 45px; height: 45px; color: #ff0000; flex-shrink: 0; }
.yt-card-thumb-wrap {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  cursor: pointer;
}
.yt-card-thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .4s ease;
}
.yt-card-thumb-wrap:hover .yt-card-thumb {
  transform: scale(1.08);
}
.yt-card-play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(.85);
  width: 64px;
  height: 64px;
  border-radius: 50%;
  border: 2px solid #fff;
  background: rgba(20,20,20,.55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity .25s ease, transform .25s ease, background .2s ease;
}
.yt-card-play svg { width: 26px; height: 26px; margin-left: 3px; }
.yt-card-thumb-wrap:hover .yt-card-play {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
.yt-card-play:hover { background: var(--accent); border-color: var(--accent); }
.yt-card-thumb-wrap iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
.yt-card-body { padding: 16px; flex: 0 0 auto; }
.yt-card-body h3 {
  color: var(--dark);
  font-size: clamp(16px, min(1.6vw, 3vh), 26px);
  font-weight: 700;
  margin-bottom: 8px;
  line-height: 1.35;
}
.yt-card-body p {
  color: #9aa0ab;
  font-size: clamp(13px, min(1.3vw, 2.4vh), 21px);
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.yt-card-watch {
  position: absolute;
  left: 50%;
  bottom: 15px;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: #fff;
  border: 1px solid var(--gray-border);
  color: var(--dark);
  font-weight: 300;
  font-size: clamp(13px, min(1.3vw, 2.4vh), 21px);
  padding: clamp(6px, min(0.6vw, 1.1vh), 10px) clamp(40px, min(5vw, 9vh), 95px);
  border-radius: 8px;
  white-space: nowrap;
  transition: background .2s ease;
}
.yt-card-watch:hover { background: var(--gray-border); }
.yt-card-watch svg {
  width: clamp(13px, min(1.3vw, 2.4vh), 21px);
  height: clamp(13px, min(1.3vw, 2.4vh), 21px);
}
@media (max-width: 900px) {
  .yt-section {
    display: block;
    height: auto;
    overflow: visible;
    padding: 60px 0;
  }
  .yt-section .container {
    padding: 0 20px;
  }
  .yt-card-thumb-wrap {
    flex: none;
    height: 400px;
  }
}

/* Progressive degradation driven by data-yt-stage, set in script.js
   (updateYtStage) from whichever of window width/height is tighter.
   Stage 1 drops the description; stage 2 additionally drops the YouTube
   icon and channel name (avatar + date stay); stages 3-5 reduce to 3/2/1
   visible cards. Never applies at/below 900px — script.js clears the
   attribute there and .yt-section's own mobile media query above takes
   over. */
@media (min-width: 901px) {
  .yt-section[data-yt-stage="1"] .yt-card-body p,
  .yt-section[data-yt-stage="2"] .yt-card-body p,
  .yt-section[data-yt-stage="3"] .yt-card-body p,
  .yt-section[data-yt-stage="4"] .yt-card-body p,
  .yt-section[data-yt-stage="5"] .yt-card-body p {
    display: none;
  }
  .yt-section[data-yt-stage="2"] .yt-card-icon,
  .yt-section[data-yt-stage="2"] .yt-card-channel,
  .yt-section[data-yt-stage="3"] .yt-card-icon,
  .yt-section[data-yt-stage="3"] .yt-card-channel,
  .yt-section[data-yt-stage="4"] .yt-card-icon,
  .yt-section[data-yt-stage="4"] .yt-card-channel,
  .yt-section[data-yt-stage="5"] .yt-card-icon,
  .yt-section[data-yt-stage="5"] .yt-card-channel {
    display: none;
  }
  .yt-section[data-yt-stage="3"] .grid-4 { grid-template-columns: repeat(3, 1fr); }
  .yt-section[data-yt-stage="3"] .yt-card:nth-child(n+4) { display: none; }
  .yt-section[data-yt-stage="4"] .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .yt-section[data-yt-stage="4"] .yt-card:nth-child(n+3) { display: none; }
  .yt-section[data-yt-stage="5"] .grid-4 { grid-template-columns: 1fr; }
  .yt-section[data-yt-stage="5"] .yt-card:nth-child(n+2) { display: none; }
}

/* ===== Footer block: Contact + Customers combined into one full-screen
   block, same pattern as the dark-banner sections — see
   feedback_fullscreen_block_pattern.md. Contact takes whatever flexible
   space is left and centers its content in it; Customers keeps its
   natural height, always fully visible at the bottom. Footer itself is a
   separate, normal (non-full-screen) section right after this block. ===== */
.footer-block {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100svh;
  height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  overflow: hidden;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
#lien-he {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  overflow: hidden;
  padding: 0;
  /* A hash-link jump or scroll-snap navigation lands on this element's own
     top edge, ignoring any margin above it — keep it clear of the sticky
     header, plus 30px of breathing room. */
  scroll-margin-top: calc(var(--header-overlap) + 30px);
}
#lien-he .container {
  width: 100%;
}
.footer-block .customers-section {
  flex: 0 0 auto;
  min-height: 0;
}
.contact-box {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 50px;
  background: var(--dark);
  border-radius: 18px;
  transform: translateY(30px);
  /* vh-based, not fixed px — see feedback_fullscreen_block_pattern.md: a
     shorter real viewport (e.g. a browser chrome that reserves more
     vertical space than usual) needs this padding to shrink along with
     .customers-section's below, or #lien-he's centered content overflows
     top/bottom of the 100dvh footer-block. */
  padding: clamp(24px, 5vh, 60px) 60px clamp(40px, 9vh, 100px);
  color: #fff;
}
.contact-box .section-title, .contact-box .section-desc { color: #fff; }
.contact-box .section-desc { color: #cfd3da; }

@media (max-width: 900px) {
  /* The full-screen treatment is a desktop enhancement — forcing the
     contact form + customer logos into one clipped 100dvh block would
     cut off form fields on a short mobile screen. Fall back to normal
     stacked flow instead (scroll-snap is already off below this width
     too, so there's no snap chain to keep in sync with). */
  .footer-block {
    display: block;
    height: auto;
    overflow: visible;
  }
  #lien-he {
    display: block;
    padding: 90px 0 30px;
    overflow: visible;
  }
}
.contact-list { margin-top: 24px; display: flex; flex-direction: column; gap: 10px; font-size: 14.5px; color: #cfd3da; }
.contact-form { display: flex; flex-direction: column; gap: 14px; }
.contact-form input, .contact-form textarea {
  padding: 13px 16px;
  border-radius: 8px;
  border: 1px solid rgba(255,255,255,.18);
  background: rgba(255,255,255,.06);
  color: #fff;
  font-family: inherit;
  font-size: 14px;
}
.contact-form input::placeholder, .contact-form textarea::placeholder { color: #9aa0ab; }
.contact-form button { align-self: flex-start; }

/* ===== Customers ===== */
.customers-section {
  padding: 0;
  background: #fff;
  text-align: center;
}
.customers-title {
  font-size: clamp(16px, min(1.5vw, 2.6vh), 24px);
  font-weight: 800;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--dark);
  margin-bottom: 43px;
}
.customers-slider {
  display: flex;
  align-items: center;
  gap: 20px;
  /* Full-bleed: stretches edge-to-edge instead of being capped by .container. */
  padding: 0 28px;
}
.customers-track-wrap {
  flex: 1;
  overflow: hidden;
}
.customers-track {
  display: flex;
  transition: transform .6s cubic-bezier(.65,0,.35,1);
}
.customer-logo {
  /* Percentage-based, not a fixed px width, so exactly 6 logos always fit
     the visible area regardless of screen size (see responsive block for
     how many show per row on smaller screens). */
  flex: 0 0 16.6667%;
  width: 16.6667%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 24px;
}
.customer-logo img {
  max-width: 100%;
  max-height: 150px;
  width: auto;
  height: auto;
}
.customers-arrow {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--gray-border);
  background: #fff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color .2s ease, background .2s ease;
}
.customers-arrow::before {
  content: '';
  width: 10px;
  height: 10px;
  border-top: 2.5px solid var(--text-muted);
  border-left: 2.5px solid var(--text-muted);
  transition: border-color .2s ease;
}
.customers-arrow-left::before { transform: rotate(-45deg); margin-left: 3px; }
.customers-arrow-right::before { transform: rotate(135deg); margin-right: 3px; }
.customers-arrow:hover {
  border-color: var(--accent);
  background: var(--accent);
}
.customers-arrow:hover::before { border-color: #fff; }

@media (max-width: 960px) {
  .customer-logo { flex: 0 0 25%; width: 25%; } /* 4 visible */
}
@media (max-width: 600px) {
  .customers-slider { padding: 0 16px; gap: 10px; }
  .customer-logo { flex: 0 0 50%; width: 50%; padding: 0 14px; } /* 2 visible */
  .customer-logo img { max-height: 56px; }
  .customers-arrow { width: 38px; height: 38px; }
}

/* ===== Footer ===== */
.footer {
  background: var(--dark-2);
  color: #cfd3da;
  padding-top: 60px;
  scroll-snap-align: start;
}
.footer-inner {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 40px;
  padding-bottom: 40px;
}
.footer-col h4 { color: #fff; font-size: 15px; margin-bottom: 16px; }
.footer-col ul { display: flex; flex-direction: column; gap: 10px; }
.footer-col ul a { font-size: 14px; color: #b6bac2; transition: color var(--transition); }
.footer-col ul a:hover { color: var(--accent); }
.footer-col p { font-size: 13.5px; margin-top: 14px; color: #9aa0ab; }
/* Simple contact line used on the pages that don't have the homepage's
   full contact-form footer block (see feedback_new_page_checklist.md-style
   reasoning — phone/email need to live SOMEWHERE now that they're gone
   from the shared topbar). */
.footer-contact-line {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px 32px;
  padding-bottom: 40px;
  font-size: 14.5px;
}
.footer-contact-line a {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: #cfd3da;
  transition: color var(--transition);
}
.footer-contact-line a:hover { color: var(--accent); }
.footer-contact-line svg { flex-shrink: 0; }
.social-row { display: flex; gap: 10px; }
.social-ico {
  width: 36px; height: 36px;
  border-radius: 50%;
  background: rgba(255,255,255,.08);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700;
  transition: background var(--transition);
}
.social-ico:hover { background: var(--accent); }
.footer-bottom {
  padding: 20px 0;
  font-size: 13px;
  text-align: center;
  color: #8a8f99;
}
.footer-credit {
  color: var(--accent);
  margin-left: 6px;
}

/* ===== Back to top ===== */
.back-to-top {
  position: fixed;
  right: 24px;
  bottom: 24px;
  width: 44px;
  height: 44px;
  border-radius: 8px;
  border: none;
  background: rgba(20, 20, 20, .55);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transform: translateY(8px);
  transition: opacity .25s ease, transform .25s ease, background .2s ease;
  z-index: 200;
}
.back-to-top.visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.back-to-top:hover {
  background: var(--accent);
}
.back-to-top svg {
  width: 18px;
  height: 18px;
}

/* ===== Scroll reveal ===== */
.reveal { opacity: 0; transform: translateY(24px); transition: opacity .6s ease, transform .6s ease; }
.reveal.is-visible { opacity: 1; transform: translateY(0); }
/* .contact-box has its own permanent translateY(30px) offset (see its
   rule above) — without this, .reveal.is-visible's translateY(0) briefly
   wins during/after the fade-in, and the box visibly jumps down to 30px
   the moment script.js cleans up the reveal/is-visible classes. Resting
   at the same 30px here means nothing changes when those classes drop. */
.contact-box.reveal.is-visible { transform: translateY(30px); }

/* ===== Responsive ===== */
@media (max-width: 960px) {
  html { scroll-snap-type: none; }
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
  .tile-row {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: none;
    height: auto;
  }
  .tile { height: 300px; }
  .split { grid-template-columns: 1fr; }
  .split-media { height: 280px; }
  .whyus {
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100svh;
    height: calc(var(--app-vh, 1vh) * 100); /* JS-locked, wins over the two lines above — see the blocking script after </header> in index.html */
  }
  .whyus-media {
    position: relative;
    inset: auto;
    height: auto;
    flex: 1 1 auto;
    min-height: 0;
  }
  .whyus-panel {
    position: relative;
    width: 100%;
    height: auto;
    margin-left: 0;
    flex: 0 0 auto;
    align-items: flex-start;
    justify-content: flex-start;
    text-align: left;
    background: var(--dark); /* solid on mobile — no transparency/diagonal cut, same reasoning as .dark-banner */
    clip-path: none;
    padding: 44px 32px;
    overflow: visible;
  }
  .whyus-panel .feature-list-light { text-align: left; }
  .tile-row { grid-template-columns: 1fr; }
  .contact-box { grid-template-columns: 1fr; padding: 36px; }
  .footer-inner { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 760px) {
  /* topbar-right (social icons + Zalo/WhatsApp + VI/EN + search) needs
     ~590px, more than a phone screen has — everything stays visible,
     just trimmed down to fit: pills drop their text label (icon-only),
     icons/gaps shrink a little, and the search input collapses to a
     small tap target that expands back out once it actually has focus. */
  .topbar-right {
    gap: 8px;
    /* anchor for .topbar-search.open below, which floats over this row
       instead of pushing the icons beside it out of the way */
    position: relative;
  }
  /* Hidden for as long as the mobile nav menu is open (toggled by
     script.js alongside .nav.open) — the menu panel covers this exact
     row anyway, so hiding it outright avoids any tint/blur bleed-through
     showing through gaps between icons. Brought back the moment the menu
     closes. */
  body.nav-open .topbar-right {
    visibility: hidden;
  }
  .topbar-contact-icons { gap: 4px; }
  .topbar-social-ico { width: 26px; height: 26px; }
  .topbar-pill { padding: 5px 6px; }
  .topbar-pill span { display: none; }
  .topbar-contact-more { width: 26px; height: 26px; }
  .topbar-contact-toggle { font-size: 10.5px; }
  /* Collapsed icon-only state itself is already the default now (see the
     base .topbar-search rules) — mobile just wants a slightly smaller
     tap target than desktop's. */
  .topbar-search { padding: 6px; }
  /* Opened (via script.js's click handler): becomes an overlay pinned to
     its own spot in the row but escaping the flex layout, so it extends
     leftward over the FB/YouTube/Zalo/WhatsApp icons instead of shoving
     them aside — there's no room on a phone screen to push them further
     left the way the desktop version does. A solid background so it
     actually reads as covering them, not just floating text on top.
     Closes again on an outside tap. */
  .topbar-search.open {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    z-index: 20;
    background: var(--dark);
    box-shadow: 0 6px 16px rgba(0,0,0,.35);
    padding: 6px 12px;
  }
  .topbar-search.open input {
    width: 260px; /* fits the full placeholder, same as desktop */
    max-width: calc(100vw - 90px); /* clamp on very narrow phones so the overlay doesn't run off the left edge */
    padding-left: 6px;
  }
  .nav {
    position: fixed;
    /* Starts below the topbar's own height (52px) instead of at the very
       top — otherwise this panel's blurred dark background paints behind
       the topbar's icon row too (topbar itself still renders visually on
       top via z-index, so the icons stay clickable either way, but
       having the panel's background bleed up behind them makes that row
       look swallowed into the dropdown instead of like its own separate
       bar sitting above it). */
    /* Covers the topbar's own icon row too, all the way from the very
       top — script.js hides .topbar-right (FB/YouTube/Zalo/WhatsApp/
       VI-EN/search) for as long as the menu is open (see body.nav-open
       below) and brings it back the moment it closes, so there's never
       a moment where those icons sit underneath this panel's tint. */
    top: 0; right: -100%;
    /* Hugs the widest link's own text by default — script.js overrides
       this with an inline width every time the menu opens, measured to
       reach exactly to the topbar's Facebook icon, so the panel's left
       edge lines up with where that icon row started before it got
       hidden, instead of a width unrelated to it. max-width is the
       fallback/cap if JS can't run for some reason. */
    width: fit-content;
    max-width: min(78%, 320px);
    height: 100vh;
    height: 100svh;
    background: rgba(13, 27, 48, .8);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow: -10px 0 30px rgba(0,0,0,.25);
    padding: 50px 24px 24px;
    transition: right .35s cubic-bezier(.22,1,.36,1);
    z-index: 99;
  }
  .nav.open { right: 0; }
  /* .nav > ul > li > a is already white by default (originally meant for
     the transparent header over the Hero) — that's exactly what this dark
     panel needs too, so no override here. */
  .nav ul { flex-direction: column; gap: 4px; }
  /* Two-pane side-by-side layout doesn't fit a phone screen — flatten into
     one column instead, each brand immediately followed by its own
     sub-items (always expanded, no accordion tap needed). The two wrapper
     columns become invisible via display:contents so their children (the
     5 brand links + 5 panels) are all direct flex children of .megamenu,
     then order pairs each brand with its own panel right after it —
     without that they'd stay grouped as "all 5 brands, then all 5
     panels" (left column's DOM position), not interleaved. */
  .has-megamenu .megamenu {
    position: static;
    display: none;
    flex-direction: column;
    opacity: 1; visibility: visible; transform: none;
    box-shadow: none; background: transparent;
  }
  .has-megamenu.open .megamenu { display: flex; }
  /* Simple one-column dropdown ("Cộng đồng") — same accordion toggle as
     the mega menus, but flat (no left/right panes to flatten). */
  .has-dropdown .dropdown {
    position: static;
    display: none;
    flex-direction: column;
    opacity: 1; visibility: visible; transform: none;
    box-shadow: none; background: transparent;
    padding: 0;
  }
  .has-dropdown.open .dropdown { display: flex; }
  .dropdown-item { color: #fff; padding: 8px 0 8px 24px; font-size: 13.5px; font-weight: 400; }
  .dropdown-item:hover { background: none; color: var(--accent); }
  .dropdown-item.dropdown-all {
    color: #fff;
    background: var(--accent);
    border-radius: 6px;
    padding-right: 12px;
    padding-left: 12px;
    margin: 4px 12px 6px 12px;
  }
  .dropdown-item.dropdown-all:hover { color: #fff; }
  .megamenu-left, .megamenu-right { display: contents; }
  .megamenu-left-item { color: #fff; padding: 10px 0 10px 12px; }
  .megamenu-left-item:hover,
  .megamenu-left-item.active { background: none; color: var(--accent); }
  .megamenu-left-item.megamenu-all {
    color: #fff;
    background: var(--accent);
    border-radius: 6px;
    padding-right: 12px;
    margin-right: 12px;
  }
  .megamenu-panel {
    display: flex !important; /* always expanded on mobile, not gated by .active */
    flex-direction: column;
  }
  .megamenu-panel-item {
    color: rgba(255,255,255,.75);
    padding: 8px 0 8px 24px;
    border-bottom: none;
    font-size: 13.5px;
  }
  .megamenu-panel-item:hover { background: none; color: var(--accent); }
  .megamenu-left-item[data-panel="go2cam"] { order: 1; }
  .megamenu-panel[data-panel="go2cam"] { order: 2; }
  .megamenu-left-item[data-panel="ncgcam"] { order: 3; }
  .megamenu-panel[data-panel="ncgcam"] { order: 4; }
  .megamenu-left-item[data-panel="solidworks"] { order: 5; }
  .megamenu-panel[data-panel="solidworks"] { order: 6; }
  .megamenu-left-item[data-panel="creo"] { order: 7; }
  .megamenu-panel[data-panel="creo"] { order: 8; }
  .megamenu-left-item[data-panel="cad2d"] { order: 9; }
  .megamenu-panel[data-panel="cad2d"] { order: 10; }
  .nav-toggle { display: flex; }
  .logo-text { display: none; } /* icon-only on mobile — not enough room next to the hamburger */
  .hero h1 { font-size: 30px; }
  .hero-arrow { display: none; }
  .grid-3 { grid-template-columns: 1fr; }
  .grid-4 { grid-template-columns: 1fr; }
  .footer-inner { grid-template-columns: 1fr; }
}
