/* ===== Design Tokens & Base ===== */

:root {
  --color-white: #ffffff;
  --color-neutral-050: #f2f2f2;
  --color-neutral-100: #e5e5e5;
  --color-neutral-150: #d8d8d8;
  --color-neutral-200: #cccccc;
  /* color/neutral/400 — added for 02-3 ESG page's info-card border
     (get_design_context, node 7007:24168 etc). Not used before this page. */
  --color-neutral-400: #999999;
  --color-neutral-600: #666666;
  --color-neutral-800: #333333;
  --color-neutral-900: #1a1a1a;

  --color-primary-050: #ffebeb;
  --color-primary-400: #f53d3d;
  --color-primary-500: #d92626;
  --color-primary-600: #c91d1d;

  /* color/secondary/100 — added for 02 about page's testimonial-quote
     card background (get_variable_defs, node 4024:14066). Not used by
     the homepage. */
  --color-secondary-100: #e1edf5;

  /* color/secondary/200 — added for 02-3 ESG page's 供應商行為準則
     section background (get_design_context, node 7007:23624). */
  --color-secondary-200: #cce0eb;

  /* color/secondary/050 — added for 04-1 news-detail page's WP editor
     Pullquote/Table light-row background (get_design_context, node
     7008:28785 / 7008:28811). Not used before this page. */
  --color-secondary-050: #e9f1f7;

  /* color/primary/100 — added for 04-1 news-detail page's WP editor
     Table border color (get_design_context, node 7008:28804). */
  --color-primary-100: #bdd5ff;

  /* color/neutral/400 already exists above (02-3) — reused as-is for
     04-1's article-head date/share label color. */

  --color-dark-40a: rgba(0, 0, 0, 0.4);
  --color-dark-60a: rgba(0, 0, 0, 0.6);
  --color-dark-80a: rgba(0, 0, 0, 0.8);

  /* color/light/20a — added for 06 dealership page's banner CTA panel
     (get_design_context, node 7060:11604 / 9112:38016 / 9112:36668).
     Not used before this page. */
  --color-light-20a: rgba(255, 255, 255, 0.2);

  --font-zh: "Noto Sans TC", sans-serif;
  --font-en: "Montserrat", sans-serif;

  --fs-13: 13px;
  --fs-14: 14px;
  --fs-base: 16px;
  --fs-18: 18px;
  --fs-20: 20px;
  --fs-22: 22px;
  --fs-24: 24px;
  --fs-28: 28px;
  --fs-32: 32px;
  /* fs/36 — added for 04-1 news-detail page's WP editor H2 and the
     "相關文章" heading at ≧768/≧1200 (get_design_context, node
     7008:28726 / 7008:29190). Not used before this page. */
  --fs-36: 36px;
  --fs-40: 40px;
  --fs-48: 48px;
  --fs-56: 56px;
  --fs-huge: 80px;
  --fs-96: 96px;

  /* lh/{value} — unitless line-height primitives, same tier as --fs-*
     (Tina, 2026-09-08). Only the two values reused by the sys/type
     typography tokens below are here; one-off line-heights (e.g. a
     fixed 44px) stay as literal values on their own rule. */
  --lh-sm: 1.25;
  --lh-def: 1.5;

  /* ls/def — single letter-spacing value used everywhere non-zero
     tracking appears on this site (Tina, 2026-09-08: previously split
     between 2% and 4% depending on selector, standardized to one
     value). Zero/"normal" tracking stays as the literal `normal`, not
     this token — this token IS the "has tracking" value. */
  --ls-def: 0.02em;

  --button-height: 40px;
  --button-padding-x: 16px;
  --button-gap: 8px;
  --button-radius: 0;
  --button-min-width: 156px;

  --grid-gutter: 24px;
  --grid-gutter-sm: 16px;
  --container-max: 1196px;
  --container-md: 642px;
  --container-sm: 480px; /* 2026-09-09: was already a repeated literal
    in 3 selectors (brand-cards__group, dealership-banner__box,
    contact-info-section__container) before Tina asked for a 4th
    (stats-section .container) — promoted to a token instead of adding
    another one-off literal. */

  /* space/{n} — raw spacing primitives (Tina, 2026-09-09). Every literal
     px value used across the --sys-layout-* families below (sec/inset/gap)
     and their >=768/>=1200 redefinitions in responsive.css. Named by the
     literal number since this IS the Reference tier — the "don't name a
     token after its literal value" rule only applies to System/Component
     tokens, which is why --sys-layout-sec-sm below points at --space-24
     instead of being named --sys-layout-sec-24. */
  --space-16: 16px;
  --space-24: 24px;
  --space-28: 28px;
  --space-32: 32px;
  --space-36: 36px;
  --space-40: 40px;
  --space-44: 44px;
  --space-48: 48px;
  --space-52: 52px;
  --space-56: 56px;
  --space-60: 60px;
  --space-64: 64px;
  --space-68: 68px;
  --space-76: 76px;
  --space-80: 80px;
  --space-88: 88px;
  --space-100: 100px;
  --space-120: 120px;

  /* sys/grid-margin — horizontal section padding (Tina, 2026-09-09).
     Separate scale from --sys-layout-sec-* (which is vertical only).
     16->24->40 is the dominant left/right padding pattern site-wide.
     Originally created as --sys-layout-inset-def since --grid-margin
     (unused) / --grid-margin-sm (flat 20px, no RWD scaling) didn't
     match this progression; renamed to --sys-grid-margin and absorbed
     --grid-margin-sm's 7 usages once Tina confirmed they're the same
     concept and should scale 16->24->40 like the rest of the site
     instead of staying flat at 20px — both --grid-margin and
     --grid-margin-sm are retired. >=768/>=1200 redefined in
     responsive.css, same pattern as --sys-layout-sec-*. */
  --sys-grid-margin: var(--space-16);

  /* sys/motion — unified transition timing for all hover/interaction
     animations (Tina, 2026-09-05, revised same day from 400ms to 600ms):
     every such transition below uses this single duration+easing pair
     instead of its own one-off value. Functional timing that's part of
     a feature's actual behavior (the service-card's 5s auto-advance
     progress bar, the gallery's 5s rotation interval) is NOT this —
     those durations mean something on their own and stay as-is. */
  --sys-motion-duration: 600ms;
  --sys-motion-easing: ease;

  /* sys/layout/sec — section vertical-spacing scale (Tina, 2026-09-04).
     <768 values below; >=768/>=1200 redefine the same names on :root in
     responsive.css so any rule using var(--sys-layout-sec-*) picks up
     the right breakpoint automatically. Points straight at literal px
     for now (no raw/primitive tier under this yet, per Tina) — and
     nothing consumes these yet: that wiring (the --comp tier) comes
     later. 2026-09-09: now points at --space-* above instead of
     literal px, closing the missing-Reference-tier gap Tina flagged. */
  --sys-layout-sec-2xl: var(--space-64);
  --sys-layout-sec-xl: var(--space-56);
  --sys-layout-sec-lg: var(--space-48);
  --sys-layout-sec-md: var(--space-40);
  --sys-layout-sec-def: var(--space-32);
  --sys-layout-sec-sm: var(--space-24);

  /* sys/layout/sec-gap-* — section 內部子區塊 gap（跟上面的 sec-*
     上下 padding 是不同批選取器）。3 條各自獨立的斷點進程，數值跟
     sec-* 對不上所以不能共用：sm=24/32/32　def=24/32/40（Tina 指定
     的目標進程）　xl=24/32/48。原本還有 md(24/24/40)、lg(24/40/40)
     兩條，2026-09-09 因為 `≥1200` 終值都一樣是 40，Tina 要求合併
     進 def（`≥768` 統一成 def 的 32，md 的 6 個選取器變大、lg 的
     4 個選取器變小）。 */
  --sys-layout-sec-gap-sm: var(--space-24);
  --sys-layout-sec-gap-def: var(--space-24);
  /* 2026-09-10：原本的 gap-xl 更名為 gap-lg（值不變），gap-xl 空出來給
     下面新的、更大的一階用。 */
  --sys-layout-sec-gap-lg: var(--space-24);
  /* 首頁六個 section container 的「標題 → 內容」間距統一綁這一階
     （service／security／brand／stats／clients／news，Tina 2026-09-10）：
     36 / 48 / 64。要一起調寬窄就只改這三個值。 */
  --sys-layout-sec-gap-xl: var(--space-36);

  /* comp/{section}/sec-p-{top|bottom} — each inherits from whichever
     --sys-layout-sec-* step matches that section's EXISTING <768
     padding-top/bottom (layout.css). Only the exact matches are here;
     sections whose current value doesn't land on a scale step (mostly
     60px, sitting exactly between xl/56 and 2xl/64) are flagged
     separately for Tina rather than guessed — see chat. Still not
     consumed by any section yet. */
  --comp-info-sec-p-top: var(--sys-layout-sec-md); /* base <768: 40px */
  --comp-info-sec-p-bottom: var(--sys-layout-sec-lg); /* 2026-09-10: per 間距-token-草案.md 3.1 */
  --comp-service-sec-p-top: var(--sys-layout-sec-lg); /* base <768: 48px */
  --comp-service-sec-p-bottom: var(--sys-layout-sec-xl); /* base <768: 56px */
  --comp-brand-sec-p-top: var(--sys-layout-sec-lg); /* 2026-09-10: per 間距-token-草案.md 3.1 */
  --comp-brand-sec-p-bottom: var(--sys-layout-sec-md); /* base <768: 40px */

  /* base <768 was 60px (no exact scale step, sat exactly between
     xl/56 and 2xl/64) — Tina chose to round down to xl for all 5 of
     these (2026-09-05). */
  --comp-security-sec-p-top: var(--sys-layout-sec-xl); /* base <768: 60px, rounded */
  --comp-stats-sec-p-top: var(--sys-layout-sec-xl); /* base <768: 60px, rounded */
  --comp-stats-sec-p-bottom: var(--sys-layout-sec-2xl); /* 2026-09-10: per 間距-token-草案.md 3.1 */
  --comp-clients-sec-p-top: var(--sys-layout-sec-xl); /* base <768: 60px, rounded */
  --comp-clients-sec-p-bottom: var(--sys-layout-sec-2xl); /* 2026-09-10: per 間距-token-草案.md 3.1 */
  --comp-news-sec-p-top: var(--sys-layout-sec-xl); /* base <768: 60px, rounded */
  --comp-news-sec-p-bottom: var(--sys-layout-sec-xl); /* base <768: 60px, rounded */
  --comp-cta-sec-p-top: var(--sys-layout-sec-xl); /* base <768: 60px, rounded */
  --comp-cta-sec-p-bottom: var(--sys-layout-sec-xl); /* base <768: 60px, rounded */

  /* base <768 was 100px, well past the scale's max (64) — Tina chose to
     round to 2xl for now (2026-09-05), flagged as provisional. */
  --comp-security-sec-p-bottom: var(--sys-layout-sec-2xl); /* base <768: 100px, rounded, provisional */
  /* How far 代理品牌 rides up over the pinned 資訊安全政策 — one viewport,
     same as the hero's parallax overlap. Not a spacing-scale value: it's
     an animation distance, so it stays in vh rather than --space-*. */
  --comp-security-overlap: 60vh;

  /* Second batch of section vertical padding (Tina, 2026-09-09) — each
     section already read its <768/>=768 padding off the shared
     --sys-layout-sec-* scale directly (no --comp-* alias existed yet);
     this promotes them to the same --comp-*-sec-p-* pattern as the
     batch above. >=1200 is handled per-section on the selector itself:
     where the real Figma value lands exactly on a scale step, it's
     wired to that step's --comp-* alias (see responsive.css); where it
     doesn't (a literal 80, sitting between md/75 and lg/90 with no
     free tier name at that position), it stays a documented literal
     override rather than forcing a new scale step or bending md/lg's
     existing, already-correct values elsewhere. */
  --comp-about-contact-sec-p-top: var(--sys-layout-sec-lg);
  --comp-about-contact-sec-p-bottom: var(--sys-layout-sec-lg);
  --comp-about-intro-sec-p-top: var(--sys-layout-sec-md);
  --comp-about-intro-sec-p-bottom: var(--sys-layout-sec-md);
  --comp-about-philosophy-sec-p-top: var(--sys-layout-sec-md);
  --comp-about-philosophy-sec-p-bottom: var(--sys-layout-sec-xl);
  --comp-about-reviews-sec-p-top: var(--sys-layout-sec-lg);
  --comp-about-reviews-sec-p-bottom: var(--sys-layout-sec-lg);
  --comp-about-testimony-sec-p-top: var(--sys-layout-sec-md);
  --comp-about-testimony-sec-p-bottom: var(--sys-layout-sec-xl);
  --comp-about-videos-sec-p-top: var(--sys-layout-sec-lg);
  --comp-about-videos-sec-p-bottom: var(--sys-layout-sec-lg);
  --comp-esg-conduct-sec-p-top: var(--sys-layout-sec-md);
  --comp-esg-conduct-sec-p-bottom: var(--sys-layout-sec-md);
  --comp-esg-csr-sec-p-top: var(--sys-layout-sec-lg);
  --comp-esg-csr-sec-p-bottom: var(--sys-layout-sec-lg);
  --comp-esg-talent-sec-p-top: var(--sys-layout-sec-md);
  --comp-esg-talent-sec-p-bottom: var(--sys-layout-sec-2xl);
  --comp-our-clients-grid-sec-p-top: var(--sys-layout-sec-sm);
  --comp-our-clients-grid-sec-p-bottom: var(--sys-layout-sec-xl);
  --comp-ubiquiti-more-sec-p-top: var(--sys-layout-sec-lg);
  --comp-ubiquiti-more-sec-p-bottom: var(--sys-layout-sec-lg);
  --comp-brand-cards-sec-p-top: var(--sys-layout-sec-def);
  --comp-brand-cards-sec-p-bottom: var(--sys-layout-sec-def);

  /* comp/{section}/sec-p-{top|bottom} — C 類組 4（2026-09-09）,13 個
     selector 裡有 2 個（dealership-banner 的 top/bottom、news-related
     的水平 padding）在 <768 就對不上任何階，維持字面值，不開
     Component Token；其餘都在下面。個別斷點覆寫（凍結字面值 or 引用
     其他階）寫在 responsive.css 的選取器本身，不是寫在這裡——原則同
     B 類，Component Token 只 pin 住 <768 對應的那一階，不重複定義。 */
  --comp-contact-form-sec-p-top: var(--sys-layout-sec-def);
  --comp-contact-form-sec-p-bottom: var(--sys-layout-sec-def);
  --comp-contact-info-sec-p-top: var(--sys-layout-sec-sm);
  --comp-contact-info-sec-p-bottom: var(--sys-layout-sec-sm);
  --comp-dealer-form-sec-p-top: var(--sys-layout-sec-def);
  --comp-dealer-form-sec-p-bottom: var(--sys-layout-sec-def);
  --comp-dealership-banner-sec-p-top: var(--sys-layout-sec-sm);
  --comp-dealership-banner-sec-p-bottom: var(--sys-layout-sec-sm);
  --comp-dealership-stores-sec-p-top: var(--sys-layout-sec-sm);
  --comp-dealership-stores-sec-p-bottom: var(--sys-layout-sec-xl);
  --comp-news-detail-content-sec-p-top: var(--sys-layout-sec-md);
  --comp-news-detail-content-sec-p-bottom: var(--sys-layout-sec-lg);
  --comp-news-detail-head-sec-p-top: var(--sys-layout-sec-sm);
  --comp-news-list-sec-p-top: var(--sys-layout-sec-md);
  --comp-news-list-sec-p-bottom: var(--sys-layout-sec-md);
  --comp-news-pagination-sec-p-top: var(--sys-layout-sec-sm);
  --comp-news-pagination-sec-p-bottom: var(--sys-layout-sec-xl);
  --comp-news-related-sec-p-top: var(--sys-layout-sec-lg);
  --comp-news-related-sec-p-bottom: var(--sys-layout-sec-xl);
  --comp-ubiquiti-categories-sec-p-top: var(--sys-layout-sec-md);
  --comp-ubiquiti-categories-sec-p-bottom: var(--sys-layout-sec-2xl);
  --comp-ubiquiti-hero-sec-p-top: var(--sys-layout-sec-lg);
  --comp-ubiquiti-principles-panel-sec-p-top: var(--sys-layout-sec-lg);
  --comp-ubiquiti-principles-panel-sec-p-bottom: var(--sys-layout-sec-xl);

  /* sys/{category}-{tier} — typography step scale (Tina, 2026-09-08),
     same pattern as --sys-layout-sec-*: each name is a fixed label
     (xs/sm/def/md/lg/xl/2xl/3xl, ascending), but the VALUE it holds is
     redefined per breakpoint on :root in responsive.css — the RWD step
     lives here, not in the --type-* aliases below. category is by
     visual role, not language — headline covers every bold/prominent
     text style (page titles, section titles, eyebrow labels, the
     homepage tagline), body is regular content copy. Figma names these
     "headline/*" vs "en/headline/*" by which script the text happens
     to be in, but the actual size rules never differ by language, so
     that split isn't reflected in the token names (Tina, 2026-09-08).
     Only font-size lives here. line-height is a single sitewide
     default on `body` (var(--lh-def), unitless so every descendant
     re-multiplies it against its OWN font-size on inherit — this is a
     documented CSS special case for unitless line-height only).
     letter-spacing can't use that same trick: em-based lengths compute
     to a fixed px at whichever element declares them and inherit as
     that fixed px, not as a ratio each descendant recalculates — so
     every selector that needs the 3% tracking (var(--ls-def)) declares
     it itself; see each selector's own rule, not a token here.
     <768 values below. */

  /* headline/3xl/b -> 4xl/b -> 6xl/b (page hero/section <h1>/<h2>). */
  --sys-headline-xl: var(--fs-28);

  /* headline/2xl/b -> 3xl/b (in-page section heading, stops at 3xl). */
  --sys-headline-md: var(--fs-24);

  /* en/headline/def/m -> md/m (hero eyebrow). */
  --sys-headline-xs: var(--fs-base);

  /* 2026-09-09: card title size (security/client/news cards). */
  --sys-headline-sm: var(--fs-18);

  /* 2026-09-09: sits between sm and def — a bigger card title variant. */
  --sys-headline-def: var(--fs-20);

  /* en/headline/sm/m -> def/m (section eyebrow). */
  --sys-headline-xxs: var(--fs-14);

  /* en/headline/3xl/b -> 5xl/b -> display/b (homepage tagline). */
  --sys-headline-2xl: var(--fs-28);

  /* 2026-09-09: homepage-only section title size, stays flat through
     >=768 and only grows at >=1200. */
  --sys-headline-lg: var(--fs-28);

  /* body/sm/r -> def/r (generic body copy; shared by both --type-*
     below). */
  --sys-body-xs: var(--fs-14);

  /* 2026-09-10: flat body text — navbar/mobile-menu link labels never
     grow past base across any breakpoint (sits above body-xs-size
     because its <768 value, 16, is already body-xs-size's >=768
     end-state). */
  --sys-body-sm: var(--fs-base);

  /* 2026-09-10: tab-item labels — same <768 value as body-sm (16) but
     keeps growing at >=1200 instead of freezing, so it's a separate,
     bigger tier. */
  --sys-body-md: var(--fs-base);

  /* 2026-09-10: same <768 value as sm/md (16) but freezes at >=768
     instead of continuing to grow at >=1200 like md does. */
  --sys-body-def: var(--fs-base);

  /* 2026-09-10: smallest meta text (footer social/bottom bar, form
     error messages, breadcrumb, news-card tag/date) — 13->14 at
     >=768, unchanged at >=1200 (Tina, 2026-09-10). */
  --sys-body-xxs: var(--fs-13);

  /* type/{name} — component-level aliases (Tina, 2026-09-08). Each
     just names which sys step a text role uses; defined ONCE here
     (not redefined per breakpoint — that happens inside the sys token
     above). One-off sizes used by only a single selector stay as
     plain --fs-* refs, no token needed. */

  --type-page-title: var(--sys-headline-xl);

  --type-page-eyebrow: var(--sys-headline-xs);

  /* 2026-09-10: navbar/mobile-menu link text (desktop nav, dropdown,
     mobile accordion/submenu) — consolidated from 6 selectors that
     each hardcoded --fs-base individually. */
  --type-nav-link: var(--sys-body-sm);

  --type-display: var(--sys-headline-2xl);

  --type-card-desc-sm: var(--sys-body-xs);

  /* 2026-09-10: bigger card-desc variants (number badges/quote text). */
  --type-card-desc-md: var(--sys-body-md);
  --type-card-desc-def: var(--sys-body-def);

  /* 2026-09-09: card title size (security/client/news cards so far). */
  --type-card-title-lg: var(--sys-headline-def);
  --type-card-title-def: var(--sys-headline-sm);
  --type-card-title-sm: var(--sys-headline-xs);

  /* Same sys step as --type-card-desc-sm — the two used to differ by
     letter-spacing, now identical since that's a sitewide default
     instead of a per-component add-on; kept as two names since they
     still mean different things (card copy vs. generic body copy). */
  --type-body-def: var(--sys-body-xs);

  --type-sec-title: var(--sys-headline-md);

  /* 2026-09-10: smaller section title variant (contact-info heading,
     footer company name). */
  --type-sec-title-sm: var(--sys-headline-def);
  --type-sec-eyebrow: var(--sys-headline-xxs);

  /* 2026-09-09: homepage-only section title size (service/security/
     brand/stats/clients/news sections) — other pages keep
     --type-sec-title. */
  --type-sec-title-hero: var(--sys-headline-lg);

  /* Same sys step as --type-card-desc-sm/--type-body-def
     (14->16, unchanged at >=1200) — .btn's font-size progression
     happens to match exactly, reused rather than opening a new sys
     token (Tina, 2026-09-08). 2026-09-10: renamed from --type-btn to
     --type-label-def since it's no longer only for .btn — also covers
     news pagination items/ellipsis and the news-detail head tag. */
  --type-label-def: var(--sys-body-xs);

  /* 2026-09-10: tab-item labels (our-clients/ubiquiti/news/dealership
     tabs) — bigger sibling of --type-label-def, keeps growing at >=1200
     instead of freezing. */
  --type-label-lg: var(--sys-body-md);

  /* 2026-09-10: footer social/bottom bar, form error messages,
     nav-heading, review-author, contact-note. */
  --type-body-sm: var(--sys-body-xxs);

  /* 2026-09-10: breadcrumb link/current + news-card tag/date. */
  --type-label-sm: var(--sys-body-xxs);
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  letter-spacing: var(--ls-def);
}

