/*
 * Mobile-first. Unqualified rules below are the <768px layout (base
 * case, no media query needed). Tablet (>=768px) and desktop
 * (>=1200px) overrides live in responsive.css, loaded last so they
 * win the cascade. Breakpoints and per-section behavior are taken
 * from Tina's actual Figma frames at each width, not scaled guesses
 * — see responsive.css's header comment for what changes at each
 * breakpoint and why.
 */

/* ===== NavBar ===== */
/* The vertical padding lives on .navbar__inner, not .navbar, so the
   mobile menu (a sibling of .navbar__inner) starts at the navbar's
   bottom edge instead of covering its lower padding. */
.navbar {
  --navbar-pad-y: 12px;
  --navbar-pad-x: 20px;
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--color-white);
  border-bottom: 1px solid var(--color-neutral-150);
  padding: 0 var(--navbar-pad-x);
  /* 入場：頁面載入時由上而下滑入（全站共用，跟 hero 入場同步起跑）。
     只動 transform；跑完 fill 交還給元素原本的無 transform 狀態，
     不影響 sticky 行為。 */
  animation: navbar-in var(--sys-motion-duration) var(--sys-motion-easing) backwards;
}

@keyframes navbar-in {
  from {
    transform: translateY(-100%);
  }
}

/* 首頁例外：navbar 不在載入時滑入，先收在畫面外，等 hero 遮罩文字
   捲到淡出完成，script.js 才加 .is-in 放它下來。沒有 JS 時由
   index.html 的 <noscript> 樣式直接還原成可見。
   改 fixed 而不是沿用 sticky：sticky 會在文件流裡佔著自己的高度，
   navbar 被 transform 收上去時那塊空間仍在，影片上方就會露出一條
   白邊。fixed 不佔位，滑入後直接蓋在影片上。 */
.navbar--hero-gated {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  box-sizing: border-box;
  animation: none;
  transform: translateY(-100%);
  transition: transform var(--sys-motion-duration) var(--sys-motion-easing);
}

.navbar--hero-gated.is-in {
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .navbar {
    animation: none;
  }
}

.navbar__inner {
  max-width: var(--container-max);
  margin: 0 auto;
  padding-block: var(--navbar-pad-y);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.navbar__left {
  display: flex;
  align-items: center;
  gap: 24px;
  flex: 1;
}

.navbar__logo img {
  width: 36px;
  height: 36px;
}

.navbar__links {
  display: none;
  align-items: center;
  flex: 1;
}

.navbar__links a,
.navbar__right a {
  padding: 8px 12px;
  font-weight: 700;
  font-size: var(--type-nav-link);
  color: var(--color-neutral-600);
  transition: color var(--sys-motion-duration) var(--sys-motion-easing);
}

.navbar__right {
  display: none;
  align-items: center;
}

/* Hover + current-page ("active") states (Tina, 2026-09-08 — Figma
   node 740:8090, "navItem" component, size=def): both states are the
   *same* treatment, just text neutral/600 -> neutral/800, no color
   accent or background. This replaces an earlier guess (primary/500
   red) made before this node reference existed. Also fixes a real
   bug this uncovered: .is-active only ever targeted .navbar__links,
   so .navbar__right's own is-active copy (contact.html's「聯絡我們」)
   was silently not styled at all. */
.navbar__links a:hover,
.navbar__right a:hover,
.navbar__links a.is-active,
.navbar__right a.is-active {
  color: var(--color-neutral-800);
}

.mobile-menu__accordion-label.is-current {
  color: var(--color-primary-500);
}

/* Same current-page indicator for a top-level mobile menu item (not
   inside the 關於英菲達 accordion) — first needed by 03 Ubiquiti, which
   sits directly in the top-level list rather than a submenu. */
.mobile-menu__item.is-active {
  color: var(--color-primary-500);
}

/* ===== Desktop nav dropdown (≧768, node 4007:15460) ===== */
/* Lives inside .navbar__links, so it's already hidden below 768 along
   with the rest of the desktop nav — no separate media query needed.
   Hover-triggered per Figma; :focus-within added so keyboard users can
   tab into the panel (no Figma spec for that, standard a11y practice). */
/* Spans the navbar's full height (negative margin cancels the padding
   it re-adds) so the panel anchors to the navbar's bottom edge and the
   pointer never leaves the hover target on its way down to the panel. */
.navbar__item--dropdown {
  position: relative;
  display: flex;
  align-items: center;
  align-self: stretch;
  margin-block: calc(-1 * var(--navbar-pad-y));
  padding-block: var(--navbar-pad-y);
}

/* 展開過場（Tina, 2026-09-10）：面板一直是 flex，改用 visibility ＋
   clip-path 控制顯示——display:none 沒有可過場的起點。clip-path 從
   inset(0 0 100% 0) 拉到 0，看起來就是從 navbar 底緣往下捲開；
   收合狀態的 visibility:hidden 一樣把面板移出 tab 順序與無障礙樹。
   只做展開，不做淡入淡出（Tina 指定）——所以沒有 opacity。 */
.navbar__dropdown {
  display: flex;
  visibility: hidden;
  clip-path: inset(0 0 100% 0);
  transition: clip-path var(--sys-motion-duration) var(--sys-motion-easing),
    visibility 0s linear var(--sys-motion-duration);
  flex-direction: column;
  position: absolute;
  top: calc(100% + 1px); /* +1px clears .navbar's border-bottom */
  left: 0;
  width: 226px;
  background: var(--color-white);
  border-left: 1px solid var(--color-neutral-150);
  border-right: 1px solid var(--color-neutral-150);
  z-index: 10;
}

.navbar__item--dropdown:hover .navbar__dropdown,
.navbar__item--dropdown:focus-within .navbar__dropdown {
  visibility: visible;
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--sys-motion-duration) var(--sys-motion-easing),
    visibility 0s;
}

/* Specificity note: the generic `.navbar__links a` rule (1 class + 1
   type = 0,1,1) would otherwise beat a bare `.navbar__dropdown-link`
   (0,1,0) and silently win — qualifying with the parent class here
   (0,2,0) is what actually overrides it. */
.navbar__dropdown .navbar__dropdown-link {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 16px;
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-nav-link);
  line-height: 1.5;
  color: var(--color-neutral-600);
  border-bottom: 1px solid var(--color-neutral-150);
  background: var(--color-white);
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing);
}

.navbar__dropdown .navbar__dropdown-link:hover {
  background: var(--color-neutral-050);
}

/* ===== Mobile Menu Toggle & Dropdown (<768 only) ===== */
/* Figma: rwd=mobile, if menu=no/yes (node 4007:14125 / 4007:15022) */
.navbar__toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 999px;
  border: none;
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* icon-button/ghost, color=dark, state=hover (node 740:8090) — same
   bg token the "open" (.is-active) state already uses. */
.navbar__toggle.is-active,
.navbar__toggle:hover {
  background: var(--color-neutral-050);
}

.navbar__toggle-icon {
  width: 20px;
  height: 20px;
}

/* Absolute, anchored to the sticky .navbar: in normal flow it grew the
   navbar and pushed the page content down every time it opened. It
   resolves against .navbar's padding box, which already spans the full
   width, so left/right 0 gives the edge-to-edge rows of the Figma
   dropdown frame with no padding to cancel. The max-height keeps a long
   menu (all rows plus the open accordion) reachable on short screens
   instead of running off the bottom with no way to scroll it. */
.mobile-menu {
  position: absolute;
  top: calc(100% + 1px); /* +1px clears .navbar's border-bottom */
  left: 0;
  right: 0;
  max-height: calc(100vh - (var(--navbar-pad-y) * 2 + 36px + 1px));
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  background: var(--color-white);
  border-top: 1px solid var(--color-neutral-200);
}

/* JS 載入前的初始狀態；第一次開啟後 hidden 就被拿掉，改由 .is-open
   控制（見 script.js setNavExpanded）。 */
.mobile-menu[hidden] {
  display: none;
}

/* 展開過場：同 .navbar__dropdown 的 clip-path 捲開，一樣不做淡入淡出。
   這是絕對定位的浮層，不需要把下方內容推開。 */
.mobile-menu {
  visibility: hidden;
  clip-path: inset(0 0 100% 0);
  transition: clip-path var(--sys-motion-duration) var(--sys-motion-easing),
    visibility 0s linear var(--sys-motion-duration);
}

.mobile-menu.is-open {
  visibility: visible;
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--sys-motion-duration) var(--sys-motion-easing),
    visibility 0s;
}

/* label/def/m, confirmed via get_design_context: fs/base, weight 500
   (line-height comes from the sitewide body default; letter-spacing
   can't — em-based letter-spacing computes to a fixed px at whichever
   element declares it and inherits as that fixed px, not as a ratio
   each descendant recalculates against its own font-size, so it needs
   its own declaration here rather than relying on body's). */
.mobile-menu__item {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 16px;
  border-bottom: 1px solid var(--color-neutral-150);
  background: var(--color-white);
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-nav-link);
  color: var(--color-neutral-600);
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* menu-item, type=underline, state=hover (node 4019:17893) */
.mobile-menu__item:hover {
  background: var(--color-neutral-050);
}

.mobile-menu__accordion {
  border-bottom: 1px solid var(--color-neutral-150);
}

/* The row holds two separate targets: the label navigates to about.html,
   the +/− button only expands the submenu. */
.mobile-menu__accordion-row {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 16px;
  background: var(--color-white);
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* collapse, state=hover (node 4019:17981) — text/icon are already at
   their hover-state color by default here (see comment above), so the
   background highlight is what actually signals hover, consistent with
   the sibling menu items. */
.mobile-menu__accordion-row:hover {
  background: var(--color-neutral-050);
}

/* label/def/b — same metrics as label/def/m above, weight 700. */
.mobile-menu__accordion-label {
  flex: 1 0 0;
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-nav-link);
  color: var(--color-neutral-800);
  text-align: left;
}

/* padding + matching negative margin keeps the icon exactly where the
   Figma row puts it while giving the button a 36px touch target. */
.mobile-menu__accordion-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  margin: -8px;
  border: none;
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
}

.mobile-menu__accordion-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

/* 手風琴子選單在文件流裡，展開時要把下面的項目往下推，所以不能用
   clip-path（不會 reflow），改用 max-height。400px 只是過場用的上限
   （4 個項目實際約 230px），不是實際高度——展開後高度仍由內容決定。
   收合狀態 visibility:hidden 讓連結不可聚焦。 */
.mobile-menu__submenu {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  max-height: 0;
  visibility: hidden;
  transition: max-height var(--sys-motion-duration) var(--sys-motion-easing),
    visibility 0s linear var(--sys-motion-duration);
}

.mobile-menu__submenu.is-open {
  max-height: 400px;
  visibility: visible;
  transition: max-height var(--sys-motion-duration) var(--sys-motion-easing),
    visibility 0s;
}

/* JS 載入前的初始狀態，同 .mobile-menu[hidden]。 */
.mobile-menu__submenu[hidden] {
  display: none;
}

.mobile-menu__submenu-link {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 16px;
  background: var(--color-white);
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-nav-link);
  color: var(--color-neutral-600);
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing);
}

.mobile-menu__submenu-link.is-active,
.mobile-menu__submenu-link:hover {
  background: var(--color-neutral-050);
}

/* ===== Hero ===== */
/* Text-masked video scroll animation. The white overlay blends with
   `lighten`, so white stays white and the black letters let the video
   through — the text is the window onto the video. Scale/opacity come
   from custom properties script.js drives off scroll position; without
   JS (or with reduced motion) the hero stays one viewport tall and the
   masked text sits still at scale 1. */
/* 滿版一個視窗：首頁 navbar 是 fixed、不佔文件流，影片直接頂到
   視窗上緣，navbar 滑入時蓋在影片上。 */
.hero {
  position: relative;
  width: 100%;
  height: 100vh;
}

.hero.is-scroll-animated {
  height: 260vh;
}

.hero__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  isolation: isolate;
  background: var(--color-white);
}

.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.8;
  transform: scale(var(--hero-video-scale, 1));
}

.hero__mask {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-white);
  mix-blend-mode: lighten;
  opacity: var(--hero-mask-opacity, 1);
}

/* <768 stacks as two lines per Figma node 7158:13242 ("iNNOVATION +"
   over "Trust"); the spans go inline again from 768 up. */
.hero__mask-text {
  margin: 0;
  font-family: var(--font-en);
  font-weight: 700;
  /* Grown via font-size, not transform: scale() magnifies the glyph
     raster the compositor baked at the base size and the edges go
     jagged; font-size re-rasterizes the outlines at every step. */
  font-size: calc(clamp(var(--fs-28), 9.6vw, var(--fs-huge)) * var(--hero-mask-scale, 1));
  line-height: 1.1;
  text-align: center;
  color: #000;
  white-space: nowrap;
}

/* Two separate marks, not one string that reflows: <768 is the stacked
   two-line lockup (Figma node 7158:13242), >=768 the single line (node
   7158:7030). Each stays nowrap so growing the font never re-breaks it. */
.hero__mask-text--inline {
  display: none;
}

.hero__mask-text--stacked span {
  display: block;
}

/* ===== Hero 入場動畫（頁面載入） ===== */
/* 白底 → 影片與遮罩文字同時淡入，影像只從字體裡透出來。終點剛好
   是上面捲動動畫的起點（mask scale 1／opacity 1、影片 0.8），兩段
   接得起來不打架。
   純 CSS animation、不靠 JS 加 class：JS 觸發會先畫一幀最終畫面
   再跳回起點閃一下；這樣寫沒有 JS 也照樣完整播放。
   全程只動 opacity／transform，不碰會觸發 layout 的屬性。 */
.hero__mask-text {
  animation: hero-text-in var(--sys-motion-duration) var(--sys-motion-easing) backwards;
}

.hero__video {
  animation: hero-video-in var(--sys-motion-duration) var(--sys-motion-easing) backwards;
}

/* ===== Hero 捲動提示 ===== */
/* 疊在遮罩之上（z-index 高於 .hero__mask），不然會被遮罩那層的白底
   連同 lighten 一起吃掉。捲動一啟動就淡出，淡出程度由 script.js 的
   --hero-hint-opacity 控制；沒有 JS 時維持顯示。 */
.hero__scroll-hint {
  position: absolute;
  left: 50%;
  bottom: 24px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  /* 跟 hero-hint-in 的 from 用同一組 transform 函式，插值才不用退回
     矩陣換算。 */
  transform: translate(-50%, 0);
  opacity: var(--hero-hint-opacity, 1);
  animation: hero-hint-in var(--sys-motion-duration) var(--sys-motion-easing)
    var(--sys-motion-duration) backwards;
}

.hero__scroll-hint-label {
  font-family: var(--font-en);
  font-size: var(--fs-13);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-neutral-600);
}

/* 軌道＋在軌道內由上往下跑的短線段，只動 transform。 */
.hero__scroll-hint-line {
  position: relative;
  width: 1px;
  height: 48px;
  overflow: hidden;
  background: var(--color-neutral-200);
}

.hero__scroll-hint-line::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-primary-500);
  animation: hero-hint-run 1.8s var(--sys-motion-easing) infinite;
}

@keyframes hero-hint-in {
  from {
    opacity: 0;
    transform: translate(-50%, 8px);
  }
}

@keyframes hero-hint-run {
  from {
    transform: translateY(-100%);
  }
  to {
    transform: translateY(100%);
  }
}

/* 只寫 from：to 交給元素自己的宣告值（文字無 transform、影片
   opacity 0.8），動畫結束後不留殘值。
   文字不做 opacity：遮罩走 mix-blend-mode: lighten，白底上的黑字
   調透明只會更白（等於更看不見），字的顯現本來就由影片那層的
   opacity 決定，再疊一次只會讓字糊掉。 */
@keyframes hero-text-in {
  from {
    transform: scale(0.94);
  }
}

@keyframes hero-video-in {
  from {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero__mask-text,
  .hero__video,
  .hero__scroll-hint,
  .hero__scroll-hint-line::after {
    animation: none;
  }
}

/* The info section rides up over the still-pinned video for the last
   viewport of the runway — the parallax half of the effect. The pull-up
   shifts the following section into that same overlap, so it needs the
   same opaque layer or the video shows through it. */
.hero.is-scroll-animated + .info-section {
  margin-top: -100vh;
}

.hero.is-scroll-animated + .info-section,
.hero.is-scroll-animated + .info-section + .service-section {
  position: relative;
  z-index: 1;
  background: var(--color-white);
}

/* ===== Info Section ===== */
.info-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 32px;
  padding: var(--comp-info-sec-p-top) var(--sys-grid-margin) var(--comp-info-sec-p-bottom);
}

.info-section__intro {
  max-width: var(--container-max);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.info-section__title-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.info-section__label-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}

.info-section__label {
  font-weight: 500;
  font-size: var(--type-page-eyebrow);
}

/* en/headline/3xl/b */
.info-section__display {
  font-family: var(--font-en);
  font-weight: 700;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 8px;
}

.info-section__display span {
  color: var(--color-primary-500);
}

/* headline/xl/b */
.info-section__headline {
  font-weight: 700;
  font-size: var(--type-sec-title);
  line-height: 1.5;
}

.info-section__body {
  color: var(--color-neutral-600);
  max-width: 900px;
}

.info-section__body p + p {
  margin-top: 4px;
}

/*
 * Peek carousel, not a scroll list — per Figma ("list" frame is
 * narrower than its children's combined width, with Clip Content on:
 * only the center image sits fully inside the visible area, the
 * neighbors peek in from both edges and get cut off). Same pattern
 * at every breakpoint, just different image sizes/gaps — see
 * responsive.css. No scrollbar, no swipe; static peek only, per the
 * "carousel/pagination gets static appearance only" rule.
 */
.info-section__gallery {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: calc(100% + 40px);
  margin: 0 -20px;
  overflow: hidden;
}

/* Width set as a % of the (edge-to-edge) gallery, fluid at every
   breakpoint instead of jumping between fixed px sizes. Center image
   is widest at 50%; the two flanking tiers step down (40%, 33.33%).
   All images share one fixed aspect-ratio (not each image's own
   natural ratio) so height scales in lockstep with width — center
   biggest, then its neighbors, then the outermost pair — regardless
   of the source photos' differing natural ratios; object-fit:cover
   crops the ones that don't already match 2:1 without distorting them.

   2026-09-09（Tina 確認之後會接 CMS，張數會變動）：改用
   nth-child/nth-last-child 雙向配對，不寫死「共 5 張」。50% 是張數夠
   多時中間那一整段的寬度（plateau），只有離兩端最近的兩層（各 1 張
   最外層 33.333%、1 張次外層 40%）用選取器覆寫，兩端往內數，不管
   實際共幾張都對稱；3 張時中間那張沒有專屬選取器命中，自然吃到
   50% plateau，一樣是三張裡最大的。 */
.info-section__gallery img {
  aspect-ratio: 2 / 1;
  object-fit: cover;
  flex-shrink: 0;
  width: 50%;
}

.info-section__gallery img:nth-child(1),
.info-section__gallery img:nth-last-child(1) {
  width: 33.333%;
}

.info-section__gallery img:nth-child(2),
.info-section__gallery img:nth-last-child(2) {
  width: 40%;
}

/*
 * ===== Service Section =====
 * Re-derived from the true 480px mobile master (node 7152:24592) —
 * an earlier pass had read a manually-resized 767px *instance* of
 * this frame, whose margins/proportions don't scale linearly with
 * width, so several values below replace an earlier wrong 70%/gap
 * based approximation.
 *
 * .service-section__content is a real wrapper (matches Figma's own
 * "content" node, 7152:24212) holding image+card as one group with
 * flexbox — no CSS Grid, no position:absolute. At >=1200 this
 * wrapper switches to display:contents so its two children become
 * direct items of .container's grid instead (see responsive.css) —
 * that's how the image can stand alone next to a title+card+
 * pagination column at that breakpoint without needing a second,
 * differently-nested copy of this markup.
 */
.service-section {
  padding: var(--comp-service-sec-p-top) 0 var(--comp-service-sec-p-bottom);
  display: flex;
  justify-content: center;
  overflow-x: hidden;
}

.service-section .container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-xl);
}

.service-section__top {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  max-width: var(--container-md);
  width: 100%;
  margin: 0 auto;
  padding: 0 16px;
}

.service-section__content {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: var(--container-md);
}

/* Figma's "content" node nests card+pagination in their own wrapper
   only at >=768 (node 7158:8164, gap:10px between just those two) —
   at <768 they're not grouped (pagination sits separately below the
   card with its own margin-top). display:contents keeps this wrapper
   out of the box tree at <768 so card/pagination act as if they were
   still flat children of .service-section__content, exactly as
   before this wrapper was introduced; >=768 below turns the grouping
   on. */
.service-section__row {
  display: contents;
}

/* Image is right-flush, card is left-flush, each capped at 448px —
   not a percentage of the container. Card overlaps 24px up into the
   image's bottom edge via a negative margin (card's own background
   paints over it, no z-index needed since it's later in DOM/paint
   order). */
/* Width 95% of container, capped at 600px (Tina, 2026-09-04) — was a
   flat 100%/448px. */
.service-section__image {
  position: relative;
  align-self: flex-end;
  width: 95%;
  max-width: 600px;
  height: auto;
  aspect-ratio: 448 / 220;
  overflow: hidden;
  margin-bottom: -24px;
}

/* Two stacked layers instead of one (Tina, 2026-09-05: swapping a
   single <img>'s own src while clipped made the whole block go blank
   mid-transition — she wants the old image to stay visible underneath,
   like the next one is already there and just gets swept over it).
   --base is the currently-settled image, always fully visible, no
   transition of its own. --incoming sits on top of it and is what
   actually animates: script.js sets its src to the next slide, clips
   it fully hidden + scaled to 110% with no transition, then clears
   both so it "Sweep to Right"s in over --base while shrinking to
   100%. Once that finishes, script.js copies the new src down onto
   --base and silently resets --incoming back to hidden — --base
   already shows the same image by then, so nothing jumps. */
.service-section__image-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.service-section__image-layer--incoming {
  clip-path: inset(0 0 0 0);
  transform: scale(1);
  transition: clip-path var(--sys-motion-duration) var(--sys-motion-easing),
    transform var(--sys-motion-duration) var(--sys-motion-easing);
}

.service-section__desc {
  color: var(--color-neutral-600);
  text-align: center;
  font-size: var(--type-body-def);
}

.service-card {
  align-self: flex-start;
  display: flex;
  width: 100%;
  max-width: 448px;
}

/* Inside .service-section__content (grouped with image+card) not a
   .container-level sibling — >=768 groups it into the same row as
   the card (see responsive.css), >=1200 needs it back with
   top+card in the left column, which content's display:contents
   there already achieves without moving it in the DOM again. */
.service-section__pagination {
  align-self: center;
  margin-top: 32px;
}

.service-section__pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 0 16px;
}

/* ===== Security Section ===== */
.security-section {
  padding: var(--comp-security-sec-p-top) var(--sys-grid-margin) var(--comp-security-sec-p-bottom);
  display: flex;
  justify-content: center;
}

.security-section__pin {
  flex: 1;
  display: flex;
  justify-content: center;
}

/* Pinned exit: the extra bottom padding is the runway the sticky child
   travels, and the brand section is pulled back up over the same
   distance — so the content sits still while 代理品牌 rides up over it.
   script.js adds .is-pinned (skipped for reduced motion) and sets the
   pin offset so content taller than the viewport still shows its
   bottom before it locks. */
/* Block, not flex: the sticky child's travel comes from its containing
   block's CONTENT box, so the runway has to be real content height (the
   ::after spacer) — padding on the section would add none. */
.security-section.is-pinned {
  display: block;
}

.security-section.is-pinned::after {
  content: "";
  display: block;
  height: var(--comp-security-overlap);
}

.security-section.is-pinned .security-section__pin {
  position: sticky;
  top: var(--security-pin-top, 0px);
  opacity: var(--security-exit-fade, 1);
  transform: scale(var(--security-exit-scale, 1));
}

.security-section.is-pinned + .brand-section {
  margin-top: calc(-1 * var(--comp-security-overlap));
}

.security-section .container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-xl);
  max-width: var(--container-md);
}

/* ===== Brand Section ===== */
.brand-section {
  background: var(--color-neutral-050);
  padding: var(--comp-brand-sec-p-top) 20px var(--comp-brand-sec-p-bottom);
  display: flex;
  justify-content: center;
  position: relative;
  z-index: 1;
}

.brand-section .container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-xl);
}

.brand-section__top {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  text-align: center;
}

.brand-section__desc {
  color: var(--color-neutral-600);
  font-size: var(--type-body-def);
}

/* ===== Stats (numSec) ===== */
.stats-section {
  background: var(--color-neutral-900);
  padding: var(--comp-stats-sec-p-top) var(--sys-grid-margin) var(--comp-stats-sec-p-bottom);
  display: flex;
  justify-content: center;
}

/* Mobile: title + stats-grid are centered and hug their content width
   (not stretched full-width) — per Figma <768. Tablet/desktop keep
   this centering too; only the grid's internal direction changes
   (see responsive.css / stats-grid rule). */
.stats-section .container {
  max-width: var(--container-sm);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-xl);
}

.stats-section .section-title {
  color: var(--color-white);
}

/* 深色底上的 eyebrow 一律 primary/400（Tina, 2026-09-10）——base
   .section-eyebrow 的 primary/500 在深色底上太悶，白色又跟旁邊的白字
   標題撞在一起。numSec 與 clientSec 是全站僅有的兩個深色底 section，
   兩邊都套同一個值，改成三個斷點都成立（原本只有 ≧1200 這樣）。
   node 7111:12619（numSec）／7125:5628（clientSec）皆為 primary/400。 */
.stats-section .section-eyebrow,
.clients-section__top .section-eyebrow {
  color: var(--color-primary-400);
}

/* Mobile: stacked column (Figma <768). Tablet+ reverts to a row —
   see responsive.css. align-self:stretch makes the grid fill the
   container's width instead of hugging content (the parent
   .stats-section .container centers/shrink-wraps its children). */
.stats-grid {
  display: flex;
  flex-direction: column;
  gap: 32px;
  align-self: stretch;
}

/* ===== Section Slash（brand-section → stats-section 的斜線分隔帶）=====
   node 9165:14111：80px 高的整寬帶，深色（stats 的底 neutral/900）打底，
   上面疊一塊淺色（brand 的底 neutral/050）三角形，斜邊從左上 (0,0) 拉到
   右下 (100%,80)——所以左側整條是深色、右側整條是淺色。

   進場動畫（Tina, 2026-09-10）：淺色三角形起始退化成一條線（高度 0），
   看起來就是一條水平線；捲進畫面時以左端 (0,0) 為支點往下轉到指定斜率。
   轉完才輪到 stats 的內容淡入——順序由 .stats-section 的 --fade-offset
   接手（見 components.css）。

   三個斷點都顯示，斜率固定不隨 RWD 變（Tina, 2026-09-10）：高度不寫死
   px，改用 aspect-ratio 讓高度＝寬度÷--slash-ratio；固定 px 高度的話
   窄斷點會變成完全不同的斜率。clip-path 的三角形頂點是百分比，斜率
   跟著 aspect-ratio 自動維持。

   --slash-ratio 是唯一的調整鈕，數字越小斜率越陡：node 9165:14111 量到
   的是 18（80/1440），Tina 2026-09-10 要求加陡，改成 12（1440 時 120px 高）。

   ----- 為什麼是「淺色打底 ＋ 深色三角形」而不是反過來 -----
   反過來寫（深色打底、淺色三角形）時，淺色三角形的上緣正好落在
   y=0，等於一條零高度的水平邊；瀏覽器對這種貼齊邊界的邊會做反鋸齒，
   只畫到半格覆蓋率，底下的深色就從整條頂緣透出來，變成一條橫貫全寬
   的細線（Tina 2026-09-10 回報）。改成淺色打底之後頂緣是「淺色對上方
   brand-section 的淺色」，沒有顏色差就不可能透出線；唯一會被反鋸齒的
   邊只剩斜邊本身，那本來就是要有的邊。

   ----- 三個防縫措施 -----
   1. clip-path 的左、右、下三個頂點各外擴 2px（斜邊的兩個端點維持
      精確的 (0,0)/(100%,100%)，斜率不受影響），外擴的部分由
      overflow:hidden 裁掉——這三條邊就不會有反鋸齒半格。
   2. margin-top:-1px：brand-section 有 z-index:1 會蓋在本元素之上，
      讓它多蓋 1px，補掉 aspect-ratio 算出小數高度時可能露出的
      body 白縫。
   3. margin-bottom:-1px：stats-section 在 DOM 之後且同為未定位元素，
      會蓋在本元素之上，同樣補掉下緣的白縫。 */