/* clip, not hidden: `overflow-x: hidden` forces overflow-y to compute
   as auto, which turns html/body into scroll containers and silently
   breaks every `position: sticky` on the page (the navbar and the hero
   stage). clip suppresses the same horizontal overflow without
   creating a scroll container. */
html {
  overflow-x: clip;
  height: 100%;
}

body {
  font-family: var(--font-zh);
  color: var(--color-neutral-800);
  background: var(--color-white);
  line-height: var(--lh-def);
  overflow-x: clip;
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

/* 內容太短時（例如眾多客戶「其他」分類只有 3 筆）把「麵包屑 + Footer」
   一起推到視窗底部，避免深色 Footer 下方露出一截白色 body 背景。
   margin-top: auto 要放在 main 後面緊接的第一個元素上，footer 才會跟著
   一起貼底、不分離——多數頁是 .breadcrumb-section，但 index.html 沒有
   麵包屑、footer 直接接在 main 後面，所以用 main + * 通用選一次涵蓋兩種
   結構，不用每頁各寫一條規則。 */
main + * {
  margin-top: auto;
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

.container {
  max-width: var(--container-max);
  margin: 0 auto;
  width: 100%;
}

/* Shared eyebrow+title wrapper — any heading built from
   .section-eyebrow + .section-title gets this 8px gap, wrapped in
   its own div separate from any following description text. */
.section-heading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

/* en/headline/sm/m */
.section-eyebrow {
  font-family: var(--font-en);
  font-weight: 500;
  font-size: var(--type-sec-eyebrow);
  line-height: 1.5;
  color: var(--color-primary-500);
}

/* headline/3xl/b */
.section-title {
  display: flex;
  align-items: center;
  gap: 16px;
  font-family: var(--font-zh);
  font-weight: 700;
  font-size: var(--type-sec-title);
  line-height: 1.5;
  color: var(--color-neutral-800);
  position: relative;
  padding-left: 24px;
}

/* 2026-09-09: homepage's own 6 section titles use --type-sec-title-hero
   instead of the site-wide --type-sec-title above — scoped by each
   section's own wrapper class so other pages' .section-title is untouched. */
.service-section .section-title,
.security-section .section-title,
.brand-section .section-title,
.stats-section .section-title,
.clients-section .section-title,
.news-section .section-title {
  font-size: var(--type-sec-title-hero);
}

.section-title::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 6px;
  height: 32px;
  background: var(--color-primary-500);
}

/* 左右內距 40px（Tina, 2026-09-10）——這個 modifier 只有首頁在用，
   6 個標題（服務項目／資訊安全政策宣示／代理品牌／資深首選／
   我們的客戶／新聞動態）統一同一個值，不再各自留一次性覆寫。 */
.section-title--center {
  justify-content: center;
  padding: 0 40px;
}
.section-title--center::before,
.section-title--center::after {
  content: "";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 6px;
  height: 20px;
  background: var(--color-primary-500);
}
.section-title--center::before { left: 0; }
.section-title--center::after { right: 0; }

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

/* Shared cross-page text styles (Tina, 2026-09-08) — each --type-*
   token bundles the font-size (and line-height/letter-spacing where
   the style has one) for a text style reused verbatim by 2+ selectors
   across different pages. Each selector below keeps its own
   font-family/weight/color/layout in its own rule in layout.css or
   components.css — only these three properties come from here. */

/* type-page-title — each page's own hero/section <h1>/<h2> title. */
.news-hero__title,
.dealership-hero__title,
.contact-hero__title,
.news-detail-head__title,
.ubiquiti-principles-heading__title,
.ubiquiti-more__title,
.about-hero__title,
.about-contact__heading,
.our-clients-hero__title,
.brands-hero__title,
.esg-hero__title,
.ubiquiti-hero__title {
  font-size: var(--type-page-title);
}

/* type-page-eyebrow — small caps label above a page/section title. */
.dealership-hero__eyebrow,
.contact-hero__eyebrow,
.ubiquiti-principles-heading__eyebrow,
.about-hero__eyebrow,
.about-contact__eyebrow,
.brands-hero__eyebrow,
.esg-hero__eyebrow,
.news-hero__eyebrow,
.our-clients-hero__eyebrow,
.ubiquiti-more__eyebrow {
  font-size: var(--type-page-eyebrow);
}

/* type-display — homepage "iNNOVATION + Trust" tagline, same text
   reused in the info-section and cta-section. */
.info-section__display,
.cta-section__display {
  font-size: var(--type-display);
}

/* type-card-desc — homepage card body copy (security-card / cta-card). */
.security-card__desc,
.cta-card__desc {
  font-size: var(--type-card-desc-sm);
}

/* type-body-def — site-wide generic body text, no >=1200 change. */
.form-field__label,
.form-input,
.form-textarea,
.form-radio-option__label,
.form-checkbox-option__label,
.form-upload__hint,
.brand-card__desc,
.ubiquiti-category-card__desc,
.ubiquiti-category-card__link,
.ubiquiti-principles-panel__list,
.news-detail-head__date,
.news-detail-head__share-label,
.news-detail-content__container > p,
ul.wp-block-list,
ol.wp-block-list,
.dealership-banner__cta-eyebrow,
.contact-info__label,
.contact-info__value,
.site-footer__nav-col ul a,
.site-footer__info,
.about-reviews__desc,
.esg-impact__desc,
.esg-csr__desc,
.about-intro__body,
.about-testimony__desc,
.about-testimony__card-desc,
.about-philosophy__card-desc,
.esg-workplace__text,
.esg-benefit-card__desc,
.esg-conduct__text,
.esg-conduct__list,
.esg-talent__text,
.info-section__body,
.stat-item__sub {
  font-size: var(--type-body-def);
}

/* "Sweep to Right" hover (Tina's spec, 2026-09-04): a colored layer grows
   in from the left edge on hover, and retreats back the same way on
   mouse-leave — the classic fixed-direction sweep (unlike the cards below,
   this direction does NOT depend on cursor position). Implemented as a
   ::before so no extra markup is needed; isolation:isolate gives the
   button its own stacking context so the ::before's z-index:-1 stays
   scoped to just this button instead of reaching into the section behind
   it. */
.btn {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--button-gap);
  height: var(--button-height);
  min-width: var(--button-min-width);
  padding: 0 var(--button-padding-x);
  border-radius: var(--button-radius);
  font-family: var(--font-zh);
  font-weight: 500;
  font-size: var(--type-label-def);
  line-height: 1.25;
  border: none;
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--sys-motion-duration) var(--sys-motion-easing),
    border-color var(--sys-motion-duration) var(--sys-motion-easing);
}