.section-slash {
  --slash-ratio: 12;
  aspect-ratio: var(--slash-ratio) / 1;
  position: relative;
  overflow: hidden;
  background: var(--color-neutral-050);
  margin-top: -1px;
  margin-bottom: -1px;
}

.section-slash::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-neutral-900);
  clip-path: polygon(
    -2px 0,
    calc(100% + 2px) 0,
    calc(100% + 2px) calc(100% + 2px),
    -2px calc(100% + 2px)
  );
  transition: clip-path var(--sys-motion-duration) var(--sys-motion-easing);
}

.section-slash.visible::before {
  clip-path: polygon(
    -2px 0,
    100% 100%,
    calc(100% + 2px) calc(100% + 2px),
    -2px calc(100% + 2px)
  );
}

/* ===== Clients Section ===== */
.clients-section {
  position: relative;
  padding: var(--comp-clients-sec-p-top) var(--sys-grid-margin) var(--comp-clients-sec-p-bottom);
  display: flex;
  justify-content: center;
  color: var(--color-white);
  overflow: hidden;
}

/* Scroll parallax (Tina, 2026-09-05, bumped from 15%/0.15 to 25%/0.25
   same day — original felt too subtle): oversized (inset -25%
   top/bottom instead of 0) so script.js can translateY it a bit slower
   than the page scrolls without ever exposing an edge —
   .clients-section's own overflow:hidden clips the excess. */
.clients-section__bg {
  position: absolute;
  inset: -25% 0;
  z-index: -1;
  will-change: transform;
}

.clients-section__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.clients-section__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-dark-80a);
}

.clients-section .container {
  max-width: var(--container-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-xl);
}

.clients-section__top {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--grid-gutter);
  width: 100%;
}

.clients-section__top .section-title {
  width: 100%;
}

.clients-section__top p {
  color: var(--color-neutral-200);
  font-size: var(--type-body-def);
}

/*
 * Mobile: flat 2-column grid, natural DOM order (公司企業/政府法人/
 * 飯店住宿/餐飲業/醫療診所/學校教育) reads row-by-row — matches
 * Figma <768. Desktop reuses the same flat card list but switches to
 * a 3-column, auto-flow:column grid so the same DOM order instead
 * fills column-by-column, reproducing the original staggered 3-column
 * layout without duplicating markup — see responsive.css.
 */
.client-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
  width: 100%;
}

/* Figma's own "content" node (7158:7168, >=768) is 3 independent flex
   columns of differing height, not a grid — display:contents here
   keeps that grouping out of the box tree at <768 so the 6 cards
   still land directly in .client-grid's 2-column grid above, exactly
   as before this wrapper was introduced; >=768 below turns the real
   per-column grouping on. */
.client-grid__col {
  display: contents;
}

/* ===== News Section ===== */
.news-section {
  padding: var(--comp-news-sec-p-top) var(--sys-grid-margin) var(--comp-news-sec-p-bottom);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.news-section__container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
}

.news-section__top {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

/* <768 的「清單 → 按鈕」＝ 40px：容器 gap 走 --sys-layout-sec-gap-def
   （這個斷點 24px），差額 16px 補在按鈕上。容器 gap 之後若再調，這個
   補正值要跟著重算。≧768 起按鈕移到標題列右側，補正歸零，列距改由
   grid 的 row-gap 接手（responsive.css）。 */
.news-section__more {
  margin-top: 16px;
}

/* Mobile: cards' combined width exceeds the container, so it scrolls
   horizontally (Figma <768 has the same overflow). Tablet+ fits all
   3 in a row — see responsive.css. Scrollbar hidden since scroll-snap
   swipe is the intended interaction, not a visible scroll track. */
.news-list {
  max-width: 952px;
  width: 100%;
  display: flex;
  gap: var(--grid-gutter-sm);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.news-list::-webkit-scrollbar {
  display: none;
}

/* Fixed 260 while this list can scroll (mobile); tablet+ switches to
   flex-fill once scrolling is off — see responsive.css. */
.news-card {
  flex-shrink: 0;
  width: 260px;
  scroll-snap-align: start;
}

/* ===== CTA Section ===== */
/* Same caveat as clients-section: no mobile/tablet Figma reference,
   generic stack default. */
.cta-section {
  position: relative;
  padding: var(--comp-cta-sec-p-top) var(--sys-grid-margin) var(--comp-cta-sec-p-bottom);
  display: flex;
  justify-content: center;
  overflow: hidden;
}

/* Scroll parallax (Tina, 2026-09-05) — same technique as
   .clients-section__bg above. */
.cta-section__bg {
  position: absolute;
  inset: -25% 0;
  z-index: -1;
  will-change: transform;
}

.cta-section__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cta-section__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-dark-40a);
}

.cta-section .container {
  max-width: var(--container-md);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 32px;
  color: var(--color-white);
}

.cta-section__top {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

/* 視覺跟 .info-section__label 相同，各自獨立宣告，不共用 class（避免跨 Block 牽動） */
.cta-section__label {
  font-weight: 500;
  font-size: var(--type-page-eyebrow);
}

/* en/headline/3xl/b — same style as .info-section__display */
.cta-section__display {
  font-family: var(--font-en);
  font-weight: 700;
}

.cta-cards {
  flex-direction: column;
  align-items: stretch;
}

/* ===== Footer ===== */
.site-footer {
  background: var(--color-neutral-800);
  color: var(--color-white);
}

.site-footer__top {
  padding: 32px 20px;
  display: flex;
  justify-content: center;
}

.site-footer__inner {
  max-width: var(--container-max);
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.site-footer__col-info {
  max-width: 464px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.site-footer__company {
  font-weight: 700;
  font-size: var(--type-sec-title-sm);
}

.site-footer__info {
  border-left: 3px solid var(--color-primary-400);
  padding-left: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.site-footer__info-row {
  display: flex;
  gap: 8px;
}

.site-footer__info-row dt {
  font-weight: 700;
}

.site-footer__nav-area {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.site-footer__nav {
  display: flex;
  gap: 24px;
  width: 100%;
}

.site-footer__nav-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.site-footer__nav-heading {
  color: var(--color-neutral-100);
  font-size: var(--type-body-sm);
}

.site-footer__nav-col ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* label/sm/b */
.site-footer__nav-col ul a {
  font-weight: 700;
  line-height: 1.25;
}

.site-footer__social {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  font-size: var(--type-body-sm);
}

.site-footer__social-icons {
  display: flex;
  gap: 12px;
}

.site-footer__social-icons a {
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background: var(--color-white);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* icon-button/filled, size=sm, state=hover */
.site-footer__social-icons a:hover {
  background: var(--color-neutral-050);
}

.site-footer__social-icons img {
  width: 20px;
  height: 20px;
}

.site-footer__bottom {
  background: rgba(217, 38, 38, 0.8);
  padding: 12px 20px;
  display: flex;
  justify-content: center;
}

.site-footer__bottom-inner {
  max-width: var(--container-max);
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  text-align: center;
  font-size: var(--type-body-sm);
}

/*
 * ===== 02 About Page =====
 * Verified against three Figma symbols (not their resized instances):
 * "02 about < 768" (9112:16117), "02 about ≧ 768" (9112:17309),
 * "02 about ≧ 1200" (4024:14066) — same file as the homepage
 * (DgTQJuQHck2Ok5UiupLxTI). Side padding at every section below follows
 * this page's own --grid/margin reading (16/24/40) — same progression
 * as --sys-grid-margin, which the homepage's sections now also use
 * (2026-09-09: the homepage's old flat 20px --grid-margin-sm was
 * retired and merged into this same token).
 */

/* ----- Hero title bar (About / 關於英菲達科技) ----- */
/* Figma's own background here is a light-gray abstract dot/line
   pattern image at 80% opacity — its asset export currently comes back
   fully transparent (see the HTML comment on this section), and there
   is no separate layer to isolate a working screenshot from (the
   pattern and the title text share one Figma frame). Falls back to a
   flat neutral tone until that asset is re-exported. */
.about-hero {
  position: relative;
  height: 180px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 0 var(--sys-grid-margin);
  background: var(--color-neutral-050);
}

.about-hero__content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

/* en/headline/def/m at <768/≧768, steps up to en/headline/md/m at ≧1200 (responsive.css) */
.about-hero__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-500);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b then headline/6xl/b in responsive.css */
.about-hero__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
}

/* ----- Intro paragraph (勇於創新，誠實信任) ----- */
.about-intro {
  background: var(--color-white);
  display: flex;
  justify-content: center;
  padding: var(--comp-about-intro-sec-p-top) 16px var(--comp-about-intro-sec-p-bottom);
}

.about-intro__container {
  max-width: 708px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
}

.about-intro__title-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

/* headline/3xl/b — fs-28 constant across all 3 breakpoints (Figma), no responsive override needed */
.about-intro__title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

/* Single vertical accent bar under the title (node imgLine1: 5px x 20px,
   primary/500) — not the same visual as .section-title's own 1px
   neutral/200 divider, so implemented as its own element rather than
   reusing that class. */
.about-intro__divider {
  width: 5px;
  height: 20px;
  background: var(--color-primary-500);
}

.about-intro__body {
  width: 100%;
  color: var(--color-neutral-600);
  line-height: 1.5;
  text-align: center;
}

/* ----- 經營理念 (business philosophy, 3 cards) ----- */
.about-philosophy {
  background: var(--color-white);
  display: flex;
  justify-content: center;
  padding: var(--comp-about-philosophy-sec-p-top) 16px var(--comp-about-philosophy-sec-p-bottom);
}

.about-philosophy__container {
  max-width: 642px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sys-layout-sec-gap-def);
}

.about-philosophy__list {
  display: flex;
  flex-direction: column;
  gap: 24px;
  width: 100%;
}

.about-philosophy__card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}

.about-philosophy__card-img {
  aspect-ratio: 296 / 170;
  width: 100%;
  object-fit: cover;
}

/* img_02_01 / img_02_03 crop from the bottom in Figma (object-bottom);
   img_02_02 crops from center (plain object-cover) — kept as written,
   not normalized to one value. */
.about-philosophy__card-img--bottom {
  object-position: bottom;
}

.about-philosophy__card-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* headline/md/b at <768/≧768 (fs-18); steps to headline/lg/b (fs-20) at ≧1200 */
.about-philosophy__card-title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-card-title-def);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

.about-philosophy__card-desc {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
}

/* ----- 客戶肯定 (customer endorsement) ----- */
.about-testimony {
  display: flex;
  justify-content: center;
  padding: var(--comp-about-testimony-sec-p-top) 16px var(--comp-about-testimony-sec-p-bottom);
}

.about-testimony__container {
  max-width: 642px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sys-layout-sec-gap-def);
}

.about-testimony__top {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

.about-testimony__desc {
  color: var(--color-neutral-600);
  line-height: 1.5;
}

.about-testimony__cards {
  display: flex;
  flex-direction: column;
  gap: 20px;
  width: 100%;
}

.about-testimony__card {
  background: var(--color-neutral-050);
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px;
  width: 100%;
}

.about-testimony__card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
}

.about-testimony__card-num {
  background: var(--color-primary-500);
  color: var(--color-white);
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-en);
  font-weight: 700;
  font-size: var(--type-card-desc-def);
  line-height: 1.5;
}

/* label/def/b at <768/≧768 (fs-base); steps to headline/md/b (fs-18) at ≧1200 */
.about-testimony__card-title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-card-title-def);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

/* body/sm/r at <768/≧768 (fs-14); steps to body/def/r (fs-base) at ≧1200 —
   deliberately neutral/800 (not the usual neutral/600 desc color), per
   Figma. */
.about-testimony__card-desc {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-800);
}

.about-testimony__photo {
  aspect-ratio: 952 / 540;
  width: 100%;
  object-fit: cover;
  object-position: bottom;
}

.about-testimony__actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  width: 100%;
}

/* ----- 實例影片 (installation videos, dark section) ----- */
.about-videos {
  background: var(--color-neutral-800);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
  padding: var(--comp-about-videos-sec-p-top) 16px var(--comp-about-videos-sec-p-bottom);
}

/* Wraps the 2 video cards as their own fade-children group. Mirrors
   .about-videos' own gap (not display:contents — an
   IntersectionObserver target needs a real layout box to ever report
   as intersecting, which a contents-display element never has). */
.about-videos__list {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
}

.about-video-card {
  background: var(--color-dark-40a);
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 20px;
  width: 100%;
}

.about-video-card__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 16px;
  width: 100%;
}

.about-video-card__num {
  background: var(--color-primary-500);
  color: var(--color-white);
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-en);
  font-weight: 700;
  font-size: var(--type-card-desc-def);
  line-height: 1.5;
}

/* label/def/b at <768/≧768 (fs-base); steps to headline/md/b (fs-18) at ≧1200.
   width:100% here (column layout, centered text) switches to flex:1
   at ≧768 (row layout, alongside the fixed num badge) — see
   responsive.css. */
.about-video-card__heading {
  width: 100%;
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-card-title-def);
  line-height: 1.5;
  color: var(--color-white);
}

.about-video-card__thumb {
  display: block;
  aspect-ratio: 960 / 540;
  width: 100%;
  object-fit: cover;
}

/* ----- 口碑好評 (customer reviews) ----- */
.about-reviews {
  display: flex;
  justify-content: center;
  padding: var(--comp-about-reviews-sec-p-top) 16px var(--comp-about-reviews-sec-p-bottom);
}

.about-reviews__container {
  max-width: 642px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
}

.about-reviews__desc {
  color: var(--color-neutral-600);
  line-height: 1.5;
  width: 100%;
}

.about-reviews__content {
  display: flex;
  flex-direction: column;
  gap: 20px;
  width: 100%;
}

.about-reviews__list {
  display: flex;
  flex-direction: column;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
}

/* Same card used for the single featured quote and the 3-up list —
   Figma gives both the same background/padding pattern at every
   breakpoint, only the wrapper (single vs row/column of 3) differs. */
.about-review-card {
  background: var(--color-secondary-100);
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px;
  width: 100%;
}

.about-review-card__icon {
  width: 24px;
  height: 24px;
}

/* body/def/r at <768 (fs-base, regular); steps to body/md/m (fs-18,
   medium, fixed 24px line-height — that named style's own value, not
   the 1.25/1.5 convention) at ≧768/≧1200 */
.about-review-card__quote {
  font-family: var(--font-zh);
  font-weight: 400;
  font-size: var(--type-card-desc-md);
  line-height: 1.5;
  color: var(--color-neutral-800);
  width: 100%;
}

.about-review-card__author {
  font-family: var(--font-zh);
  font-weight: 400;
  font-size: var(--type-body-sm);
  line-height: 1.25;
  color: var(--color-neutral-800);
  text-align: right;
  width: 100%;
}

/* ----- 底部 CTA (歡迎聯繫我們, bg photo + gradient) ----- */
.about-contact {
  position: relative;
  display: flex;
  justify-content: center;
  overflow: hidden;
  padding: var(--comp-about-contact-sec-p-top) 16px var(--comp-about-contact-sec-p-bottom);
}

.about-contact__bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.about-contact__bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Directional wash (not the dark ::after overlay other sections use) —
   fades from 80%-white at the left down to transparent by 100%,
   matching node 7039:5963's own gradient exactly. */
.about-contact__gradient {
  position: absolute;
  inset: 0;
  background: linear-gradient(to right, rgba(255, 255, 255, 0.8) 30%, rgba(255, 255, 255, 0) 100%);
}

.about-contact__container {
  position: relative;
  max-width: 642px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  text-align: left;
  gap: var(--sys-layout-sec-gap-def);
}

.about-contact__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  width: 100%;
}

/* en/headline/def/m at <768/≧768 (fs-base); steps to en/headline/md/m (fs-18) at ≧1200 */
.about-contact__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-500);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b then headline/6xl/b in responsive.css */
.about-contact__heading {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
  width: 100%;
}

/* ----- Breadcrumb (shared pattern — every future inner page reuses this) ----- */
.breadcrumb-section {
  background: var(--color-neutral-050);
  display: flex;
  justify-content: center;
  padding: 0 16px;
}

.breadcrumb-section__list {
  max-width: var(--container-max);
  width: 100%;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 12px 0;
  list-style: none;
}

/* Every crumb stays on one line — no wrapping to a second row. Only
   the last crumb (.breadcrumb-section__current, a CMS page title of
   unbounded length) is allowed to shrink/grow to fill the remaining
   row width; the fixed labels before it (首頁/最新消息/...) and the
   separator icons keep their natural width. */
.breadcrumb-section__list li {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.breadcrumb-section__list li:has(.breadcrumb-section__current) {
  flex: 1 1 auto;
  min-width: 0;
}

/* label/sm/m */
.breadcrumb-section__link,
.breadcrumb-section__current {
  display: block;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-label-sm);
  line-height: 1.25;
  color: var(--color-neutral-600);
}

.breadcrumb-section__separator {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

/* ===== FAB ===== */
.fab {
  position: fixed;
  right: 16px;
  bottom: 16px;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background: var(--color-neutral-800);
  border: none;
  color: var(--color-white);
  font-size: var(--fs-18);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing),
    opacity var(--sys-motion-duration) var(--sys-motion-easing),
    visibility var(--sys-motion-duration) var(--sys-motion-easing);
}

/* Shown once scrolled past 300px (PRD 章節二／待辦) — see script.js
   updateBackToTopVisibility. */
.fab.is-visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

/* icon-button/fab, size=sm, state=hover (node 7063:3976) */
.fab:hover {
  background: var(--color-neutral-600);
}

/* Sized in % so the ring tracks the button at every breakpoint — the
   old <img> kept its intrinsic 46.67px and overflowed the 40px button
   below 768. Geometry (r 22.475, stroke 1.716 in a 46.6667 viewBox)
   matches the original assets/imgProgressBar.svg; rotated so the arc
   starts at 12 o'clock. */
.fab__progress {
  position: absolute;
  inset: 8.33%;
  width: 83.34%;
  height: 83.34%;
  transform: rotate(-90deg);
}

.fab__progress-track,
.fab__progress-bar {
  fill: none;
  stroke-width: 1.716;
}

.fab__progress-track {
  stroke: var(--color-white);
}

.fab__progress-bar {
  stroke: var(--color-primary-500);
  stroke-dasharray: 141.22; /* 2πr */
  stroke-dashoffset: calc(141.22 * (1 - var(--fab-progress, 0)));
}

.fab__arrow {
  position: relative;
  width: 20px;
  height: 20px;
}

/*
 * ===== 02-1 Our Clients Page =====
 * Verified against three Figma symbols (not their resized instances):
 * "02-1 our clients < 768" (9112:34154), "02-1 our clients ≧ 768"
 * (9112:35099), "02-1 our clients ≧ 1200" (7006:19607) — same file as
 * the homepage/about page (DgTQJuQHck2Ok5UiupLxTI). Side padding
 * follows this page's own 16/24/40 reading, same numbers as the about
 * page's own margin scale (see that section's header comment) — kept
 * as its own literal set rather than a shared token, same as that
 * page's precedent.
 *
 * The customer-logo grid content in every Figma frame here is the
 * site's own "iTC" mark repeated as a generic placeholder (not named
 * client logos) — see PRD 章節三 02-1 and this page's script.js
 * comment. Real per-category client names/logos are still pending
 * from Tina.
 */

/* ----- Hero title bar (Our Clients / 眾多客戶) ----- */
.our-clients-hero {
  position: relative;
  height: 180px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 0 var(--sys-grid-margin);
}

.our-clients-hero__bg {
  position: absolute;
  inset: 0;
}

.our-clients-hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.our-clients-hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--color-dark-80a);
}

.our-clients-hero__content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

/* en/headline/def/m at <768/≧768 (fs-base), steps to en/headline/md/m
   (fs-18) at ≧1200 — same metrics as .about-hero__eyebrow; color is
   primary/400 here vs primary/500 there, per each page's own Figma
   reading, so kept as a separate rule rather than sharing the class. */
.our-clients-hero__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-400);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b then
   headline/6xl/b in responsive.css — same named-style progression as
   .about-hero__title, white here (dark photo bg) instead of
   neutral/800. */
.our-clients-hero__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-white);
}

/* ----- Category tabs ----- */
/* Mobile: 7 tabs at natural width don't fit one row and Figma gives no
   gap value between them — its own frame clips with padding-left only
   (no padding-right), the same "scroll instead of wrap/shrink" call as
   .news-list, rather than compressing tab labels. Tablet/desktop: the
   7 tabs fill the row evenly instead (flex:1 each, no scroll) — see
   responsive.css. */
.our-clients-tabs {
  background: var(--color-neutral-050);
  padding: 16px 0 0 16px;
  display: flex;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.our-clients-tabs::-webkit-scrollbar {
  display: none;
}

.our-clients-tabs__list {
  position: relative;
  display: flex;
  align-items: flex-end;
  width: 100%;
}

/* flex:1 0 0 fills the row evenly at every breakpoint, same as
   ≧768/≧1200 — no min-width override, so the browser's default
   min-width:auto still applies: combined with white-space:nowrap,
   an item can never shrink smaller than its own label needs, so at
   narrow widths the row overflows (and .our-clients-tabs's own
   overflow-x:auto lets it scroll) instead of wrapping the label. */
.our-clients-tabs__item {
  flex: 1 0 0;
  padding: 8px;
  border: none;
  border-bottom: 1px solid var(--color-neutral-200);
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-label-lg);
  line-height: 1.5;
  color: var(--color-neutral-600);
  transition: color var(--sys-motion-duration) var(--sys-motion-easing),
    border-color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* tab, state=selected (node 9112:33775 / 9112:34870 / 7006:19383) —
   the underline itself now lives on .tabs-indicator (script.js), not
   here, so it can slide between tabs instead of jump-swapping. */
.our-clients-tabs__item.is-active {
  color: var(--color-neutral-800);
}

/* ----- Logo grid ----- */
.our-clients-grid-section {
  background: var(--color-white);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--comp-our-clients-grid-sec-p-top) 16px var(--comp-our-clients-grid-sec-p-bottom);
}

.our-clients-grid-wrap {
  position: relative;
  max-width: var(--container-md);
  width: 100%;
}

/* Flat CSS Grid rather than Figma's own per-row flex wrappers — a
   uniform N-column repeating grid (unlike the homepage's asymmetric
   client-grid) has no structural reason to keep row grouping, and a
   flat grid is what a CMS-fed list of variable length actually needs:
   items reflow on their own, no row bucketing to maintain. Column
   count only changes per breakpoint (4/5/6) — see responsive.css. */
.our-clients-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  column-gap: var(--grid-gutter);
  row-gap: 16px;
}

.our-clients-card {
  background: var(--color-white);
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
}

.our-clients-card[hidden] {
  display: none;
}

/* object-fit: contain keeps the logo's own aspect ratio inside the square
   card — a plain width/height pair here would stretch it whenever the grid
   column (governed by base.css's `img { max-width: 100% }` reset) ends up
   narrower than the declared box, which is what caused the visible
   distortion Tina flagged (2026-09-07). */
.our-clients-card img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* Fade-to-white affordance over the last visible row before "展開更多"
   is pressed — approximates Figma's own bottom gradient ("cover" node:
   transparent -> white at an 85% stop), sized to roughly one card's
   height instead of matching its literal (much taller) px value: that
   node overlays a fixed 6-row demo grid, but this grid's real row
   count is CMS-driven, so a row-relative height is what still makes
   sense once the item count changes. Hidden once expanded, or when
   every item already fits within the visible threshold — see
   script.js. */
.our-clients-cover {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 80px;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, var(--color-white) 85%);
  pointer-events: none;
}

.our-clients-grid-section.is-expanded .our-clients-cover,
.our-clients-grid-section.is-all-visible .our-clients-cover {
  display: none;
}

.our-clients-more {
  display: flex;
  justify-content: center;
  margin-top: 24px;
  width: 100%;
}

.our-clients-grid-section.is-expanded .our-clients-more,
.our-clients-grid-section.is-all-visible .our-clients-more {
  display: none;
}

/*
 * ===== 02-2 Authorized Brand Page =====
 * Verified against three Figma symbols (not their resized instances):
 * "02-2 authorized brand < 768" (9112:19049), "02-2 authorized brand
 * ≧ 768" (9112:19687), "02-2 authorized brand ≧ 1200" (7007:23396) —
 * same file as the homepage/about/our-clients pages
 * (DgTQJuQHck2Ok5UiupLxTI). Breadcrumb ("首頁 > 代理品牌") confirmed
 * directly from these three symbols — PRD had this page's breadcrumb
 * flagged unconfirmed before this pass.
 *
 * Known content gap (see PRD 章節三 02-2 and this session's delivery
 * report — not resolved here, just documented at the source): this
 * page reuses the HOMEPAGE's own 16 logo images (imgLogo1~16.png,
 * same DOM order as index.html's brand-section, including its
 * existing imgLogo7-used-twice/imgLogo12-unused debt from 切版驗收
 * 清單.md) per Tina's explicit instruction, rather than Figma's own
 * 02-2 canvas order. Opening the actual PNG files confirmed both sets
 * are the SAME 16 real brands (iTC/Ubiquiti/Druid Software/ANDREW/
 * CommScope/MonitorApp/DiCon Fiberoptics/Oscilloquartz/Teltonika/
 * TASSTA/EnerSys/NetQuest/6WIND/NEOX Networks/Arrosoft Solutions/
 * technetix), just arranged in a different order — card name text
 * below is read directly off each logo image's own artwork, high
 * confidence. Figma's own card component text is a separate, smaller
 * problem: its description copy was only ever written for 3 of the 16
 * cards (iTC/Ubiquiti/Druid Software) and copy-pasted onto the other
 * 13 unchanged — same copy-paste-and-forgot-to-change pattern as the
 * 06/03/02-3 breadcrumb bug already documented in the PRD. Only the 3
 * cards with real Figma copy (imgLogo1/2/3 = Druid Software/iTC/
 * Ubiquiti) carry a real description below; the other 13 are visible
 * "待補" placeholders rather than invented copy. See the HTML comment
 * above .brand-cards for the full writeup.
 */

.brands-hero {
  height: 180px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--sys-grid-margin);
  background: var(--color-white);
}

.brands-hero__content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

/* en/headline/def/m at <768/≧768 (fs-base), steps to en/headline/md/m
   (fs-18) at ≧1200 — same metrics as .about-hero__eyebrow, and same
   primary/500 color reading (not our-clients-hero's primary/400). */
.brands-hero__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-500);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b then
   headline/6xl/b in responsive.css — identical progression to
   .about-hero__title/.our-clients-hero__title. */
.brands-hero__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
}