.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  transform: scaleX(0);
  transform-origin: 0% 50%;
  transition: transform var(--sys-motion-duration) var(--sys-motion-easing);
}

.btn:hover::before {
  transform: scaleX(1);
}

/* button/filled, state=hover (node 7006:20676): bg-default #333 -> bg-hover primary/500 */
.btn-filled {
  background: var(--color-neutral-800);
  color: var(--color-white);
}

.btn-filled::before {
  background: var(--color-primary-500);
}

/* button/outline, state=hover (node 4040:13181): fills bg-default #333, text flips to white */
.btn-outline {
  background: var(--color-white);
  border: 1px solid var(--color-neutral-800);
  color: var(--color-neutral-800);
}

.btn-outline::before {
  background: var(--color-neutral-800);
}

.btn-outline:hover {
  color: var(--color-white);
}

/* Same outline button, white-on-dark variant (clients-section CTA over a
   dark photo bg) — was inline-styled with no hover; promoted to a BEM
   modifier so :hover can actually apply (inline styles win over any
   stylesheet rule, hover included). No dedicated Figma "white" variant
   node was given, so hover mirrors the dark variant's fill-invert logic:
   sweeps in white, text goes dark. */
.btn-outline--white {
  background: transparent;
  border-color: var(--color-white);
  color: var(--color-white);
}

.btn-outline--white::before {
  background: var(--color-white);
}

.btn-outline--white:hover {
  color: var(--color-neutral-800);
}