/* ----- Card grid -----
   2026-09-08：曾短暫改成 8 個 .brand-cards__row 明確列（每行卡片數
   3/2/3/2/2/2/3/2），Tina 覺得太亂，同日改回這份原始版本。

   Two groups (not Figma's literal 6 row-divs) because the column
   count is genuinely non-uniform only at ≧1200 (main=3col,
   featured=2col — see responsive.css); at <768/≧768 both groups
   render identically (1col/2col), so a flat per-group CSS Grid
   reproduces every breakpoint exactly without needing 6 separate
   row-flex wrappers. The gap below doubles as the seam between the
   two groups, matching Figma where there's no distinct value for
   that transition — just one flat list of row-divs sharing one gap. */
.brand-cards {
  background: var(--color-neutral-050);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-lg);
  padding: var(--comp-brand-cards-sec-p-top) 16px var(--comp-brand-cards-sec-p-bottom);
}

.brand-cards__group {
  display: grid;
  grid-template-columns: 1fr;
  row-gap: var(--sys-layout-sec-gap-lg);
  column-gap: 24px;
  width: 100%;
  max-width: var(--container-sm);
}

.brand-card {
  background: var(--color-white);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 16px;
  width: 100%;
}

.brand-card__logo {
  width: 100%;
  aspect-ratio: 324 / 140;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Tina, 2026-09-09 (third pass): the 19 logo files all happen to be
   exactly 324×140 today (matching this box's own ratio), so cover/
   contain/fill were pixel-identical — no real cropping was ever
   happening despite how it looked. Tina flagged the underlying risk
   directly: img height shouldn't be hard-pinned to 100% at all, since
   that only stays crop-free by coincidence (today's files matching
   the box ratio exactly) — any future logo with a different ratio
   would get cropped under cover/fill. object-fit: contain removes
   that dependency entirely: the image always scales down to whatever
   fits within the box using its own ratio, never cropped, regardless
   of what ratio a future file actually is. */
.brand-card__logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.brand-card__content {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
  width: 100%;
}

/* headline/lg/b at <768 (fs-20); steps to headline/2xl/b (fs-24) at ≧768/≧1200 */
.brand-card__name {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-card-title-def);
  line-height: 1.5;
  color: var(--color-neutral-800);
  text-align: center;
  width: 100%;
  transition: color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* Hover：只有品牌名變色，logo／說明不動（Tina, 2026-09-10 — 代理品牌／
   眾多客戶／經銷據點三份清單共用這個規則；眾多客戶只有 logo 沒有標題，
   所以看不出 hover，不另外做）。primary/500 是暫定值，等 Figma 節點確認。 */
.brand-card:hover .brand-card__name {
  color: var(--color-primary-500);
}

/* body/sm/r at <768 (fs-14); steps to body/def/r (fs-base) at ≧768/≧1200 */
.brand-card__desc {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
  width: 100%;
}

/* ===== 02-3 ESG Page ===== */

/* Hero title bar — same broken-asset situation as .about-hero: Figma's
   own "sec" background image (node 9112:20715, opacity 80%) exports
   fully transparent (get_design_context asset, alpha extrema (0,0)),
   and get_screenshot can't isolate it either (shares the frame with
   nothing separable). Falls back to flat neutral/050 like .about-hero
   until re-exported. */
.esg-hero {
  height: 180px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--sys-grid-margin);
  background: var(--color-neutral-050);
}

.esg-hero__content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

/* en/headline/def/m at <768/≧768, steps to en/headline/md/m at ≧1200 —
   same progression as .about-hero__eyebrow/.brands-hero__eyebrow. */
.esg-hero__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-500);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b then
   headline/6xl/b in responsive.css — same progression as
   .about-hero__title/.brands-hero__title. */
.esg-hero__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
}

/* ----- 供應商行為準則 (supplier code of conduct) ----- */
.esg-conduct {
  background: var(--color-secondary-200);
  display: flex;
  justify-content: center;
  padding: var(--comp-esg-conduct-sec-p-top) 16px var(--comp-esg-conduct-sec-p-bottom);
}

.esg-conduct__container {
  background: var(--color-white);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  max-width: var(--container-md);
  width: 100%;
  padding: 28px 20px;
}

/* Shares --type-sec-title with .section-title (Tina,
   2026-09-08: was its own fixed fs-24 constant across all
   breakpoints, standardized to the same 24->28 step). */
.esg-conduct__title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title);
  line-height: 1.5;
  color: var(--color-neutral-800);
  text-align: center;
  width: 100%;
}

.esg-conduct__group {
  display: flex;
  flex-direction: column;
  width: 100%;
}

/* Figma gives every child (H4 / p / ol) inside a group its own
   pt-16 instead of a group-level gap — including the group's own
   first child, which reads oddly on its own but is what every group
   in the design actually does (WP-editor-generated rich text). */
.esg-conduct__group > * {
  padding-top: 16px;
}

.esg-conduct__heading {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-card-title-def);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

/* body/def/r: fs-base, constant across all 3 breakpoints */
.esg-conduct__text {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
}

.esg-conduct__list {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
  list-style: decimal;
  padding-left: 24px;
}

/* ----- 企業社會責任 (CSR) + 友善職場 (friendly workplace) ----- */
.esg-csr {
  display: flex;
  justify-content: center;
  padding: var(--comp-esg-csr-sec-p-top) 16px var(--comp-esg-csr-sec-p-bottom);
}

.esg-csr__container {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-def);
  max-width: var(--container-md);
  width: 100%;
}

.esg-csr__heading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  text-align: center;
  width: 100%;
}

/* Shares --type-sec-title with .section-title (Tina,
   2026-09-08: was its own fixed fs-24 constant across all
   breakpoints, standardized to the same 24->28 step). */
.esg-csr__title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

.esg-csr__desc {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
  width: 100%;
}

.esg-workplace {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
}

/* Centered heading flanked by two 5px accent bars — same visual
   pattern as .about-flanked-title, but a dedicated ESG class since
   the per-section base(<768) font sizes here are NOT uniform (22 for
   友善職場, 20 for 人才發展/社會公益/環境永續 — confirmed against all
   3 Figma symbols), unlike .about-flanked-title's uniform fs-24 base.
   All 4 converge on fs-24 at ≧768 (responsive.css). */
/* Font-size now shares --type-sec-title with .section-title
   (Tina, 2026-09-08: was fs-20 at <768, per-instance 20/22 split via
   .esg-workplace__title below — standardized to the same 24->28 step
   as .section-title / .about-flanked-title instead of a one-off). */
.esg-flanked-title {
  --flank-color: var(--color-primary-500);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 40px;
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

.esg-flanked-title::before,
.esg-flanked-title::after {
  content: "";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 5px;
  height: 20px;
  background: var(--flank-color);
}

.esg-flanked-title::before { left: 0; }
.esg-flanked-title::after { right: 0; }

/* Used on 社會公益's dark photo-overlay card — white text + lighter
   flank bars, same accent treatment as .about-flanked-title--accent. */
.esg-flanked-title--accent {
  --flank-color: var(--color-primary-400);
  color: var(--color-white);
}

.esg-workplace__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
}

.esg-workplace__text {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
  width: 100%;
  white-space: pre-wrap;
}

/* Figma aspect-ratio 2048/1200 — same photo (img-02-3-01) at all 3
   breakpoints, only the surrounding container width changes. */
.esg-workplace__photo {
  display: block;
  aspect-ratio: 2048 / 1200;
  width: 100%;
  object-fit: cover;
  object-position: bottom;
}

/* 6 numbered benefit cards（Figma 本身零 gap，卡片彼此緊貼，設計上
   的分隔就是那條框線本身 — get_metadata 確認 y 位移完全相接）。
   格線畫法：容器負責上框與左框，每張卡片只畫右框與下框，同一條線
   永遠只有一個來源。不用「每張卡都畫四邊＋負 margin 疊起來」，是
   因為欄寬是 1fr 的小數（952/3=317.33），負 1px 位移在某些欄會被
   小數吃掉，兩條 1px 框線變成並排的 2px 粗線。
   CMS 之後把卡片改成非 3 的倍數時，最後一列右側會缺一段框線，要
   補的話在最後一張卡片加 border-right 的例外規則。
   ≧768 切成 3 欄（responsive.css）。 */
.esg-benefit-cards {
  display: grid;
  grid-template-columns: 1fr;
  width: 100%;
  margin-top: 12px;
  border-top: 1px solid var(--color-neutral-400);
  border-left: 1px solid var(--color-neutral-400);
  box-sizing: border-box;
}

.esg-benefit-card {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  border-right: 1px solid var(--color-neutral-400);
  border-bottom: 1px solid var(--color-neutral-400);
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px;
  width: 100%;
}

/* state=hover (node 7136:20982): 卡片翻成 primary/600 實心底＋白字，
   數字徽章也反白（白底、primary/600 字）；外框維持 neutral/400。
   進場方式跟 .security-card 一樣是「由左往右掃過」（Tina,
   2026-09-10）：底色走 ::before 的 scaleX，不是整塊 background 淡入。
   isolation:isolate 讓 ::before 的 z-index:-1 只作用在這張卡片內，
   不會穿到 section 背景。 */
.esg-benefit-card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-primary-600);
  transform: scaleX(0);
  transform-origin: 0% 50%;
  transition: transform var(--sys-motion-duration) var(--sys-motion-easing);
}

.esg-benefit-card:hover::before {
  transform: scaleX(1);
}

.esg-benefit-card__head {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
}

/* en number badge — Montserrat bold. 24px/fs-base at <768; steps to
   28px/fs-18 at ≧768 (responsive.css) — confirmed different from
   .about-testimony__card-num, which stays 24px/fs-base at every
   breakpoint. */
.esg-benefit-card__num {
  background: var(--color-primary-500);
  color: var(--color-white);
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-en);
  font-weight: 700;
  font-size: var(--type-card-desc-def);
  line-height: 1.5;
  transition: background var(--sys-motion-duration) var(--sys-motion-easing),
    color var(--sys-motion-duration) var(--sys-motion-easing);
}

.esg-benefit-card:hover .esg-benefit-card__num {
  background: var(--color-white);
  color: var(--color-primary-600);
}

/* label/def/b at <768; steps to headline/md/b (fs-18) at ≧768 */
.esg-benefit-card__title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-card-title-def);
  line-height: 1.5;
  color: var(--color-neutral-800);
  flex: 1 0 0;
  min-width: 0;
  transition: color var(--sys-motion-duration) var(--sys-motion-easing);
}

.esg-benefit-card:hover .esg-benefit-card__title {
  color: var(--color-white);
}

.esg-benefit-card__desc {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
  width: 100%;
  transition: color var(--sys-motion-duration) var(--sys-motion-easing);
}

.esg-benefit-card:hover .esg-benefit-card__desc {
  color: var(--color-white);
}

/* ----- 人才發展 (talent development) ----- */
.esg-talent {
  display: flex;
  justify-content: center;
  padding: var(--comp-esg-talent-sec-p-top) 16px var(--comp-esg-talent-sec-p-bottom);
}

.esg-talent__container {
  background: var(--color-white);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
  max-width: var(--container-md);
  width: 100%;
}

.esg-talent__row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--sys-layout-sec-gap-sm);
  width: 100%;
}

/* Figma aspect-ratio 2048/1200 — same as .esg-workplace__photo, kept
   as a separate class since the two sections are unrelated content. */
.esg-talent__photo {
  display: block;
  aspect-ratio: 2048 / 1200;
  width: 100%;
  object-fit: cover;
  object-position: bottom;
}

/* body/def/r: fs-base, constant across all 3 breakpoints */
.esg-talent__text {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
  width: 100%;
}

/* ----- 社會公益 / 環境永續 ----- */
/* Hover interaction (Tina, 2026-09-07): both cards default to a plain
   white background + dark text; hovering a card fades in its own bg
   photo + dark overlay + switches text to white. Same behavior at
   every breakpoint — this replaces an earlier reading of the Figma
   frames as a fixed breakpoint difference (environment losing its
   photo at ≧768), which was actually a default/hover state pair. */
.esg-impact {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  border-top: 1px solid var(--color-neutral-200);
}

.esg-impact__card {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 40px;
  width: 100%;
  background: var(--color-white);
}

.esg-impact__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  transition: opacity var(--sys-motion-duration, 300ms) var(--sys-motion-easing, ease);
}

.esg-impact__card:hover .esg-impact__bg {
  opacity: 1;
}

.esg-impact__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* hover 時在裁切框內放大 120%（Tina 指定的倍率），時間曲線比照
     .news-card__img img。 */
  transition: transform var(--sys-motion-duration) var(--sys-motion-easing);
}

.esg-impact__card:hover .esg-impact__bg img {
  transform: scale(1.2);
}

.esg-impact__overlay {
  position: absolute;
  inset: 0;
  background: var(--color-dark-40a);
}

.esg-impact__container {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sys-layout-sec-gap-def);
  max-width: 560px;
  width: 100%;
}

.esg-impact__desc {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  width: 100%;
}

/* Default (no hover): dark-on-white, same as the rest of the page.
   Hover swaps to white-on-photo once .esg-impact__bg fades in. */
.esg-impact__card--social .esg-flanked-title,
.esg-impact__card--environment .esg-flanked-title {
  --flank-color: var(--color-primary-500);
  color: var(--color-neutral-800);
  transition: color var(--sys-motion-duration, 300ms) var(--sys-motion-easing, ease);
}

.esg-impact__card--social .esg-impact__desc,
.esg-impact__card--environment .esg-impact__desc {
  color: var(--color-neutral-600);
  transition: color var(--sys-motion-duration, 300ms) var(--sys-motion-easing, ease);
}

.esg-impact__card--social:hover .esg-flanked-title,
.esg-impact__card--environment:hover .esg-flanked-title {
  --flank-color: var(--color-primary-400);
  color: var(--color-white);
}

.esg-impact__card--social:hover .esg-impact__desc,
.esg-impact__card--environment:hover .esg-impact__desc {
  color: var(--color-white);
}

/*
 * ===== 03 Ubiquiti Page =====
 * Verified against three Figma symbols (not their resized instances):
 * "03 ubiquiti < 768" (9112:24824), "03 ubiquiti ≧ 768" (9112:28762),
 * "03 ubiquiti" (7056:10219, ≧1200) — same file as the homepage
 * (DgTQJuQHck2Ok5UiupLxTI).
 */

/* ----- Hero (title + top nav tabs) ----- */
/* Figma's hero background here is the SAME known export-failure
   pattern already documented on .about-hero/.esg-hero/.our-clients-hero:
   a light dot-pattern graphic (opacity 80%) plus a masked "Ubiquiti"
   wordmark sit at the top, and a faded product-device illustration
   fills the rest of this 720px-tall frame — none of it has a separate
   layer to export (the asset comes back fully transparent; a
   get_screenshot render shows the illustration but get_metadata finds
   no isolatable node for it, so there's nothing to download as a clean
   asset). Falls back to a flat neutral background, same treatment as
   the other 3 known cases, until Figma re-exports these as real
   layers. Height is a literal 720px in all 3 breakpoints
   (get_metadata confirms this independently at each), kept as-is to
   preserve the space reserved for that illustration once available.
   Side padding (16/24/40) is NOT in Figma at this specific sub-frame
   (title text has no side margin there) — added anyway for text
   safety on narrow phones and to stay consistent with every other
   section's grid margin on this same page. */
.ubiquiti-hero {
  position: relative;
  height: 720px;
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: hidden;
  background: var(--color-neutral-050);
}

/* 整寬白底帶（node 7092:10572）：不透明白色蓋住影片上半部，白底跟著
   viewport 滿寬（不是只有 container 的 1196px）。section 的
   padding-top 移到這裡，白底才會連同標題上方的留白一起覆蓋；高度
   由內容撐出來，不寫死 352px，三個斷點通用。 */
.ubiquiti-hero__top {
  position: relative;
  z-index: 1;
  width: 100%;
  display: flex;
  justify-content: center;
  background: var(--color-white);
  padding: var(--comp-ubiquiti-hero-sec-p-top) var(--sys-grid-margin) 0;
}

/* Video background. 2026-09-10：原本疊了一層 75% 白色 scrim 保證深色
   標題/tab 文字可讀；標題列改到 .ubiquiti-hero__top 的不透明白底帶上
   之後就沒有這個需求了，scrim 依 Tina 指示移除，影片下半部維持原色
   （同 node 7053:4283 下半部沒有蒙版）。 */
.ubiquiti-hero__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.ubiquiti-hero__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ubiquiti-hero__container {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-lg);
  width: 100%;
  max-width: var(--container-max);
}

/* headline/3xl/b at <768 (two lines, manual break); steps to a single
   line at ≧768/≧1200 (responsive.css) — see .ubiquiti-hero__title-break */
.ubiquiti-hero__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
  text-align: center;
}

.ubiquiti-hero__title em {
  font-style: normal;
  color: var(--color-primary-500);
}

/* Mobile-only manual line-break (Figma <768 wraps "免費授權" / "打造
   IT 的未來" onto two lines; ≧768/≧1200 read as one line — see
   responsive.css). */
.ubiquiti-hero__title-break {
  display: inline;
}

/* ----- Shared scroll/fill tabs (hero nav + brand-principles nav) -----
   Fills evenly (flex:1 each) at every breakpoint; falls back to
   horizontal scroll only once the row genuinely can't fit without
   wrapping a label — same mechanism as .our-clients-tabs (see its own
   .our-clients-tabs__item comment for how the fallback works). */
.ubiquiti-tabs {
  position: relative;
  display: flex;
  align-items: flex-end;
  width: 100%;
  max-width: 952px;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.ubiquiti-tabs::-webkit-scrollbar {
  display: none;
}

.ubiquiti-tabs__item {
  flex: 1 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  border: none;
  border-bottom: 1px solid var(--color-neutral-200);
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-label-lg);
  line-height: 1.5;
  color: var(--color-neutral-600);
  transition: color var(--sys-motion-duration) var(--sys-motion-easing),
    border-color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* Underline lives on .tabs-indicator (script.js) — see .our-clients-
   tabs__item.is-active for why. */
.ubiquiti-tabs__item.is-active {
  color: var(--color-neutral-800);
}

/* ----- Category cards (5, fixed — not affected by hero tabs) ----- */
.ubiquiti-categories {
  display: flex;
  justify-content: center;
  padding: var(--comp-ubiquiti-categories-sec-p-top) var(--sys-grid-margin) var(--comp-ubiquiti-categories-sec-p-bottom);
}

.ubiquiti-categories__grid {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
  max-width: var(--container-max);
}

/* Hover：node 7092:10602（size=lg, state=hover）。整張卡底色換成
   neutral/050、「了解更多」換成 primary/600，其餘（icon、標題、說明、
   外框）維持 default——不是站上其他卡片那種「紅色由左往右掃過＋文字
   翻白」的語言。 */
.ubiquiti-category-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 20px;
  background: var(--color-white);
  border: 1px solid var(--color-neutral-200);
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing);
}

.ubiquiti-category-card:hover {
  background: var(--color-neutral-050);
}

.ubiquiti-category-card__icon {
  width: 60px;
  height: 60px;
  object-fit: contain;
}

/* headline/lg/b */
.ubiquiti-category-card__title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-card-title-def);
  line-height: 1.5;
  color: var(--color-neutral-800);
  text-align: center;
}

/* body/def/r */
.ubiquiti-category-card__desc {
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
  text-align: center;
  width: 100%;
}

/* label/def/m；hover 換 button-text/text-color-primary-hover
   （= primary/600 #c91d1d，node 7092:10602 的變數） */
.ubiquiti-category-card__link {
  font-family: var(--font-zh);
  font-weight: 500;
  line-height: 1.25;
  color: var(--color-primary-500);
  transition: color var(--sys-motion-duration) var(--sys-motion-easing);
}

.ubiquiti-category-card:hover .ubiquiti-category-card__link {
  color: var(--color-primary-600);
}

/* ----- "品牌原則" heading ----- */
.ubiquiti-principles-heading {
  display: flex;
  justify-content: center;
  padding: 28px 16px;
}

.ubiquiti-principles-heading__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  max-width: var(--container-max);
  text-align: center;
}

.ubiquiti-principles-heading__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-500);
}

.ubiquiti-principles-heading__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
}

/* ----- "品牌原則" tabs row ----- */
.ubiquiti-principles-tabs-section {
  display: flex;
  justify-content: center;
  padding: 24px 0 0 16px;
}

/* ----- "品牌原則" content panel (title + list + image, tab-switched) ----- */
.ubiquiti-principles-panel {
  display: flex;
  justify-content: center;
  padding: var(--comp-ubiquiti-principles-panel-sec-p-top) var(--sys-grid-margin) var(--comp-ubiquiti-principles-panel-sec-p-bottom);
}

.ubiquiti-principles-panel__container {
  display: flex;
  flex-direction: column;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
  max-width: var(--container-max);
}

.ubiquiti-principles-panel__text {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.ubiquiti-principles-panel__title {
  padding-left: 24px;
  border-left: 6px solid var(--color-primary-500);
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--fs-22);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

.ubiquiti-principles-panel__list {
  list-style: disc;
  padding-left: 21px;
  display: flex;
  flex-direction: column;
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
}

.ubiquiti-principles-panel__image-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 288 / 229;
  overflow: hidden;
}

.ubiquiti-principles-panel__image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ----- "探索更多 Ubiquiti 系列商品" CTA (bg photo + gradient wash) -----
   Same structural pattern as .about-contact. */
.ubiquiti-more {
  position: relative;
  display: flex;
  justify-content: center;
  overflow: hidden;
  padding: var(--comp-ubiquiti-more-sec-p-top) 16px var(--comp-ubiquiti-more-sec-p-bottom);
}

.ubiquiti-more__bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.ubiquiti-more__bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Directional wash matching this component's own Figma gradient (white
   80% at 15% down to transparent at 90%, left to right). */
.ubiquiti-more__gradient {
  position: absolute;
  inset: 0;
  background: linear-gradient(to right, rgba(255, 255, 255, 0.8) 15%, rgba(255, 255, 255, 0) 90%);
}

.ubiquiti-more__container {
  position: relative;
  max-width: 952px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sys-layout-sec-gap-def);
}

.ubiquiti-more__content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  width: 100%;
}

.ubiquiti-more__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-500);
}

.ubiquiti-more__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
  width: 100%;
}

/*
 * ===== 04 News List Page =====
 * Verified against three Figma symbols (not their resized instances):
 * "04 news < 768" (9112:27197), "04 news ≧ 768" (9112:28375), "04 news
 * ≧ 1200" (7008:27493) — same file as the other inner pages
 * (DgTQJuQHck2Ok5UiupLxTI). Side padding is this page's own literal
 * 16/24/40 (no shared token — same precedent as the about/our-clients
 * hero sections' own margin scale).
 *
 * CMS 待接：卡片資料（分類、日期、標題、示意數量）目前寫在 script.js 的
 * NEWS_ARTICLES，之後接 WordPress 只需要把那份資料源換掉，DOM 掛鉤
 * （.news-tabs__item[data-category] / #newsGrid / .news-pagination__item）
 * 不需要跟著改。
 */

/* ----- Hero title bar (plain white bg, no photo — unlike the
   about/our-clients hero, this page's Figma "sec" node has no
   background image or dark overlay) ----- */
.news-hero {
  height: 180px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px 16px;
}

.news-hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

/* en/headline/def/m at <768/≧768 (fs-base), steps to en/headline/md/m
   (fs-18) at ≧1200 — see responsive.css. */
.news-hero__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-500);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b (fs-32,
   fixed 44px line-height) at ≧768, then headline/6xl/b (fs-40, 1.25)
   at ≧1200 — see responsive.css. */
.news-hero__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
}

/* ----- Category tabs ----- */
/* This page's 5 tabs fit flex:1 at every breakpoint per all three
   Figma symbols, but still falls back to horizontal scroll (rather
   than wrapping a label) on a screen too narrow for that — same
   fallback mechanism as .our-clients-tabs / .ubiquiti-tabs. */
.news-tabs {
  display: flex;
  justify-content: center;
  padding: 24px 16px 0;
}

.news-tabs__list {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  max-width: 960px;
  width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.news-tabs__list::-webkit-scrollbar {
  display: none;
}

.news-tabs__item {
  flex: 1 0 0;
  padding: 8px;
  border: none;
  border-bottom: 1px solid var(--color-neutral-200);
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-label-lg);
  line-height: 1.5;
  color: var(--color-neutral-600);
  transition: color var(--sys-motion-duration) var(--sys-motion-easing),
    border-color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* tab, state=selected (node 9112:26875 / 9112:28149 / 7008:26291) —
   underline lives on .tabs-indicator (script.js), not here. */
.news-tabs__item.is-active {
  color: var(--color-neutral-800);
}

/* ----- Card grid ----- */
.news-list-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--comp-news-list-sec-p-top) var(--sys-grid-margin) var(--comp-news-list-sec-p-bottom);
}

.news-list-section__wrap {
  width: 100%;
  max-width: var(--container-md);
  display: flex;
  justify-content: center;
}

/* Flat CSS Grid (not Figma's per-row flex wrappers) — same reasoning
   as .our-clients-grid: a CMS-fed list of variable length just needs
   items to reflow, no row bucketing to maintain. Column count steps
   1→2→3 per breakpoint — see responsive.css. Scoped `.news-grid
   .news-card` override beats the plain `.news-card` rule in
   components.css (2 classes vs 1) regardless of load order, so the
   homepage's fixed-260px/scroll-list card sizing doesn't leak in here. */
.news-grid {
  display: grid;
  grid-template-columns: 1fr;
  row-gap: var(--sys-layout-sec-gap-def);
  width: 100%;
}

.news-grid .news-card {
  width: 100%;
  flex-shrink: 1;
}

/* 列表沒有資料時取代卡片的一行字（04 最新消息／02-1 眾多客戶／
   06 經銷據點共用，見 script.js）。容器維持最小高度，標題與篩選 tab
   照常顯示，使用者才知道可以切到別的分類。
   grid-column / flex-basis 兩種都寫：三個列表容器有的是 grid、有的是
   flex，這條規則要在兩種版面下都撐滿整列。 */
.news-grid__empty,
.list-empty {
  grid-column: 1 / -1;
  flex-basis: 100%;
  width: 100%;
  text-align: center;
  color: var(--color-neutral-600);
  font-family: var(--font-zh);
  font-size: var(--fs-base);
  line-height: 1.5;
  padding: 40px 0;
  margin: 0;
}

/* ----- Pagination ----- */
.news-pagination-section {
  display: flex;
  justify-content: center;
  padding: var(--comp-news-pagination-sec-p-top) var(--sys-grid-margin) var(--comp-news-pagination-sec-p-bottom);
}

.news-pagination {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Numbered-page buttons are generated into this wrapper by script.js
   (count/labels differ by breakpoint — see getNewsPaginationStops);
   display:contents lets them sit as direct flex items of
   .news-pagination so they share its gap instead of nesting a second
   flex row. */
.news-pagination__pages {
  display: contents;
}

.news-pagination__arrow {
  width: 40px;
  height: 40px;
  border-radius: 999px;
  border: none;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  padding: 0;
}

.news-pagination__arrow[disabled] {
  opacity: 0.4;
  cursor: default;
  pointer-events: none;
}

.news-pagination__arrow img {
  width: 24px;
  height: 24px;
}

.news-pagination__item,
.news-pagination__ellipsis {
  min-width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  border-radius: 999px;
  border: none;
  background: var(--color-white);
  font-family: var(--font-zh);
  font-weight: 400;
  font-size: var(--type-label-def);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

.news-pagination__item {
  cursor: pointer;
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing),
    color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* pagination-item, state=active (node 7060:10791) */
.news-pagination__item.is-active {
  background: var(--color-neutral-800);
  color: var(--color-white);
}

/*
 * ===== 04-1 News Detail Page =====
 * Verified against three Figma symbols (not their resized instances):
 * "04-1 news-details < 768" (9112:30770), "04-1 news-details ≧ 768"
 * (9112:31783), "04-1 news-details" ≧1200 (4128:14146) — same file as
 * the other inner pages. Side padding is 16px at <768, 40px from
 * ≧768 up (no intermediate 24px step here, unlike the 04 list page
 * above — confirmed identical at both ≧768 and ≧1200 symbols).
 *
 * Figma 疑似重複：both the ≧768 and ≧1200 symbols repeat the exact same
 * breadcrumb section a second time, directly above the footer (after
 * "相關文章"), in addition to the one right under the navbar. No other
 * page in this project repeats its breadcrumb — this reads as the
 * same kind of copy/paste artifact already logged elsewhere in the
 * PRD (麵包屑 mis-copies on 06/03/02-3), so only the top breadcrumb
 * (next to the navbar, matching every other page's convention) is
 * implemented. Flagged for Tina rather than assumed.
 */

/* ----- Article head ----- */
.news-detail-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
  padding: var(--comp-news-detail-head-sec-p-top) var(--sys-grid-margin) 0;
}

.news-detail-head__container {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  max-width: var(--container-md);
}

/* Mobile: tail (date+share) sits ABOVE the category tag (column,
   order swapped via `order`) — a genuine Figma structural difference
   at <768, not a mistake. ≧768 resets to a row with the tag on the
   left, tail on the right — see responsive.css. */
.news-detail-head__info {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
}

.news-detail-head__tag {
  order: 2;
  align-self: flex-start;
  background: var(--color-primary-500);
  color: var(--color-white);
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-label-def);
  line-height: 1.5;
  padding: 4px 12px;
}

.news-detail-head__tail {
  order: 1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  width: 100%;
}

/* label/def/r — fs-14 at <768, fs-base(16px) at ≧768/≧1200 (responsive.css) */
.news-detail-head__date {
  flex-shrink: 0;
  padding-right: 12px;
  border-right: 2px solid var(--color-neutral-400);
  color: var(--color-neutral-400);
  font-family: var(--font-zh);
  line-height: 1.5;
  text-align: right;
  white-space: nowrap;
}

.news-detail-head__share {
  display: flex;
  align-items: center;
  gap: 12px;
}

.news-detail-head__share-label {
  color: var(--color-neutral-400);
  font-family: var(--font-zh);
  line-height: 1.5;
  white-space: nowrap;
}

.news-detail-head__share-buttons {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* icon-button/outline, color=primary (node 4113:18317) */
.news-detail-head__share-btn {
  width: 40px;
  height: 40px;
  border-radius: 999px;
  border: 1px solid var(--color-primary-500);
  background: var(--color-white);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color var(--sys-motion-duration) var(--sys-motion-easing);
}

.news-detail-head__share-btn:hover {
  background: var(--color-primary-050);
}

/* Brief visual confirmation after the native-share fallback copies the
   link to the clipboard (see script.js) — mirrors the hover tint. */
.news-detail-head__share-btn.is-copied {
  background: var(--color-primary-050);
}

.news-detail-head__share-btn img {
  width: 24px;
  height: 24px;
}

/* headline/3xl/b (fs-28) at <768, steps to headline/4xl/b (fs-32,
   fixed 44px) at ≧768, headline/6xl/b (fs-40, 1.25) at ≧1200 —
   same progression as .news-hero__title above. */
.news-detail-head__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
  width: 100%;
}

.news-detail-head__cover {
  width: 100%;
  max-width: var(--container-md);
  aspect-ratio: 1920 / 1080;
  overflow: hidden;
}

.news-detail-head__cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ----- Article content (WordPress editor blocks) ----- */
.news-detail-content {
  display: flex;
  justify-content: center;
  padding: var(--comp-news-detail-content-sec-p-top) var(--sys-grid-margin) var(--comp-news-detail-content-sec-p-bottom);
}

.news-detail-content__container {
  width: 100%;
  max-width: var(--container-md);
}

/* Block-to-block spacing (2026-09-11, Tina): every WP editor block
   type below gets its own explicit margin-top instead of one shared
   catch-all rule, so each type's spacing can be tuned independently.
   Real Gutenberg output doesn't wrap blocks in a `.wp-block` div —
   each block type's own markup (h1-h6/p/ul/ol/blockquote/figure/div)
   sits directly under the content container, so `:first-child` below
   still correctly zeroes out whichever block happens to be first. */
.news-detail-content__container > *:first-child {
  margin-top: 0;
}

.wp-block-heading {
  margin-top: 12px;
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-neutral-800);
  line-height: 1.5;
}

/* headline/6xl/b: fs-40 desktop (own fixed line-height, not the 1.5
   convention above) — matches a real <h1 class="wp-block-heading">
   and this template's SEO-safe div[role="heading"][aria-level="2"]
   substitute (only one real <h1> per page, see news-detail.html). */
h1.wp-block-heading,
[role="heading"][aria-level="2"].wp-block-heading {
  font-size: var(--fs-24);
}

h2.wp-block-heading {
  font-size: var(--fs-22);
}

h3.wp-block-heading {
  font-size: var(--fs-20);
}

h4.wp-block-heading {
  font-size: var(--fs-18);
}

h5.wp-block-heading {
  font-size: var(--fs-base);
}

h6.wp-block-heading {
  font-size: var(--fs-14);
}

/* body/def/r (mobile: fs-14, per Figma's own mobile-only downgrade —
   ≧768/≧1200 step to fs-base/16px, see responsive.css). Scoped to
   direct-child <p> only, so it doesn't also match the <p> nested
   inside .wp-block-quote/.wp-block-pullquote below. */
.news-detail-content__container > p {
  margin-top: 8px;
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-600);
}

ul.wp-block-list,
ol.wp-block-list {
  margin-top: 16px;
  font-family: var(--font-zh);
  line-height: 1.5;
  padding-left: 21px;
}

:is(ul, ol).wp-block-list + :is(ul, ol).wp-block-list {
  margin-top: 8px;
}

/* WP editor / Unordered List (node 4112:19523): neutral/800 */
ul.wp-block-list {
  color: var(--color-neutral-800);
}

ul.wp-block-list li {
  list-style: disc;
}

/* WP editor / Ordered List (node 7008:28761): neutral/600 — a real
   Figma color difference from the unordered list, not an oversight. */
ol.wp-block-list {
  color: var(--color-neutral-600);
}

ol.wp-block-list li {
  list-style: decimal;
}

/* WP editor / Quote (node 7008:28773): left accent bar + indented text */
.wp-block-quote {
  margin-top: 12px;
  padding: 0;
  display: flex;
  gap: 16px;
  align-items: stretch;
  border: none;
}

.wp-block-quote::before {
  content: "";
  flex-shrink: 0;
  width: 4px;
  background: var(--color-primary-500);
}

.wp-block-quote p {
  padding: 16px 24px 16px 0;
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--fs-14);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

/* WP editor / Pullquote (node 7008:28785): fixed sizing at every
   breakpoint (Figma gives this component no responsive variant) —
   only its container width changes (100% at <768, 282px fixed from
   ≧768 up, see responsive.css). Real Gutenberg pullquote markup is
   figure.wp-block-pullquote > blockquote > p + cite. */
.wp-block-pullquote {
  margin-top: 12px;
  width: 100%;
}

.wp-block-pullquote blockquote {
  margin: 0;
  background: var(--color-secondary-050);
  color: var(--color-neutral-800);
  text-align: center;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.wp-block-pullquote cite {
  font-family: var(--font-zh);
  font-style: normal;
  font-weight: 400;
  font-size: var(--fs-13);
  line-height: 1.5;
  order: -1;
}

.wp-block-pullquote blockquote p {
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--fs-base);
  line-height: 24px;
}

/* WP editor / Table (node 7008:28803): fixed 354px at ≧768/≧1200
   (Figma gives this component no responsive variant beyond mobile),
   full width at <768 — see responsive.css. Real Gutenberg table
   markup is figure.wp-block-table > table. */
.wp-block-table {
  margin-top: 16px;
  width: 100%;
}

.wp-block-table table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--color-primary-100);
  font-family: var(--font-zh);
}

.wp-block-table th,
.wp-block-table td {
  border-bottom: 1px solid var(--color-neutral-150);
  border-right: 1px solid var(--color-neutral-150);
  padding: 12px 16px;
  text-align: left;
  font-weight: 400;
  font-size: var(--fs-14);
  line-height: 1.5;
}

.wp-block-table th:last-child,
.wp-block-table td:last-child {
  border-right: none;
}

/* Table, type=primary (node 7008:28797): header row */
.wp-block-table th {
  background: var(--color-neutral-800);
  color: var(--color-white);
  font-weight: 700;
}

/* Table, type=light (node 7008:28799): alternating data row */
.wp-block-table tr:nth-child(even) td {
  background: var(--color-secondary-050);
}

/* ----- Two-column image + caption ----- */
/* Real Gutenberg markup: div.wp-block-columns > div.wp-block-column >
   figure.wp-block-image > img + figcaption.wp-element-caption. */
.wp-block-columns {
  margin-top: 16px;
  display: flex;
  gap: 16px;
  width: 100%;
}

.wp-block-column {
  flex: 1 1 0;
  min-width: 0;
}

.wp-block-column .wp-block-image img {
  width: 100%;
  aspect-ratio: 468 / 263;
  object-fit: cover;
}

.wp-element-caption {
  padding-top: 12px;
  font-family: var(--font-zh);
  font-weight: 400;
  font-size: var(--fs-14);
  line-height: 1.5;
  color: var(--color-neutral-600);
}

/* ----- Related articles ----- */
.news-related {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-def);
  padding: var(--comp-news-related-sec-p-top) var(--sys-grid-margin) var(--comp-news-related-sec-p-bottom);
}

.news-related__top {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  max-width: 1196px;
  text-align: center;
}

.news-related__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  font-size: var(--type-sec-eyebrow);
  line-height: 1.5;
  color: var(--color-primary-500);
}

.news-related__title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

/* Mobile: reuse the homepage's own .news-list (fixed-260px scroll-snap
   card row) verbatim — this section's Figma <768 symbol lays out the
   3 related cards exactly the same way as the homepage's news-section.
   ≧768/≧1200 switch to a fixed-flex row (no scroll) — see
   responsive.css. */
.news-related__list {
  max-width: 1196px;
}

/*
 * ===== 06 Dealership Page =====
 * Verified against three Figma symbols (not their resized instances):
 * "06 dealership < 768" (9112:37057), "06 dealership ≧ 768"
 * (9112:38264), "06 dealership ≧ 1200" (7006:22713) — same file as the
 * other inner pages (DgTQJuQHck2Ok5UiupLxTI). Side padding is this
 * page's own literal 16/24/40 (no shared token — same precedent as
 * 03 Ubiquiti / 04 News).
 *
 * CMS 待接：卡片資料（據點名稱）目前寫在 script.js 的 DEALERSHIP_STORES，
 * 之後接 WordPress 只需要把那份資料源換掉，DOM 掛鉤
 * (.dealership-tabs__item[data-region] / #dealershipGrid) 不需要跟著改。
 */

/* ----- Hero title bar (bg photo + dark overlay — same structural
   pattern as .ubiquiti-more__bg/__gradient below). Height is a flat
   200px across all three Figma symbols, no responsive override
   needed. ----- */
.dealership-hero {
  position: relative;
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 0 var(--sys-grid-margin);
}

.dealership-hero__bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.dealership-hero__bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.dealership-hero__overlay {
  position: absolute;
  inset: 0;
  background: var(--color-dark-40a);
}

.dealership-hero__content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

/* en/headline/def/m at <768/≧768 (fs-base), steps to en/headline/md/m
   (fs-18) at ≧1200 — same stepping as .news-hero__eyebrow. */
.dealership-hero__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-400);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b (fs-32, fixed
   44px line-height) at ≧768, then headline/6xl/b (fs-40, 1.25) at
   ≧1200 — see responsive.css. */
.dealership-hero__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-white);
}

/* ----- Brand banner (iTC logo + 前往線上商店 CTA) -----
   <768: logo stacks above the CTA panel. ≧768/≧1200: side by side —
   only the box's own max-width and horizontal page padding change
   between those two (identical structure otherwise), so both share one
   ≧768 override block in responsive.css. */
.dealership-banner {
  background: var(--color-white);
  display: flex;
  justify-content: center;
  padding: var(--comp-dealership-banner-sec-p-top) var(--sys-grid-margin) var(--comp-dealership-banner-sec-p-bottom);
}

/* Now the actual <a> (whole box is clickable, not just .dealership-
   banner__cta) — text-decoration reset since it's a real link now. */
.dealership-banner__box {
  width: 100%;
  max-width: var(--container-sm);
  background: var(--color-secondary-050);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 8px 0;
  text-decoration: none;
}

.dealership-banner__logo-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 464 / 160;
}

.dealership-banner__logo-inner {
  position: absolute;
  inset: 15.9% 16.45% 16.05% 16.45%;
}

.dealership-banner__logo-inner img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
}

.dealership-banner__cta {
  width: 100%;
  display: flex;
  align-items: flex-end;
  gap: 32px;
  padding: 0 24px 16px;
  background: var(--color-light-20a);
  backdrop-filter: blur(12px);
}

.dealership-banner__cta-text {
  flex: 1 0 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* en/headline/def/m at <768 (fs-14); steps to fs-base at ≧768/≧1200 */
.dealership-banner__cta-eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-500);
}

/* headline/2xl/b (fs-24) at <768; steps to headline/4xl/b (fs-32, fixed
   44px line-height) at ≧768/≧1200 */
.dealership-banner__cta-title {
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title-hero);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

/* Text/icon "roll" reveal on .dealership-banner__box hover (Tina):
   each roll wrapper holds the real line + an aria-hidden duplicate.
   The duplicate is absolutely positioned one line below (top:100%) so
   the wrapper's own height comes only from the real line in normal
   flow — no hardcoded line-height needed to match this component's
   two different text styles (eyebrow/title) across breakpoints.
   translateY(-100%) on hover moves each child up by exactly its own
   height, so the duplicate lands precisely where the real line was. */
.dealership-banner__cta-roll {
  position: relative;
  display: block;
  overflow: hidden;
}

.dealership-banner__cta-roll > * {
  display: block;
  transition: transform var(--sys-motion-duration) var(--sys-motion-easing);
}

.dealership-banner__cta-roll > *:last-child {
  position: absolute;
  left: 0;
  top: 100%;
}

.dealership-banner__box:hover .dealership-banner__cta-roll > * {
  transform: translateY(-100%);
}

.dealership-banner__cta-icon-roll {
  flex-shrink: 0;
}

/* Icon fill now follows currentColor (inline SVG) so it always matches
   .dealership-banner__cta-title's color instead of drifting out of
   sync with a separately-recolored asset. */
.dealership-banner__cta-icon {
  width: 40px;
  height: 40px;
  color: var(--color-neutral-800);
}

/* ----- Region tabs (北區經銷／中區經銷／南區經銷) -----
   Same "flex:1 each" fill pattern as .news-tabs — 3 tabs fit evenly at
   every breakpoint per all three Figma symbols (mobile: 96px × 3 =
   288px content width exactly) — with the same horizontal-scroll
   fallback for a screen too narrow for that. */
.dealership-tabs-section {
  background: var(--color-neutral-050);
  display: flex;
  justify-content: center;
  padding: 16px 16px 0;
}

.dealership-tabs {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  max-width: 1196px;
  width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.dealership-tabs::-webkit-scrollbar {
  display: none;
}

.dealership-tabs__item {
  flex: 1 0 0;
  padding: 8px;
  border: none;
  border-bottom: 1px solid var(--color-neutral-200);
  background: transparent;
  cursor: pointer;
  white-space: nowrap;
  text-align: center;
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-label-lg);
  line-height: 1.5;
  color: var(--color-neutral-600);
  transition: color var(--sys-motion-duration) var(--sys-motion-easing),
    border-color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* tab, state=selected (node 9112:36675 / 9112:38023 / 7006:22030) —
   underline lives on .tabs-indicator (script.js), not here. */
.dealership-tabs__item.is-active {
  color: var(--color-neutral-800);
}

/* ----- Store grid -----
   Flat CSS Grid rather than Figma's own per-row flex wrappers — same
   rationale as .our-clients-grid: a CMS-fed list of variable length
   needs items to reflow on their own, no row bucketing to maintain.
   Column count is 3 at <768, 4 at ≧768/≧1200 (Figma's own <768 symbol
   only demos 12 of the 16 placeholder cards — 4 rows × 3 — but the
   real count is CMS-driven per PRD 章節三/七, so this just renders
   however many script.js gives it). */
.dealership-stores {
  background: var(--color-white);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--comp-dealership-stores-sec-p-top) var(--sys-grid-margin) var(--comp-dealership-stores-sec-p-bottom);
}

.dealership-stores__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 16px;
  row-gap: var(--sys-layout-sec-gap-sm);
  width: 100%;
  max-width: 1196px;
}

.dealership-store-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.dealership-store-card__icon-box {
  width: 100%;
  height: 90px;
  background: var(--color-white);
  display: flex;
  align-items: center;
  justify-content: center;
}

.dealership-store-card__icon {
  width: 60px;
  height: 60px;
  object-fit: contain;
}

/* label/sm/m (fs-14) at <768; steps to label/def/m (fs-base) at ≧768,
   then label/lg/m (fs-20) at ≧1200 */
.dealership-store-card__name {
  width: 100%;
  text-align: center;
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-card-title-sm);
  line-height: 1.25;
  color: var(--color-neutral-800);
  transition: color var(--sys-motion-duration) var(--sys-motion-easing);
}

/* Hover：只有據點名變色，logo 不動（Tina, 2026-09-10 — 同 .brand-card
   的規則）。只有渲染成 <a> 的卡片會有 hover；沒有 url 的仍是 <article>，
   維持非互動外觀。primary/500 是暫定值，等 Figma 節點確認。 */
a.dealership-store-card:hover .dealership-store-card__name {
  color: var(--color-primary-500);
}

/* ----- 成為我們的經銷夥伴 (dealer sign-up form, bg secondary/200) -----
   Field styles themselves live in components.css (.form-field /
   .form-input / .form-textarea / .form-radio-*) — shared with 07
   聯絡我們's future form. Only this section's own layout (row/panel
   sizing) is page-specific. */
.dealer-form-section {
  background: var(--color-secondary-200);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sys-layout-sec-gap-sm);
  padding: var(--comp-dealer-form-sec-p-top) var(--sys-grid-margin) var(--comp-dealer-form-sec-p-bottom);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b (fs-32, fixed
   44px line-height) at ≧768, then headline/6xl/b (fs-40, 1.25) at
   ≧1200 */
.dealer-form-section__title {
  width: 100%;
  text-align: center;
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title-hero);
  color: var(--color-neutral-800);
}

.dealer-form-section__panel-wrap {
  width: 100%;
  max-width: 642px;
}

/* 白底盒子在 panel 上，不在 form 上：form 與感謝畫面互斥（一定有一個
   帶 [hidden]），底色與 padding 放在共同的外層才不會跟著一起消失。
   跟 07 的 .contact-form-panel 同一套結構。 */
.dealer-form-panel {
  width: 100%;
  background: var(--color-white);
  padding: 20px;
  /* 撐住表單與感謝畫面共用的這個盒子：兩塊高度差 230px 左右，不鎖
     高度的話一送出整頁會往上跳。內容用 flex 置中，感謝畫面才不會黏
     在盒子頂端。<768 為 480px，≥768 起 540px（responsive.css）。 */
  min-height: 480px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.dealer-form {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sys-layout-sec-gap-def);
}

/* 無條件的 display:flex 會贏過 UA 的 [hidden]{display:none}，切換時
   兩塊會同時出現，所以要補這條。 */
.dealer-form[hidden] {
  display: none;
}

.dealer-form__row {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

.dealer-form__row .form-field {
  flex: 1 0 0;
}

.dealer-form__submit {
  width: 100%;
}

/* =====================================================================
 * 07 聯絡我們 (contact.html)
 * Figma: symbol "07 contact us" (≧1200, node 7006:21493) / "07 contact
 * us ≧ 768" (9112:41965) / "07 contact us < 768" (9112:40377), plus the
 * standalone "from-thank-you" frames 7006:21324 (desktop) / 9112:40992
 * (mobile) that sit on the same canvas but outside those three symbols.
 * ===================================================================== */

/* ----- Hero title bar — same bg photo + dark overlay + eyebrow/title
   structure as .dealership-hero, just page-specific copy/asset, so kept
   as its own block per this codebase's one-hero-class-per-page
   convention rather than reusing .dealership-hero's class names. ----- */
.contact-hero {
  position: relative;
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 0 var(--sys-grid-margin);
}

.contact-hero__bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.contact-hero__bg img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.contact-hero__overlay {
  position: absolute;
  inset: 0;
  background: var(--color-dark-40a);
}

.contact-hero__content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

/* en/headline/def/m at <768/≧768 (fs-base), steps to en/headline/md/m
   (fs-18) at ≧1200 — same stepping as .dealership-hero__eyebrow. */
.contact-hero__eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  line-height: 1.5;
  color: var(--color-primary-400);
}

/* headline/3xl/b (fs-28) at <768; steps to headline/4xl/b (fs-32, fixed
   44px line-height) at ≧768, then headline/6xl/b (fs-40, 1.25) at
   ≧1200 — see responsive.css. */
.contact-hero__title {
  font-family: var(--font-zh);
  font-weight: 700;
  color: var(--color-white);
}

/* ----- Company info + Google Map placeholder -----
   <768: info block stacks above the map box (column). ≧768/≧1200: side
   by side (row) — the map's own self-stretch fills whatever height the
   info column ends up needing, no fixed height on either side. */
.contact-info-section {
  display: flex;
  justify-content: center;
  padding: var(--comp-contact-info-sec-p-top) var(--sys-grid-margin) var(--comp-contact-info-sec-p-bottom);
}

.contact-info-section__container {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
  max-width: var(--container-sm);
  margin: 0 auto;
}

.contact-info {
  display: flex;
  flex-direction: column;
  gap: 20px;
  width: 100%;
  min-width: 0;
}

/* Heading + address list + "無實體商品" note — this inner gap is a flat
   16px at every breakpoint per all three Figma symbols (unlike the
   outer .contact-info gap and the .contact-info__list gap below, which
   both step at ≧768). */
.contact-info__primary {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

/* fs-22 at <768, steps to fs-28 at ≧768/≧1200 (confirmed against both
   the ≧768 and ≧1200 get_design_context symbols — same 28px value on
   both, not a guess). */
.contact-info__heading {
  width: 100%;
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title-sm);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

/* Reused for both the 公司電話/客服電話/公司地址 list (inside
   .contact-info__primary) and the 業務信箱/售後信箱/服務時段 list
   (its own direct .contact-info child, no heading) — identical gap
   numbers for both in every Figma symbol checked. */
.contact-info__list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
}

.contact-info__row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  width: 100%;
}

.contact-info__label {
  flex-shrink: 0;
  font-family: var(--font-zh);
  font-weight: 700;
  line-height: 1.5;
  color: var(--color-neutral-800);
  white-space: nowrap;
}

.contact-info__value {
  flex: 1 0 0;
  min-width: 0;
  font-family: var(--font-zh);
  font-weight: 400;
  line-height: 1.5;
  color: var(--color-neutral-800);
}

/* 「無實體商品展示」提示 */
.contact-info__note {
  width: 100%;
  font-family: var(--font-zh);
  font-weight: 400;
  font-size: var(--type-body-sm);
  line-height: 1.25;
  color: var(--color-neutral-400);
}

/* 2026-09-09 起改為正式 Google Map 嵌入（原本是 Figma 佔位灰底＋文字，
   見 PRD 章節三 07）。容器尺寸維持原本的 aspect-ratio，iframe 直接撐滿。 */
.contact-map {
  overflow: hidden;
  width: 100%;
  aspect-ratio: 464 / 287;
  background: var(--color-neutral-050);
}

.contact-map iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ----- 若有網路需求...（B2B 報價需求表單） -----
   Field styles themselves live in components.css (.form-field /
   .form-input / .form-textarea / .form-radio-* / .form-checkbox-* /
   .form-select-* / .form-upload*) — shared with 06 成為經銷商 where
   applicable. Only this section's own layout (row/panel sizing) is
   page-specific, same split as .dealer-form-section above. */
.contact-form-section {
  background: var(--color-secondary-200);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--comp-contact-form-sec-p-top) var(--sys-grid-margin) var(--comp-contact-form-sec-p-bottom);
}

.contact-form-section__panel-wrap {
  width: 100%;
  max-width: 642px;
}

/* White box itself — padding only. No gap here: .contact-form and
   .contact-form__thankyou are mutually exclusive (one always [hidden]),
   each carrying its own gap so the visible one lays out correctly on
   its own. */
.contact-form-panel {
  width: 100%;
  background: var(--color-white);
  padding: 20px;
  /* 同 .dealer-form-panel：鎖最小高度＋內容垂直置中，送出後換成感謝
     畫面時盒子不會塌掉。<768 480px，≥768 起 540px。 */
  min-height: 480px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.contact-form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
}

/* Same [hidden] vs unconditional `display: flex` clash as
   .contact-form__thankyou[hidden] below — script.js toggles this form's
   own [hidden] attribute back on when "返回表單" is clicked. */
.contact-form[hidden] {
  display: none;
}

/* body/md/r: fs-18, fixed 24px line-height in the Figma style itself
   (not the site's usual 1.5 ratio — 24/18 isn't a clean multiple), same
   exception as a display-number's line-height:1 elsewhere on the site.
   Base below is the <768 value (fs-14, ratio 1.5); responsive.css steps
   it to the named style's actual fs-18/24px at ≧768. */
.contact-form__intro,
.wpforms-description {
  width: 100%;
  text-align: center;
  font-family: var(--font-zh);
  font-weight: 400;
  font-size: var(--fs-14);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

.contact-form__content {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

.contact-form__row {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

.contact-form__row .form-field {
  flex: 1 0 0;
}

.contact-form__submit {
  width: 100%;
}

/* ----- from-thank-you (post-submit state, Figma nodes 7006:21324 /
   9112:40992) — swapped in for .contact-form via script.js on submit;
   no ≧768-specific Figma frame was given for this state (only 桌機/
   手機), so the ≧768 override below reuses the ≧1200 frame's numbers
   for both tablet and desktop, same as this page's own panel-wrap width
   steps collapsing two breakpoints where Figma only gave one set of
   extra numbers. ----- */
.contact-form__thankyou,
.dealer-form__thankyou {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--sys-layout-sec-gap-def);
  width: 100%;
}

/* The [hidden] attribute is how script.js toggles this panel vs
   .contact-form — without this override, the unconditional
   `display: flex` above would beat the UA stylesheet's `[hidden] {
   display: none }` and show both panels at once. */
.contact-form__thankyou[hidden],
.dealer-form__thankyou[hidden] {
  display: none;
}

.contact-form__thankyou-icon,
.dealer-form__thankyou-icon {
  width: 80px;
  height: 56px;
}

.contact-form__thankyou-text,
.dealer-form__thankyou-text {
  width: 100%;
  font-family: var(--font-zh);
  font-weight: 400;
  font-size: var(--fs-base);
  line-height: 1.5;
  color: var(--color-neutral-800);
}

.contact-form__thankyou-btn,
.dealer-form__thankyou-btn {
  width: 156px;
}
