/* ============================================================
   Asiagate — Direction 2: 格線編輯 (Grid Editorial)
   Amendment pass: the grid is now a felt structure (alignment,
   generous whitespace, occasional restrained rule) rather than a
   drawn overlay. Soft corners throughout. One brand red, paired
   with a quiet dusty-rose secondary and warm blush-cream neutrals
   — premium and subtle, not a multi-hue "dynamic" system.
   Independent from direction-1-warm-portrait — no shared assets.
   ============================================================ */


:root{
  /* brand anchor — the client's real logo red, unchanged across every direction */
  --red: #B20A30;
  --red-deep: color-mix(in oklch, var(--red) 78%, black);
  --red-wash: color-mix(in oklch, var(--red) 7%, white);

  /* one quiet warm secondary, derived from the red hue — captions, stat panels */
  --rose: #B08476;
  --rose-deep: #7A594E;
  --rose-wash: color-mix(in oklch, var(--rose) 14%, white);

  /* a second, cooler accent — the jade tile color from the hero photography.
     Red still carries every CTA and primary emphasis; jade is the one place
     the system genuinely pairs a second hue against it, not a third wash. */
  --jade: #3F5F4E;
  --jade-deep: #2A4135;
  --jade-wash: color-mix(in oklch, var(--jade) 12%, white);

  --ink: #221B17;
  --ink-soft: #6B5F57;
  --ink-faint: #A89A8F;
  --paper: #F1E4D5;
  --paper-deep: #E5D2BB;
  --surface: #FFFFFF;
  --surface-sunk: #EBDCC6;
  --line: rgba(34,27,23,.10);
  --line-strong: rgba(34,27,23,.18);

  --font-display: 'Noto Serif TC', serif;
  --font-body: 'Noto Sans TC', sans-serif;
  --font-caption: 'EB Garamond', 'Times New Roman', serif;
  /* logo lockup — matches the client's official artwork: bold gothic 中文,
     Helvetica-family bold Latin, both set in the brand red. Never serif. */
  --font-logo-zh: 'Noto Sans TC', sans-serif;
  --font-logo-en: Helvetica, 'Helvetica Neue', Arial, sans-serif;

  --radius-sm: 8px;
  --radius-md: 16px;
  --radius-lg: 24px;
  --gap: clamp(10px, 1.2vw, 16px);
  --pad: clamp(20px, 4.4vw, 68px);
  --shadow-sm: 0 2px 10px rgba(34,27,23,.05);
  --shadow-md: 0 20px 48px rgba(34,27,23,.10);
  --ease: cubic-bezier(.16,1,.3,1);
}

*,*::before,*::after{ box-sizing:border-box; }
html{ scroll-behavior:smooth; }
body{
  margin:0;
  font-family:var(--font-body);
  color:var(--ink);
  background-color:var(--paper);
  font-size:16px;
  line-height:1.7;
  -webkit-font-smoothing:antialiased;
}
img{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
h1,h2,h3{ font-family:var(--font-display); font-weight:900; line-height:1.2; letter-spacing:-0.01em; margin:0; text-wrap:balance; }
p{ margin:0; }
button{ font-family:inherit; cursor:pointer; }

.caption{
  font-family:var(--font-caption);
  font-style:italic;
  font-weight:500;
  font-size:15px;
  color:var(--rose-deep);
  letter-spacing:.01em;
}

@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::as{ animation-duration:.001ms !important; transition-duration:.001ms !important; }
}

/* ---------- dev note ---------- */
/* Preview-only banner. The markup was removed from every page before the
   client preview upload (2026-08-02); these two rules are kept so the bar
   can be reinstated by pasting one <div class="dev-note"> back under
   <body> for a future internal round. */
.dev-note{ background:var(--ink); color:var(--paper); font-size:13px; line-height:1.6; padding:10px var(--pad); text-align:center; }
.dev-note b{ color:#E8B6A3; font-weight:700; }

/* ============================================================
   LAYOUT SYSTEM — the grid is felt through alignment and
   generous whitespace, not drawn as a persistent overlay.
   ============================================================ */
.wrap{
  max-width:1360px;
  margin-inline:auto;
  padding-inline:var(--pad);
}
.grid12{
  display:grid;
  grid-template-columns:repeat(12,1fr);
  column-gap:var(--gap);
}
@media (max-width: 980px){
  .grid12{ grid-template-columns:repeat(6,1fr); row-gap:28px; }
}
@media (max-width: 620px){
  .grid12{ grid-template-columns:repeat(4,1fr); row-gap:22px; }
}

/* ============================================================
   NAV
   ============================================================ */
.nav{ position:sticky; top:0; z-index:40; background:color-mix(in oklch, var(--paper) 92%, transparent); backdrop-filter:blur(10px); border-bottom:1px solid var(--line-strong); }
.nav-inner{
  max-width:1360px; margin-inline:auto; padding-inline:var(--pad);
  height:76px; display:flex; align-items:center; justify-content:space-between; gap:24px;
}
.brand{ display:flex; align-items:center; gap:12px; }
.brand img{ width:34px; height:34px; }
.brand-text{ display:flex; flex-direction:column; line-height:1.15; }
.brand-zh{ font-family:var(--font-logo-zh); font-weight:700; font-size:16px; letter-spacing:.02em; color:var(--red); }
.brand-en{ font-family:var(--font-logo-en); font-weight:700; font-size:10.5px; letter-spacing:.005em; color:var(--red); text-transform:none; }
.nav-links{ list-style:none; display:flex; gap:30px; margin:0; padding:0; }
.nav-links a{ font-size:14.5px; font-weight:600; color:var(--ink-soft); position:relative; padding-block:6px; }
.nav-links a.on{ color:var(--ink); }
.nav-links a.on::after{ content:''; position:absolute; left:0; right:0; bottom:0; height:2px; background:var(--red); border-radius:2px; }
.nav-links a:not(.unavailable):hover{ color:var(--ink); }
.nav-links a.unavailable{ color:var(--ink-faint); cursor:not-allowed; }
.nav-cta{ background:var(--red); color:#fff; font-weight:700; font-size:14px; padding:11px 22px; border-radius:var(--radius-sm); white-space:nowrap; transition:background .2s var(--ease); }
.nav-cta:hover{ background:var(--red-deep); }

/* ---------- mobile menu (hamburger) ---------- */
.nav-toggle{
  display:none; width:44px; height:44px; margin-right:-10px;
  align-items:center; justify-content:center;
  background:transparent; border:0; color:var(--ink); border-radius:var(--radius-sm);
}
.nav-toggle-bars,.nav-toggle-bars::before,.nav-toggle-bars::after{
  content:''; display:block; width:22px; height:2px; border-radius:2px;
  background:currentColor; transition:transform .28s var(--ease), opacity .2s var(--ease);
}
.nav-toggle-bars{ position:relative; }
.nav-toggle-bars::before{ position:absolute; top:-7px; left:0; }
.nav-toggle-bars::after{ position:absolute; top:7px; left:0; }
.nav.menu-open .nav-toggle-bars{ background:transparent; }
.nav.menu-open .nav-toggle-bars::before{ transform:translateY(7px) rotate(45deg); }
.nav.menu-open .nav-toggle-bars::after{ transform:translateY(-7px) rotate(-45deg); }

.mobile-menu{
  position:fixed; left:0; right:0; z-index:39;
  background:var(--paper); border-bottom:1px solid var(--line-strong);
  box-shadow:var(--shadow-md);
  padding:12px var(--pad) 22px;
  opacity:0; visibility:hidden; transform:translateY(-10px);
  transition:opacity .26s var(--ease), transform .26s var(--ease), visibility 0s linear .26s;
}
.nav.menu-open .mobile-menu{ opacity:1; visibility:visible; transform:none; transition:opacity .26s var(--ease), transform .26s var(--ease); }
.mobile-menu-links{ list-style:none; margin:0; padding:0; }
.mobile-menu-links li{ border-bottom:1px solid var(--line); }
.mobile-menu-links a{
  display:flex; align-items:center; justify-content:space-between; gap:12px;
  padding:15px 2px; font-size:16px; font-weight:600; color:var(--ink); min-height:44px;
}
.mobile-menu-links a.on{ color:var(--red); }
.mobile-menu-links a.unavailable{ color:var(--ink-faint); pointer-events:none; }
.mobile-menu-links .soon-tag{
  font-family:var(--font-body); font-size:11px; font-weight:700; letter-spacing:.02em;
  color:var(--ink-faint); border:1px solid var(--line-strong); border-radius:999px; padding:2px 10px;
}
.mobile-menu-cta{ width:100%; justify-content:center; margin-top:18px; }

.menu-backdrop{
  position:fixed; inset:0; z-index:38; background:rgba(34,27,23,.34);
  opacity:0; visibility:hidden; transition:opacity .26s var(--ease), visibility 0s linear .26s;
}
.menu-backdrop.open{ opacity:1; visibility:visible; transition:opacity .26s var(--ease); }

/* the full official lockup (信言保險代理有限公司 + Asiagate Insurance Agencies Ltd.)
   is ~215px wide, so the inline nav needs ~1000px before it stops wrapping */
@media (max-width: 1000px){
  .nav-links{ display:none; }
  .nav-toggle{ display:inline-flex; }
  /* with .nav-links gone, .nav-inner's justify-content:space-between leaves
     .nav-cta stranded mid-bar (it's the flex item between brand and toggle,
     so "space-between" puts equal gaps on both sides of it instead of
     pinning it next to the toggle at the right edge) */
  .nav-cta{ margin-left:auto; }
}
@media (min-width: 1001px){
  .mobile-menu,.menu-backdrop{ display:none; }
}
/* brand name in the menu — only surfaced on very narrow screens, where the
   inline logo text is dropped to stop the bar from crowding together */
.mobile-menu-brand{ display:none; padding:2px 2px 14px; border-bottom:1px solid var(--line); margin-bottom:6px; }
.mobile-menu-brand .brand-zh{ font-size:18px; display:block; line-height:1.25; }
.mobile-menu-brand .brand-en{ font-size:11px; }
@media (max-width: 381px){
  .brand-text{ display:none; }
  .mobile-menu-brand{ display:block; }
}

/* ============================================================
   BUTTONS — soft rounded rectangle, not hard-square, not full pill
   ============================================================ */
.btn-primary,.btn-ghost,.btn-white,.btn-outline-w{
  display:inline-flex; align-items:center; gap:9px;
  font-weight:700; font-size:15px; padding:15px 28px;
  border-radius:var(--radius-sm);
  border:1px solid transparent; transition:background .2s var(--ease), color .2s var(--ease), border-color .2s var(--ease);
}
.btn-primary{ background:var(--red); color:#fff; }
.btn-primary:hover{ background:var(--red-deep); }
.btn-ghost{ border-color:var(--line-strong); color:var(--ink); }
.btn-ghost:hover{ border-color:var(--ink); background:var(--surface); }
.btn-white{ background:var(--paper); color:var(--ink); }
.btn-white:hover{ background:#fff; }
.btn-outline-w{ border-color:rgba(255,255,255,.35); color:#fff; }
.btn-outline-w:hover{ border-color:#fff; }

/* ============================================================
   HERO — HOMEPAGE: headline + a red-textured stat block (blend-
   mode, not a flat fill) on top; the four trust points as real
   cards underneath, replacing what used to be two photo cells
   that only duplicated imagery shown again in the picker below.
   ============================================================ */
.hero-bento{ position:relative; padding-block:clamp(32px,5vw,56px) clamp(20px,3vw,32px); }
/* rev53: was `left:50%; transform:translateX(-50%); width:100vw`.
   100vw INCLUDES the scrollbar, so on any page tall enough to scroll the
   backdrop was ~8-15px wider than the layout viewport and the centring
   pushed half of that excess past each edge — real horizontal scroll on
   the homepage. .hero-bento is position:relative with no inline padding
   and spans the viewport exactly, so width:100% is both simpler and
   pixel-identical. */
.hero-backdrop{
  position:absolute; top:0; left:0;
  width:100%; height:clamp(480px,64vw,670px); z-index:0;
  background:linear-gradient(180deg, var(--red) 0%, var(--red) 72%, transparent 100%);
}
.hero-bento .wrap{ position:relative; z-index:1; }
.bento-grid{
  display:grid;
  grid-template-columns:1.6fr 1fr;
  grid-template-rows:auto auto;
  grid-template-areas:
    "headline stat"
    "points   points";
  gap:var(--gap);
}
.bento-cell{ position:relative; border-radius:var(--radius-lg); overflow:hidden; }

.cell-headline{
  grid-area:headline; overflow:visible; position:relative;
  background:var(--surface);
  border:1px solid var(--line-strong); padding:clamp(28px,3.4vw,44px); padding-right:clamp(180px,26vw,300px);
  display:flex; flex-direction:column; justify-content:center; gap:18px;
}
.cell-headline .caption{ display:block; }
.cell-headline h1{ font-size:clamp(1.9rem,3.2vw,3.2rem); color:var(--ink); }
.cell-headline h1 em{ font-style:normal; color:var(--red); }
.cell-headline p{ color:var(--ink-soft); font-size:15px; max-width:40ch; }
.cell-headline .btn-primary{ align-self:flex-start; margin-top:4px; }
/* the kettle — a real key visual, popping out past the card edge, not a
   tucked-in icon */
.cell-headline-illustration{
  position:absolute; top:50%; right:-38%; width:115%; aspect-ratio:1408/768;
  transform:translateY(-50%) rotate(-7deg); z-index:2;
  filter:drop-shadow(0 34px 52px rgba(34,27,23,.3));
}
.cell-headline-illustration img{
  width:100%; height:100%; object-fit:contain;
  /* soften the hard bottom crop of the poured tea into a fade instead of a
     sharp cut-off */
  -webkit-mask-image:linear-gradient(to bottom, #000 84%, rgba(0,0,0,.35) 94%, transparent 100%);
  mask-image:linear-gradient(to bottom, #000 84%, rgba(0,0,0,.35) 94%, transparent 100%);
}
/* At ≤980px (tablet + phone) the hero collapses to a single column and gets
   the same treatment: the desktop 2-column overhang looks cramped once the
   headline card narrows, so instead the headline leads with WORDS and the
   kettle floats over the red 15%+ block below. Only >980px keeps the big
   2-column KV. */
@media (max-width: 980px){
  /* The headline leads with WORDS — caption, title, description and CTA come
     first. The kettle drops out of the text flow and floats over the red
     15%+ block below, its top edge crossing the seam to just touch the
     headline card.
     NOTE: the source PNG is ~724x670 (near-square). We must NOT force the
     desktop 1408/768 box here — object-fit:contain would letterbox it and
     shrink the visible kettle. aspect-ratio:auto + img height:auto make the
     box hug the actual artwork. Size is a clamp (not a raw %) so it stays
     moderate across the whole 385–980 range instead of ballooning near 980.
     Crucially we DON'T give the cells their own z-index: if .cell-headline /
     .cell-stat form stacking contexts, the kettle gets trapped above the
     whole stat block including its text. With the cells at z-index:auto, the
     kettle (z-index:5) and the 15%+ text (z-index:6) both resolve in the
     shared .wrap stacking context, so the text always wins and stays
     readable even where the kettle overlaps it. */
  .hero-bento{ overflow-x:clip; }
  /* the kettle is a DOM child of the headline; the headline's data-reveal uses
     translateY, and ANY transform creates a stacking context that would trap
     the kettle (z-index:5) behind the red stat block. On mobile the headline
     reveals with opacity only (no transform) so the kettle can paint over the
     stat block as intended. */
  .cell-headline[data-reveal]{ transform:none; }
  .cell-headline{ padding-right:clamp(28px,3.4vw,44px); padding-top:clamp(28px,3.4vw,44px); }
  .cell-headline-illustration{
    position:absolute; left:auto; top:100%; right:4%;
    width:clamp(180px, 40vw, 300px); max-width:none; aspect-ratio:auto;
    margin:0; z-index:5;
    transform:translateY(-12%) rotate(-5deg);
    transform-origin:center;
    filter:drop-shadow(0 18px 30px rgba(34,27,23,.34));
  }
  .cell-headline-illustration img{ height:auto; }
  /* taller red canvas so the floating kettle fits above the 15%+ figure
     (.bento-grid prefix raises specificity over the later base .cell-stat) */
  .bento-grid .cell-stat{ min-height:clamp(220px, 38vw, 320px); }
  /* higher specificity than the base .cell-stat-body rule (which is defined
     later in the file) so the 15%+ text keeps its z-index:6 and stays above
     the kettle (z-index:5). */
  .cell-stat .cell-stat-body{ z-index:6; }
}

.cell-stat{ grid-area:stat; background:var(--red); display:flex; flex-direction:column; justify-content:flex-end; padding:clamp(22px,2.6vw,30px); min-height:180px; }
.cell-stat-texture{ position:absolute; inset:0; background-size:cover; background-position:center; filter:grayscale(1) contrast(1.15); opacity:.28; mix-blend-mode:overlay; }
.cell-stat-body{ position:relative; z-index:1; }
/* label sits ABOVE the figure — the sentence sets up the number, the number
   lands as the payoff. The "+" is a true superscript, not a full-size glyph. */
.cell-stat-body span{ display:block; font-size:13px; color:rgba(255,255,255,.85); margin-bottom:6px; }
.cell-stat-body strong{ display:block; font-family:var(--font-display); font-size:clamp(2.4rem,3.4vw,3.2rem); font-weight:900; color:#fff; line-height:1; }
.cell-stat-body strong sup{ font-size:.42em; vertical-align:super; line-height:0; margin-left:.03em; }
/* rev67 — 客戶第七輪拿走首頁的 15%+，這一格由一個數字變成一句六字短語
   （拒絕隱藏收費）。九百字重的展示襯線在 3.2rem 下，六個全形字約 307px 闊，
   遠超這一格的可用寬度；與餐飲頁第五輪同一問題（見 .hp-stat strong 的 rev66
   註）。字級收細一級、行高由 1 放寬至 1.22 以容兩行，其餘處理一如原設計。 */
.cell-stat-body.is-phrase strong{ font-size:clamp(1.6rem,2.4vw,2.15rem); line-height:1.22; }

/* four real cards, not a thin flex strip — carrying the visual weight
   the removed photo cells used to. Each card carries a custom monoline
   SVG icon (not a circular photo badge or a filled colour icon-in-circle
   — the generic trust-icon trope), drawn specifically for its own point
   so the four are genuinely distinct shapes, not one icon repeated. */
.cell-points-row{ grid-area:points; display:grid; grid-template-columns:repeat(4,1fr); gap:var(--gap); }
.point-card{
  position:relative; border:1px solid var(--line-strong); border-radius:var(--radius-lg); padding:22px 20px 20px;
  display:flex; flex-direction:column; gap:10px; background:var(--surface);
}
.point-icon{ flex-shrink:0; width:26px; height:26px; color:var(--red); }
.point-icon svg{ display:block; width:100%; height:100%; }
.point-card strong{ font-size:15px; font-weight:700; }
.point-card span{ font-size:12.5px; color:var(--ink-soft); line-height:1.5; }

@media (max-width: 980px){
  /* single column from tablet down: headline (words) over the 15%+ block over
     the points — the kettle floats over the 15%+ block (see hero rework) */
  .bento-grid{
    grid-template-columns:1fr;
    grid-template-areas: "headline" "stat" "points";
  }
  .cell-points-row{ grid-template-columns:repeat(2,1fr); }
}
@media (max-width: 620px){
  .cell-points-row{ grid-template-columns:1fr; }
}

/* ============================================================
   SECTION SHELL
   ============================================================ */
.section{ padding-block:clamp(64px,9vw,120px); }
.section-head{ grid-column:1/13; margin-bottom:clamp(36px,5vw,56px); }
.section-head h2{ font-size:clamp(2rem,3.6vw,3rem); margin-top:12px; }
.section-head p{ color:var(--ink-soft); font-size:15.5px; max-width:46ch; }

/* ============================================================
   SOLUTIONS — the uneven 3-photo picker, back by request: catering
   spans a tall 2-row cell, elderly sits above a third cell that
   carries the cutout collage instead of a photo or a bare "+7".
   Tight to the hero — no header copy, small top padding.
   ============================================================ */
.coverage-index{ padding-block:clamp(28px,3.4vw,40px) clamp(56px,8vw,96px); }
.solutions-grid{ grid-column:1/13; display:grid; grid-template-columns:repeat(12,1fr); grid-template-rows:auto auto; column-gap:var(--gap); row-gap:var(--gap); }
.ind-card{ position:relative; display:flex; flex-direction:column; }
.ind-card.catering{ grid-column:1/8; grid-row:1/3; }
.ind-card.catering .ind-photo{ aspect-ratio:16/10.5; }
.ind-card.elderly{ grid-column:8/13; grid-row:1/2; }
.ind-card.elderly .ind-photo{ aspect-ratio:16/9; }

.ind-photo{ position:relative; overflow:hidden; border-radius:var(--radius-lg); }
.ind-photo img{ width:100%; height:100%; object-fit:cover; transition:transform .6s var(--ease); }
.ind-card:hover .ind-photo img{ transform:scale(1.035); }
.ind-badge{ position:absolute; top:14px; left:14px; background:var(--ink); color:var(--paper); font-size:11.5px; font-weight:700; padding:5px 12px; letter-spacing:.04em; border-radius:var(--radius-sm); }
.ind-body{ padding-top:18px; display:flex; justify-content:space-between; align-items:flex-start; gap:16px; }
.ind-body h3{ font-size:1.4rem; margin-bottom:8px; }
.ind-body p{ font-size:14px; color:var(--ink-soft); max-width:42ch; }
.ind-cta{ flex-shrink:0; font-size:13.5px; font-weight:700; color:var(--red); display:flex; align-items:center; gap:6px; white-space:nowrap; }
.ind-card.soon .ind-cta{ color:var(--ink-faint); }

/* the third cell — cutout collage instead of a photo or a "+7" placeholder.
   Flipped to the drenched red / cream-text treatment used in the
   "了解您的業務" band (.spacious) rather than the jade wash, so the
   homepage's two red moments echo each other instead of introducing
   a third, one-off colour. */
.ind-empty.cutout-cell{
  grid-column:8/13; grid-row:2/3; position:relative; background:var(--red);
  border-radius:var(--radius-lg); padding:26px 24px; display:flex; flex-direction:column; justify-content:flex-end; overflow:visible;
}
.cutout-cell-graphic{ position:absolute; z-index:2; top:-42px; right:6px; width:234px; height:198px; transform:rotate(-6deg); filter:drop-shadow(0 14px 22px rgba(34,27,23,.18)); }
.cutout-cell-graphic img{ width:100%; height:100%; object-fit:contain; }
/* pale-yellow radiating sunburst — kept INSIDE the red cell (top-right),
   dimmed ~20% and radially masked so it never reaches the cell edges. */
.ind-empty.cutout-cell::after{
  content:""; position:absolute; z-index:0; top:8%; right:5%; height:84%; aspect-ratio:1;
  background:repeating-conic-gradient(from 0deg at 50% 50%,
     color-mix(in oklch, #F2D98A 50%, transparent) 0deg 4.5deg,
     transparent 4.5deg 9deg);
  -webkit-mask:radial-gradient(circle at 50% 50%, #000 6%, transparent 62%);
          mask:radial-gradient(circle at 50% 50%, #000 6%, transparent 62%);
  pointer-events:none;
}
.cutout-cell-body{ position:relative; z-index:1; }
.cutout-cell-body h3{ font-size:1.2rem; color:var(--paper); margin-bottom:6px; }
.cutout-cell-body p{ font-size:13px; color:rgba(241,228,213,.82); max-width:32ch; }

/* ============================================================
   OTHER LINES — the seven remaining lines as plain bordered text
   chips, deliberately without icons or colour badges: a repeated
   icon-in-circle row is the generic SaaS-feature tell this
   direction is avoiding.
   ============================================================ */
.other-lines{ padding-block:0 clamp(56px,8vw,96px); }
.other-lines-row{ grid-column:1/13; display:flex; flex-wrap:wrap; gap:12px; }
.line-chip{ border:1px solid var(--line-strong); border-radius:999px; padding:14px 24px; font-size:14.5px; font-weight:600; background:var(--surface); color:var(--ink); }
.solutions-more{ grid-column:1/13; margin-top:24px; font-size:14px; color:var(--ink-soft); }
.solutions-more a{ color:var(--red); font-weight:700; }

@media (max-width: 980px){
  .ind-card.catering,.ind-card.elderly,.ind-empty.cutout-cell{ grid-column:1/13; grid-row:auto; }
  .ind-empty.cutout-cell{ padding-top:96px; }
  .cutout-cell-graphic{ top:-64px; right:8px; width:270px; height:227px; }
}
@media (max-width: 620px){
  .line-chip{ padding:12px 18px; font-size:13.5px; }
}

/* ============================================================
   SPACIOUS TEXT-ONLY SECTION — title + text only, generous
   whitespace, restrained hairline rules. No photo, no dark
   inversion, no pattern fill.
   ============================================================ */
/* a drenched moment — full saturated red, cream display type flipped
   against it, echoing the Jones BBQ reference's alternating red/cream
   rhythm instead of red staying confined to buttons and one stat block */
.spacious{ background:var(--red); }
.spacious-lead{ grid-column:1/6; }
.spacious-lead .caption{ display:block; margin-bottom:16px; color:#F1D6C9; }
.spacious-lead h2{ font-size:clamp(1.9rem,3.2vw,2.6rem); color:var(--paper); }
.spacious-lead p{ color:rgba(241,228,213,.82); font-size:15.5px; margin-top:20px; max-width:40ch; line-height:1.8; }
.spacious-points{ grid-column:7/13; display:flex; flex-direction:column; }
.spacious-point{ padding-block:26px; border-top:1px solid rgba(241,228,213,.25); }
.spacious-point:last-child{ border-bottom:1px solid rgba(241,228,213,.25); }
.spacious-point h3{ font-size:17px; font-weight:700; font-family:var(--font-body); margin-bottom:8px; color:var(--paper); }
.spacious-point p{ color:rgba(241,228,213,.78); font-size:14px; max-width:52ch; }
@media (max-width: 980px){ .spacious-lead,.spacious-points{ grid-column:1/7; } }
@media (max-width: 620px){ .spacious-lead,.spacious-points{ grid-column:1/5; } }

/* ============================================================
   PARTNERS
   ============================================================ */
.partners-grid{ grid-column:1/13; display:grid; grid-template-columns:repeat(5,1fr); gap:var(--gap); }
.partner-mark{ aspect-ratio:16/10; display:flex; align-items:center; justify-content:center; background:var(--surface); border:1px solid var(--line-strong); border-radius:var(--radius-md); color:var(--ink-soft); }
.partners-note{ grid-column:1/13; font-size:12.5px; color:var(--ink-faint); margin-top:20px; }
@media (max-width: 620px){ .partners-grid{ grid-template-columns:repeat(2,1fr); } }

/* ============================================================
   CTA BANNER
   ============================================================ */
.cta-banner{ background:var(--red); color:#fff; padding-block:clamp(52px,7vw,88px); }
.cta-banner .grid12{ align-items:center; }
.cta-copy{ grid-column:1/8; }
.cta-copy h2{ color:var(--paper); font-size:clamp(1.9rem,3.4vw,2.6rem); }
.cta-copy p{ color:rgba(241,228,213,.82); margin-top:12px; font-size:15px; }
.cta-actions{ grid-column:9/13; display:flex; flex-direction:column; gap:12px; align-items:stretch; }
.cta-actions a{ justify-content:center; }
@media (max-width: 980px){ .cta-copy,.cta-actions{ grid-column:1/7; } .cta-actions{ flex-direction:row; margin-top:20px; } }
@media (max-width: 620px){ .cta-copy,.cta-actions{ grid-column:1/5; } .cta-actions{ flex-direction:column; } }

/* ============================================================
   FOOTER
   ============================================================ */
.footer{ background:var(--ink); color:var(--paper); padding-top:clamp(52px,7vw,76px); }
.footer-brand-cell{ grid-column:1/5; }
.footer-brand{ display:flex; align-items:center; gap:12px; margin-bottom:18px; }
.footer-brand img{ width:32px; height:32px; filter:invert(1) grayscale(1) brightness(2); }
/* reversed lockup: same wording + font style as the official artwork, but the
   red is swapped for cream — brand red on the dark footer fails contrast */
.footer-brand-zh{ font-family:var(--font-logo-zh); font-weight:700; font-size:15.5px; letter-spacing:.02em; }
.footer-brand-en{ font-family:var(--font-logo-en); font-weight:700; font-size:10.5px; color:rgba(248,242,237,.72); letter-spacing:.005em; text-transform:none; }
.footer-brand-cell p{ font-size:13.5px; color:rgba(248,242,237,.68); max-width:34ch; margin-bottom:14px; }
.footer-placeholder{ font-size:11.5px; color:#E8B6A3; border:1px solid rgba(232,182,163,.4); padding:8px 12px; display:inline-block; border-radius:var(--radius-sm); }
.footer-col{ grid-column:span 3; }
.footer-col h4{ font-family:var(--font-body); font-weight:700; font-size:13px; letter-spacing:.05em; text-transform:uppercase; color:rgba(248,242,237,.55); margin-bottom:16px; }
.footer-col ul{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:11px; }
.footer-col a{ font-size:14px; color:rgba(248,242,237,.85); }
.footer-col a.on{ color:#E8B6A3; }
.footer-col a.unavailable{ color:rgba(248,242,237,.4); cursor:not-allowed; }
/* padding-bottom clears the fixed WhatsApp button, which used to sit on top of
   the licence number when scrolled to the very end of the page */
.footer-bottom{ grid-column:1/13; border-top:1px solid rgba(248,242,237,.14); margin-top:24px; padding-top:20px; padding-bottom:100px; display:flex; justify-content:space-between; gap:20px; font-size:12px; color:rgba(248,242,237,.55); }
@media (max-width: 980px){ .footer-brand-cell{ grid-column:1/7; } .footer-col{ grid-column:span 3; } }
@media (max-width: 620px){ .footer-brand-cell,.footer-col{ grid-column:1/5; } .footer-bottom{ flex-direction:column; gap:8px; padding-bottom:86px; } }

/* ============================================================
   PRODUCT PAGE — HERO
   ============================================================ */
.hero-product{ padding-block:56px clamp(56px,8vw,92px); }
.hero-product .grid12{ align-items:center; }
.hp-headline{ grid-column:1/7; display:flex; flex-direction:column; justify-content:center; }
.hp-headline .caption{ display:block; margin-bottom:16px; }
.hp-headline h1{ font-size:clamp(2.2rem,4.6vw,3.7rem); }
.hp-headline h1 em{ font-style:normal; color:var(--red); }
.hp-headline p{ color:var(--ink-soft); font-size:15.5px; margin-top:20px; max-width:44ch; }
.hp-ctas{ display:flex; gap:14px; margin-top:28px; flex-wrap:wrap; }
.hp-photo{ grid-column:7/13; }

/* rev67 — 其他保險 hero 冇咗相片（客戶第六輪：「hero 圖片拎走唔要」）。
   .oi-jump 本來 margin-top:-169px 爬上去疊住相片框；相片一冇，佢就跌咗落
   「立即免費諮詢」兩粒掣上面（實測疊 161px）。所以呢個 modifier 做三件事：
   數據列唔再留出相片位、右欄垂直置中、跳轉列回復正常流動。
   相鄰選擇器令 markup 只需加一個 class —— 中間嘅 HTML 註解唔算元素。 */
.hero-product.no-photo .hp-photo{ display:flex; align-items:center; }
.hero-product.no-photo .hp-stat-strip{ margin-top:0; width:100%; }
.hero-product.no-photo + .oi-jump{ margin-top:0; }
.hp-photo-frame{ position:relative; aspect-ratio:4/5; overflow:hidden; border-radius:var(--radius-lg); }
.hp-photo-frame img{ width:100%; height:100%; object-fit:cover; }
.hp-stat-strip{ display:flex; margin-top:var(--gap); background:var(--rose-wash); border-radius:var(--radius-md); overflow:hidden; }
/* min-width:0 —— flex 項目預設 min-width:auto，唔肯縮到細過內容嘅最闊單字。
   繁中每格四五個方塊字，字字同寬，三格自然平均；英文「Transparent」一個字就
   140px，第一格被撐到 276px，第二格淨返 140px，「Core Coverages」被迫斷兩行，
   成條數據列高矮不齊。加 min-width:0 之後 flex:1 先真係做到等闊（實測英文
   276/140/193 → 202/203/203，條高 116 → 88px）。繁中／简中量到完全不變。 */
.hp-stat{ flex:1; min-width:0; padding:16px 18px; text-align:left; }
.hp-stat + .hp-stat{ border-left:1px solid rgba(122,89,78,.16); }
.hp-stat strong{ display:block; font-family:var(--font-display); font-size:1.5rem; color:var(--red); }
.hp-stat span{ font-size:11.5px; color:var(--rose-deep); }
/* footnote qualifying the "15%+" saving claim — sits under the stat box */
.hp-stat strong sup{ font-size:.5em; top:-.75em; margin-left:1px; }
.hp-stat-note{ margin:10px 2px 0; font-size:11.5px; line-height:1.7; color:var(--ink-faint); }
@media (max-width: 980px){ .hp-headline,.hp-photo{ grid-column:1/7; } .hp-photo{ margin-top:28px; } }
@media (max-width: 620px){ .hp-headline,.hp-photo{ grid-column:1/5; } }

/* savings strip */
.savings{ background:var(--rose-deep); color:#fff; }
.savings .grid12{ padding-block:18px; align-items:center; }
.savings-msg{ grid-column:1/9; font-size:15px; font-weight:600; }
.savings-msg strong{ font-family:var(--font-display); font-size:1.2em; margin-right:8px; color:#F1D6C9; }
.savings-cta{ grid-column:10/13; text-align:right; font-weight:700; font-size:14px; text-decoration:underline; text-underline-offset:3px; }
@media (max-width: 980px){ .savings-msg,.savings-cta{ grid-column:1/7; } .savings-cta{ text-align:left; margin-top:6px; } }
@media (max-width: 620px){ .savings-msg,.savings-cta{ grid-column:1/5; } }

/* why-list (product page) — quiet cells, soft border, no letter tags */
.why-list-grid{ grid-column:1/13; display:grid; grid-template-columns:repeat(4,1fr); gap:var(--gap); }
.why-item{ padding:28px 24px; background:var(--surface); border:1px solid var(--line-strong); border-radius:var(--radius-md); }
.why-item h3{ font-size:16px; font-family:var(--font-body); font-weight:700; margin-bottom:8px; line-height:1.4; }
.why-item p{ font-size:13px; color:var(--ink-soft); }
@media (max-width: 980px){ .why-list-grid{ grid-template-columns:repeat(2,1fr); } }
@media (max-width: 620px){ .why-list-grid{ grid-template-columns:1fr; } }

/* coverage bento — soft-radius cards, one photo cell */
.coverage-grid{ grid-column:1/13; display:grid; grid-template-columns:repeat(12,1fr); grid-auto-rows:auto; column-gap:var(--gap); row-gap:var(--gap); }
.cov-card{ grid-column:span 6; border:1px solid var(--line-strong); border-radius:var(--radius-lg); padding:30px; display:flex; flex-direction:column; gap:16px; background:var(--surface); }
.cov-card.featured{ grid-column:span 7; background:var(--ink); color:var(--paper); border-color:transparent; }
.cov-card.photo{ grid-column:span 5; padding:0; overflow:hidden; position:relative; border:none; }
.cov-card.photo img{ width:100%; height:100%; object-fit:cover; min-height:220px; object-position:center 25%; }
.cov-card.photo .cov-photo-label{ position:absolute; bottom:0; left:0; right:0; background:linear-gradient(0deg, rgba(34,27,23,.78), transparent); color:#fff; padding:18px 20px; font-size:13px; font-weight:600; }
.cov-label{ font-family:var(--font-caption); font-style:italic; font-size:14px; color:var(--rose-deep); }
.cov-card.featured .cov-label{ color:#F1D6C9; }
.cov-card h3{ font-size:1.3rem; }
.cov-card.featured h3{ color:#fff; }
.cov-card p{ font-size:14px; color:var(--ink-soft); }
.cov-card.featured p{ color:rgba(248,242,237,.72); }
.cov-tags{ display:flex; flex-wrap:wrap; gap:8px; margin-top:auto; }
.pill{ font-size:12px; font-weight:700; padding:6px 13px; background:var(--red-wash); color:var(--red-deep); border-radius:999px; }
.cov-card.featured .pill{ background:rgba(241,214,201,.14); color:#F1D6C9; }
@media (max-width: 980px){ .cov-card,.cov-card.featured,.cov-card.photo{ grid-column:span 6; } }
@media (max-width: 620px){ .cov-card,.cov-card.featured,.cov-card.photo{ grid-column:span 4; } }

.trust-row{ grid-column:1/13; display:flex; gap:0; margin-top:8px; }
.trust-item{ flex:1; display:flex; align-items:center; gap:12px; padding:20px var(--gap) 0 0; border-left:1px solid var(--line); padding-left:var(--gap); }
.trust-item:first-child{ border-left:none; padding-left:0; }
.trust-item strong{ display:block; font-size:14.5px; }
.trust-item span{ font-size:12.5px; color:var(--ink-soft); }
@media (max-width: 620px){ .trust-row{ flex-direction:column; } .trust-item{ border-left:none; padding-left:0; border-top:1px solid var(--line); padding:16px 0 0; } }

/* quote form */
.form-section{ background:var(--surface-sunk); }
.form-intro-cell{ grid-column:1/5; }
.form-intro-cell h2{ font-size:clamp(1.8rem,3vw,2.4rem); }
.form-intro-cell p{ color:var(--ink-soft); font-size:14.5px; margin-top:16px; max-width:36ch; }
.promise{ margin-top:26px; display:flex; flex-direction:column; gap:12px; }
.promise-item{ display:flex; align-items:center; gap:10px; font-size:13.5px; color:var(--ink-soft); }
.promise-item svg{ color:var(--red); flex-shrink:0; }
.quote-card{ grid-column:5/13; background:var(--surface); border-radius:var(--radius-lg); box-shadow:var(--shadow-md); padding:clamp(24px,3vw,44px); }
.step-bar{ display:flex; gap:6px; margin-bottom:12px; }
.step-dot{ flex:1; height:3px; background:var(--line-strong); border-radius:2px; }
.step-dot.done{ background:var(--red); }
.step-meta{ font-size:12.5px; color:var(--ink-soft); margin-bottom:26px; }
.step-meta b{ color:var(--ink); }
.f-step{ display:none; }
.f-step.on{ display:block; }
.f-step h3{ font-family:var(--font-body); font-weight:700; font-size:17px; margin-bottom:20px; }
.f-row{ display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.f-group{ margin-bottom:16px; }
.f-group label{ display:block; font-size:13px; font-weight:600; margin-bottom:7px; }
.req{ color:var(--red); }
.f-group input,.f-group select{
  width:100%; border:1px solid var(--line-strong); background:var(--paper); border-radius:var(--radius-sm);
  padding:12px 14px; font-family:inherit; font-size:14.5px; color:var(--ink);
}
.f-group input:focus,.f-group select:focus{ outline:2px solid var(--red); outline-offset:1px; }
/* 分組清單嘅組名。瀏覽器預設出粗斜體 —— 中文冇真斜體，靠系統硬斜出嚟好核突，
   所以扳返正體，用字重同灰度分層次。 */
.f-group select optgroup{ font-style:normal; font-weight:700; color:var(--ink-soft); }
.f-group select optgroup option{ font-weight:400; color:var(--ink); }
.f-hint{ font-size:12px; color:var(--ink-faint); margin-top:6px; }
/* conditional claims table — marked with a rule so it reads as a follow-up to
   the 是/否 question rather than as another top-level block of the form */
.claim-details[hidden]{ display:none; }
.claim-details{ padding-left:16px; border-left:2px solid var(--red-wash); margin-bottom:4px; }
@media (max-width:620px){ .claim-details{ padding-left:12px; } }
.upload-box{ border:1px dashed var(--line-strong); border-radius:var(--radius-md); padding:26px 14px; text-align:center; color:var(--ink-soft); font-size:13px; }
.upload-box svg{ margin-bottom:8px; color:var(--ink-faint); }
.f-nav{ display:flex; justify-content:space-between; align-items:center; margin-top:8px; }
.f-back{ background:none; border:none; font-size:14px; font-weight:700; color:var(--ink-soft); padding:14px 0; }
.f-next,.f-submit{ display:inline-flex; align-items:center; gap:8px; background:var(--red); color:#fff; font-weight:700; font-size:14.5px; padding:13px 24px; border:none; border-radius:var(--radius-sm); margin-left:auto; }
.f-next:hover,.f-submit:hover{ background:var(--red-deep); }
@media (max-width: 980px){ .form-intro-cell,.quote-card{ grid-column:1/7; } }
@media (max-width: 620px){ .form-intro-cell,.quote-card{ grid-column:1/5; } .f-row{ grid-template-columns:1fr; } }

/* faq */
.faq{ background:var(--surface); }
.faq-head-cell{ grid-column:1/13; margin-bottom:28px; }
.faq-list{ grid-column:1/9; }
.faq-item{ border-top:1px solid var(--line-strong); }
.faq-item:last-child{ border-bottom:1px solid var(--line-strong); }
.faq-q{ width:100%; display:flex; justify-content:space-between; align-items:center; background:none; border:none; padding:22px 0; text-align:left; font-size:15.5px; font-weight:600; color:var(--ink); }
.faq-q svg{ flex-shrink:0; transition:transform .3s var(--ease); color:var(--ink-soft); }
.faq-q[aria-expanded="true"] svg{ transform:rotate(180deg); }
.faq-a{ display:grid; grid-template-rows:0fr; transition:grid-template-rows .3s var(--ease); }
.faq-a.open{ grid-template-rows:1fr; }
.faq-a-in{ overflow:hidden; font-size:14.5px; color:var(--ink-soft); padding-right:40px; }
.faq-a.open .faq-a-in{ padding-bottom:22px; }
@media (max-width: 980px){ .faq-list{ grid-column:1/7; } }
@media (max-width: 620px){ .faq-list{ grid-column:1/5; } }

/* ---------- scroll reveal ---------- */
[data-reveal]{ opacity:0; transform:translateY(18px); transition:opacity .6s var(--ease), transform .6s var(--ease); }
[data-reveal].in{ opacity:1; transform:none; }

/* ============================================================
   MIXED-MEDIA accents — CLIENT-APPROVED design (2026-07-29, rev7)
   1) 「信」grey watermark (weak)   2) hero white-card halftone
   3) point-cards: warm-tinted bg + red rounded-tile icon holding a real
      object cut-out that protrudes past the tile; corner gradient kept
   4) 「了解您的業務」red band: coiled measuring-tape cutout + deep-red bloom
   ============================================================ */

.cell-hz-wm{ position:absolute; inset:0; overflow:hidden; z-index:0; border-radius:inherit; pointer-events:none; }
.cell-hz-wm span{
  position:absolute; right:8%; left:auto; top:50%; transform:translateY(-50%);
  font-family:var(--font-display); font-weight:900; line-height:.72;
  font-size:clamp(230px, 27vw, 380px); color:var(--ink); opacity:.04;
}
.cell-hz-wm::after{
  content:""; position:absolute; right:0; bottom:0; width:54%; height:64%;
  background:var(--red); opacity:.17; pointer-events:none;
  -webkit-mask:url('assets/halftone-corner.png') bottom right / contain no-repeat;
          mask:url('assets/halftone-corner.png') bottom right / contain no-repeat;
}
.cell-headline .caption, .cell-headline h1, .cell-headline p, .cell-headline .btn-primary{ position:relative; z-index:1; }

/* (3) point-cards — warm-tinted background, a level down from white */
.point-card{
  background:color-mix(in oklch, var(--rose) 14%, white);
  border-color:color-mix(in oklch, var(--rose) 22%, white);
  overflow:hidden;
}
/* corner geometric gradient (circle + rotated rounded-square), kept */
.point-card::before{
  content:""; position:absolute; top:-40px; right:-24px; width:116px; height:116px;
  border-radius:50%; z-index:0; pointer-events:none;
  background:radial-gradient(circle at 34% 66%,
     color-mix(in oklch, var(--rose) 30%, white) 0%,
     color-mix(in oklch, var(--rose) 12%, white) 46%, transparent 72%);
}
.point-card::after{
  content:""; position:absolute; top:-14px; right:-42px; width:92px; height:92px;
  border-radius:20px; transform:rotate(20deg); z-index:0; pointer-events:none;
  background:linear-gradient(150deg,
     color-mix(in oklch, var(--red) 20%, white) 0%,
     color-mix(in oklch, var(--red) 9%, white) 40%, transparent 74%);
}
.point-card > *{ position:relative; z-index:1; }
/* the icon is now a red rounded-square tile holding a real object cut-out
   that protrudes past the tile's top edge (mixed-media). */
.point-tile{
  position:relative; width:46px; height:46px; border-radius:13px;
  background:var(--red); margin-bottom:18px;
  box-shadow:0 6px 14px rgba(178,10,48,.22);
}
/* the 4 objects are pre-normalised to a uniform square canvas, so one size
   here renders them balanced; bottom-aligned so each protrudes evenly. */
.point-tile img{
  position:absolute; left:50%; bottom:-2px; transform:translateX(-50%);
  width:64px; height:64px; object-fit:contain; object-position:center bottom;
  filter:drop-shadow(0 3px 5px rgba(34,27,23,.30));
}
/* titles one level larger — then +30% again on client request (16.5 → 21.5) */
.point-card strong{ font-size:21.5px; }

/* title sits BESIDE the tile rather than under it, so the card reads as
   one "icon + 標題" line with the descriptive half on its own line below.
   The tile keeps its original y-position (it's still the first thing in the
   card), so the protruding cut-out crops exactly as it did before. The gap
   clears the widest cut-out's horizontal overhang (80px art on a 46px tile). */
.point-head{ display:flex; align-items:center; gap:22px; }
.point-head .point-tile{ margin-bottom:0; flex:0 0 auto; }
/* the client's 「X｜Y」 line is set as two lines instead of one: X is the
   title beside the tile, Y drops to its own line as a lede. Three weights
   now — title / lede / body — so the lede has to sit clearly between them. */
/* both halves of the client's 「X｜Y」 line are ONE title, so the lede takes
   the heading colour too — only the paragraph below stays body-coloured. */
.point-lede{ font-size:14px; font-weight:600; line-height:1.5; color:var(--heading); margin:0; }
.point-card span{ margin-top:-2px; }

/* (4) 「了解您的業務」red band — measuring-tape cutout + deep-red radial bloom */
.spacious{ position:relative; overflow:hidden; }
.spacious::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(58% 92% at 76% 52%, var(--red-deep) 0%, transparent 62%);
}
.spacious::after{
  content:""; position:absolute; top:50%; right:-3%; transform:translateY(-50%);
  width:min(600px,54%); aspect-ratio:1456/800; z-index:0; pointer-events:none;
  background:url('assets/tape-coil.png') center right / contain no-repeat;
  mix-blend-mode:multiply; opacity:.55;
}
.spacious .wrap{ position:relative; z-index:1; }

/* ---- rev9: coloured 2nd-level headings + centred icon chips ---- */
/* second-level section headings on light grounds → brand red (was ink/black).
   The red-ground headings (.spacious-lead h2, .cta-copy h2) keep their paper
   colour via their own higher-specificity rules. */
.section-head h2, .faq-head-cell h2{ color:var(--red); }

/* the seven other-line chips: centre the whole row, each chip gets a
   theme icon in brand red. */
.other-lines-row{ justify-content:center; }
.line-chip{ display:inline-flex; align-items:center; gap:8px; }
.line-chip svg{ width:17px; height:17px; color:var(--red); flex-shrink:0; }

/* ---- rev10: olive icon tiles + coloured card titles ---- */
:root{ --olive:#808233; }
.point-tile{ background:var(--olive); box-shadow:0 6px 14px rgba(128,130,51,.30); }
/* card-level titles (not just section h2) also take the brand colour, on
   their light grounds. Red-ground titles keep their own paper colour. */
.point-card strong{ color:var(--red); }
.ind-body h3{ color:var(--red); }

/* ---- rev11: lavender icon tiles + amber headings ---- */
:root{ --heading:#af6310; }
.point-tile{ background:#ceb8de; box-shadow:0 6px 14px rgba(150,120,175,.32); }
/* the coloured headings (section h2 + card titles) move from red → amber */
.section-head h2, .faq-head-cell h2, .point-card strong, .ind-body h3{ color:var(--heading); }

/* ---- rev12: bigger corner motif + catering-card motion overlay ---- */
/* enlarge the top-right corner illustration on the point-cards a little */
.point-card::before{ top:-52px; right:-30px; width:150px; height:150px; }
.point-card::after{  top:-20px; right:-54px; width:116px; height:116px; }

/* motion overlay on the 餐飲業綜合保險 photo — the four coverages cascade in
   on an infinite staggered loop (Motion principles in CSS: staggered
   entrance + spring easing + GPU transform/opacity only). */
.ind-card.catering .ind-photo{ position:relative; overflow:hidden; }
.svc-anim{
  position:absolute; left:16px; bottom:14px; z-index:2;
  display:flex; flex-direction:column; gap:8px; align-items:flex-start;
  pointer-events:none;
}
.svc-chip{
  display:inline-flex; align-items:center; gap:7px;
  font-family:var(--font-body); font-size:13px; font-weight:700; color:var(--ink);
  background:rgba(255,255,255,.92);
  border:1px solid rgba(255,255,255,.65);
  padding:7px 14px 7px 11px; border-radius:999px;
  box-shadow:0 6px 16px rgba(34,27,23,.20);
  white-space:nowrap; opacity:0; will-change:transform,opacity;
  animation:svcCascade 6s var(--ease) infinite;
  animation-delay:calc(var(--i) * 0.5s);
}
.svc-chip svg{ width:16px; height:16px; color:var(--red); flex-shrink:0; }
@keyframes svcCascade{
  0%   { opacity:0; transform:translateY(18px) scale(.94); }
  7%   { opacity:1; transform:translateY(0) scale(1); }
  70%  { opacity:1; transform:translateY(0) scale(1); }
  82%  { opacity:0; transform:translateY(-12px) scale(.98); }
  100% { opacity:0; transform:translateY(18px) scale(.94); }
}
@media (prefers-reduced-motion: reduce){
  .svc-chip{ animation:none; opacity:1; transform:none; }
}

/* ---- rev13: centred icons · per-card corner variation · glass chips right ---- */
/* objects are now centred in their canvas → display them centred in the tile */
.point-tile img{ top:50%; left:50%; bottom:auto; transform:translate(-50%,-50%);
  width:66px; height:66px; object-position:center; }

/* each card's top-right corner motif differs slightly (size / offset / angle) */
.point-card.pc-clock::before{ width:132px; height:132px; top:-46px; right:-20px; }
.point-card.pc-clock::after{  width:98px;  height:98px;  top:-10px; right:-46px; transform:rotate(-14deg); }
.point-card.pc-magnifier::before{ width:166px; height:166px; top:-62px; right:-40px; }
.point-card.pc-magnifier::after{  width:120px; height:120px; top:-26px; right:-62px; transform:rotate(33deg); }
.point-card.pc-tape::before{ width:120px; height:120px; top:-34px; right:-16px; }
.point-card.pc-tape::after{  width:134px; height:134px; top:-30px; right:-58px; transform:rotate(8deg); border-radius:30px; }

/* catering motion overlay → RIGHT side, vertically centred, slight L-R stagger,
   glassmorphic bubbles with white text (not solid white pills). */
.svc-anim{
  left:auto; right:20px; bottom:auto; top:50%; transform:translateY(-50%);
  align-items:flex-end; gap:10px;
}
.svc-chip{
  background:rgba(255,255,255,.14); -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  color:#fff; border:1px solid rgba(255,255,255,.34);
  box-shadow:0 8px 20px rgba(0,0,0,.20); text-shadow:0 1px 2px rgba(0,0,0,.25);
}
.svc-chip svg{ color:#fff; }
.svc-chip:nth-child(1){ margin-right:28px; }
.svc-chip:nth-child(2){ margin-right:2px; }
.svc-chip:nth-child(3){ margin-right:36px; }
.svc-chip:nth-child(4){ margin-right:12px; }

/* ---- rev14: per-card icon sizing ---- */
.point-card.pc-contract .point-tile img{ width:76px; height:76px; }  /* 客戶經理 — bigger */
.point-card.pc-clock .point-tile img{ width:54px; height:54px; }  /* 日曆 — smaller */
.point-card.pc-magnifier .point-tile img{ width:73px; height:73px; }  /* 證書 — a touch bigger */
.point-card.pc-tape .point-tile img{ width:73px; height:73px; }  /* 間尺 — a touch bigger */

/* ---- rev15: chip group nudged left + wider L-R spread; solution h3 → red ---- */
.svc-anim{ right:34px; }
.svc-chip:nth-child(1){ margin-right:50px; }
.svc-chip:nth-child(2){ margin-right:0; }
.svc-chip:nth-child(3){ margin-right:64px; }
.svc-chip:nth-child(4){ margin-right:20px; }
/* the two solution-card titles revert to brand red (section + point-card
   titles stay amber via the earlier rule). */
.ind-body h3{ color:var(--red); }

/* ---- elderly-home page: special-coverage highlights + staff checkboxes ---- */
.highlight-row{ grid-column:1/13; display:grid; grid-template-columns:1fr 1fr; gap:var(--gap); margin-top:6px; }
.highlight-item{ display:flex; gap:14px; align-items:flex-start; padding:22px 24px; border-radius:var(--radius-lg);
  background:var(--red-wash); border:1px solid color-mix(in oklch, var(--red) 16%, white); }
.highlight-item svg{ color:var(--red); flex-shrink:0; margin-top:2px; }
.highlight-item strong{ display:block; font-size:15.5px; font-weight:700; margin-bottom:4px; color:var(--ink); }
.highlight-item span{ font-size:13.5px; color:var(--ink-soft); line-height:1.6; }
@media (max-width:620px){ .highlight-row{ grid-template-columns:1fr; } }

.check-row{ display:flex; flex-wrap:wrap; gap:10px; }
.check{ display:inline-flex; align-items:center; gap:7px; font-size:13.5px; font-weight:500; color:var(--ink);
  border:1px solid var(--line-strong); border-radius:999px; padding:8px 14px; cursor:pointer; user-select:none; }
.check input{ accent-color:var(--red); width:15px; height:15px; }

/* ---- footer real-contact block ---- */
.footer-lines{ list-style:none; margin:14px 0 0; padding:0; }
.footer-lines li{ font-size:13px; color:rgba(241,228,213,.80); line-height:1.95; }
/* full-width regulatory disclosure — IA licence status + non-advice disclaimer.
   Spans the whole footer rather than the narrow brand column: it is long-form
   legal copy, and a 3-column-wide measure would set it as a cramped wall. */
.footer-legal{
  grid-column:1/13; margin-top:40px; padding-top:22px;
  border-top:1px solid rgba(248,242,237,.14);
  display:grid; gap:9px;
}
.footer-legal p{ margin:0; font-size:12px; line-height:1.9; color:rgba(241,228,213,.52); }
.footer-legal strong{ color:rgba(241,228,213,.74); font-weight:700; }

/* ---- rev16: bold per-icon sizing (overrides rev14) ---- */
/* manager is now a filled head-shoulders bust → reads big even in a small tile */
.point-card.pc-contract .point-tile img{ width:80px; height:80px; }
/* calendar clearly smaller, sits inside the tile */
.point-card.pc-clock .point-tile img{ width:44px; height:44px; }
/* certificate enlarged so it breaks out past the tile frame */
.point-card.pc-magnifier .point-tile img{ width:92px; height:92px; }
/* ruler enlarged so it breaks out past the tile frame */
.point-card.pc-tape .point-tile img{ width:100px; height:100px; }

/* ---- rev17: FIX — global img{max-width:100%} was clamping tile icons to the
   tile width (46px), so any width bigger than the tile had no effect. ---- */
.point-tile img{ max-width:none; }

/* ---- rev18: dial the icon sizes down ---- */
.point-card.pc-contract .point-tile img{ width:62px; height:62px; }  /* 經理 smaller */
.point-card.pc-clock .point-tile img{ width:38px; height:38px; }  /* 日曆 smaller still */
.point-card.pc-magnifier .point-tile img{ width:72px; height:72px; }  /* 證書 smaller */
.point-card.pc-tape .point-tile img{ width:80px; height:80px; }  /* 間尺 smaller */

/* ---- rev19: enlarge the clock (極速理賠) icon ---- */
.point-card.pc-clock .point-tile img{ width:60px; height:60px; }

/* ============================================================
   rev20: catering motion bubbles + 其他保險 chips as a red block
   forming an inverted-L with the 其他保險方案 cell
   ============================================================ */
/* bubbles: bigger, nudged further left, wider left-right spacing */
.svc-anim{ right:50px; gap:15px; }
.svc-chip{ font-size:14px; padding:9px 17px 9px 13px; }
.svc-chip svg{ width:17px; height:17px; }
.svc-chip:nth-child(1){ margin-right:72px; }
.svc-chip:nth-child(2){ margin-right:0; }
.svc-chip:nth-child(3){ margin-right:92px; }
.svc-chip:nth-child(4){ margin-right:28px; }

/* pull the chips up close to the solutions block */
.coverage-index{ padding-bottom:0; }
/* square the 其他保險方案 cell's bottom so it merges into the chips block */
.ind-empty.cutout-cell{ border-bottom-left-radius:0; border-bottom-right-radius:0; }

/* the chips live inside a red block; square its top-right corner so the red
   is continuous with the cell above → one inverted-L shape. */
.other-lines-row{
  background:var(--red); border-radius:var(--radius-lg); border-top-right-radius:0;
  padding:24px clamp(18px,3vw,34px); justify-content:center; gap:12px;
}
.line-chip{ background:rgba(255,255,255,.10); border-color:rgba(255,255,255,.42); color:#fff; }
.line-chip svg{ color:#fff; }
.solutions-more{ margin-top:18px; }

/* ============================================================
   rev21: 其他保險 umbrella illustration + chips block polish + L inner fillet
   ============================================================ */
/* new umbrella illustration — smaller, tucked in the top-right corner so it
   never overlaps the cell copy on the left */
.cutout-cell-graphic{ width:154px; height:154px; top:-26px; right:14px; transform:rotate(0deg); }
.cutout-cell-graphic img{ object-position:top center; }

/* chips block: more vertical breathing room; text follows the cell copy colour */
.other-lines-row{ padding:34px clamp(22px,3vw,40px); }
.line-chip{ color:var(--paper); border-color:rgba(241,228,213,.42); background:rgba(255,255,255,.08); }
.line-chip svg{ color:var(--paper); }

/* soften the inner (concave) corner of the inverted-L where the cell meets
   the chips block. A red quarter-disc fillet bridges the right-angle.
   (cell starts at grid col 8/13 → ~58% across the row.) */
.other-lines{ position:relative; }
.other-lines-row{ position:relative; overflow:visible; }
.other-lines-row::before{
  content:""; position:absolute; top:0; left:58.3%; width:26px; height:26px;
  transform:translate(-100%,-100%); background:var(--red); pointer-events:none;
  -webkit-mask:radial-gradient(circle at 0 0, transparent 26px, #000 26.5px);
          mask:radial-gradient(circle at 0 0, transparent 26px, #000 26.5px);
}

/* ---- rev22: L inner fillet anchored to the cell (touches both edges) + spacing ---- */
/* remove the guessed %-position fillet */
.other-lines-row::before{ display:none; }
/* concave fillet at the cell's own bottom-left → guaranteed to meet the cell
   edge and the chips block top, bridging the inverted-L's inner corner. */
.ind-empty.cutout-cell{ overflow:visible; }
.ind-empty.cutout-cell::before{
  content:""; position:absolute; left:0; bottom:0; width:22px; height:22px;
  transform:translateX(-100%); pointer-events:none; z-index:2;
  background:radial-gradient(circle at top left, transparent 22px, var(--red) 22.5px);
}
/* more breathing room: space the red block away from the section below, and
   give the chips row a touch more room from the cards above. */
.coverage-index{ padding-bottom:0; }
.other-lines{ padding-top:clamp(14px,2vw,22px); padding-bottom:clamp(84px,11vw,140px); }
.solutions-more{ margin-top:28px; }

/* ---- rev22b: keep the L connected — no top gap on the chips block ---- */
.other-lines{ padding-top:0; }

/* ============================================================
   rev23: solution-card body padding bump (client request 2026-07-29)
   the 餐飲/安老 cards' text block sat too tight under the photo —
   give it clearly more breathing room above and a little below.
   ============================================================ */
.ind-card.catering .ind-body,
.ind-card.elderly .ind-body{ padding:30px 2px 8px; }

/* ============================================================
   rev24: full-site content build (2026-07-29) — testimonials,
   about page, other-insurance page, contact page, quote page,
   privacy page, consent checkbox. All on the existing system.
   ============================================================ */

/* ---- personal-data consent block (all forms) ---- */
.consent{ margin-top:6px; padding:16px 18px; background:var(--paper); border:1px solid var(--line-strong); border-radius:var(--radius-md); }
.consent p{ font-size:12px; color:var(--ink-soft); line-height:1.7; }
.consent-check{ display:flex; align-items:flex-start; gap:10px; margin-top:12px; font-size:13px; font-weight:600; color:var(--ink); cursor:pointer; }
.consent-check input{ accent-color:var(--red); width:17px; height:17px; margin-top:2px; flex-shrink:0; }
.consent-check .req{ color:var(--red); }

/* ---- testimonials ---- */
.reviews{ background:var(--surface-sunk); }
.reviews-grid{ grid-column:1/13; display:grid; grid-template-columns:repeat(3,1fr); gap:var(--gap); }
.review-card{ background:var(--surface); border:1px solid var(--line-strong); border-radius:var(--radius-lg); padding:30px 28px; display:flex; flex-direction:column; gap:16px; }
.review-stars{ display:flex; gap:3px; color:var(--red); }
.review-stars svg{ width:17px; height:17px; }
.review-card blockquote{ margin:0; font-size:14.5px; line-height:1.85; color:var(--ink); }
.review-author{ margin-top:auto; padding-top:8px; border-top:1px solid var(--line); font-size:13px; font-weight:700; color:var(--ink); }
.review-author span{ display:block; font-weight:500; font-size:12.5px; color:var(--ink-soft); margin-top:2px; }
@media (max-width: 980px){ .reviews-grid{ grid-template-columns:1fr; } }

/* ---- about page ---- */
.about-lead-cell{ grid-column:1/13; }
.about-lead-cell .caption{ display:block; margin-bottom:16px; }
.about-lead-cell h1{ font-size:clamp(2.1rem,4.4vw,3.4rem); }
.about-lead-cell h1 em{ font-style:normal; color:var(--red); }
.about-lead-cell .about-sub{ font-family:var(--font-caption); font-style:italic; font-size:clamp(1.1rem,2vw,1.5rem); color:var(--rose-deep); margin-top:18px; max-width:32ch; line-height:1.5; }
.about-story{ grid-column:1/8; }
.about-story p{ color:var(--ink-soft); font-size:15.5px; line-height:1.9; margin-bottom:20px; }
.about-aside{ grid-column:9/13; display:flex; flex-direction:column; gap:var(--gap); }
.about-fact{ background:var(--surface); border:1px solid var(--line-strong); border-radius:var(--radius-lg); padding:26px 24px; }
.about-fact.brandred{ background:var(--red); border-color:transparent; }
.about-fact .fact-label{ font-family:var(--font-caption); font-style:italic; font-size:13px; color:var(--rose-deep); margin-bottom:8px; }
.about-fact.brandred .fact-label{ color:#F1D6C9; }
.about-fact strong{ display:block; font-family:var(--font-display); font-size:1.5rem; color:var(--ink); line-height:1.25; }
.about-fact.brandred strong{ color:#fff; }
.about-fact p{ font-size:13px; color:var(--ink-soft); margin-top:8px; line-height:1.7; }
.about-fact.brandred p{ color:rgba(241,228,213,.86); }
@media (max-width: 980px){ .about-story,.about-aside{ grid-column:1/7; } .about-aside{ margin-top:8px; flex-direction:row; flex-wrap:wrap; } .about-fact{ flex:1; min-width:220px; } }
@media (max-width: 620px){ .about-story,.about-aside{ grid-column:1/5; } }

/* director / member card */
.member-row{ grid-column:1/13; display:grid; grid-template-columns:repeat(2,1fr); gap:var(--gap); }
.member-card{ background:var(--surface); border:1px solid var(--line-strong); border-radius:var(--radius-lg); padding:28px 26px; display:flex; align-items:center; gap:20px; }
.member-monogram{ width:60px; height:60px; flex-shrink:0; border-radius:50%; background:var(--red); color:#fff; display:flex; align-items:center; justify-content:center; font-family:var(--font-display); font-weight:900; font-size:1.4rem; }
.member-card .m-role{ font-family:var(--font-caption); font-style:italic; font-size:13px; color:var(--rose-deep); }
.member-card .m-name{ font-family:var(--font-display); font-weight:900; font-size:1.3rem; color:var(--ink); }
@media (max-width: 620px){ .member-row{ grid-template-columns:1fr; } }

/* carrier / partner marks. 四個承保夥伴嘅官方標誌已全部到齊（豐隆保險 2026-09-28
   最後交來），文字 lockup 功成身退。
   標誌用 max-% 而唔係固定尺寸：四個檔案嘅比例由 5.9:1（亞洲保險橫排）去到
   1.2:1（忠意保險上下排），統一約束闊同高先至令視覺份量睇齊。 */
.partner-mark img{ max-width:74%; max-height:56%; width:auto; height:auto; object-fit:contain; }

/* ---- other-insurance page: line-of-business list ---- */
.lob-list{ grid-column:1/13; display:flex; flex-direction:column; }
.lob-item{ display:grid; grid-template-columns:64px 1fr; gap:26px; padding:38px 0; border-top:1px solid var(--line-strong); }
.lob-item:last-child{ border-bottom:1px solid var(--line-strong); }
.lob-icon{ width:56px; height:56px; border-radius:16px; background:var(--red-wash); color:var(--red); display:flex; align-items:center; justify-content:center; }
.lob-icon svg{ width:28px; height:28px; }
.lob-body h3{ font-size:1.35rem; color:var(--heading); margin-bottom:6px; }
.lob-num{ font-family:var(--font-caption); font-style:italic; font-size:13px; color:var(--rose-deep); display:block; margin-bottom:10px; }
.lob-body p{ font-size:14.5px; color:var(--ink-soft); line-height:1.9; max-width:70ch; }
.lob-tags{ display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; }
@media (max-width: 620px){ .lob-item{ grid-template-columns:1fr; gap:14px; } .lob-icon{ width:48px; height:48px; } }

/* ---- contact page ---- */
.contact-info-cell{ grid-column:1/5; }
.contact-form-cell{ grid-column:5/13; }
.contact-lines{ list-style:none; margin:24px 0 0; padding:0; display:flex; flex-direction:column; gap:2px; }
.contact-lines li{ display:flex; gap:14px; align-items:flex-start; padding:16px 0; border-bottom:1px solid var(--line); font-size:14.5px; color:var(--ink); }
.contact-lines li:first-child{ border-top:1px solid var(--line); }
.contact-lines .ci-ic{ width:22px; height:22px; color:var(--red); flex-shrink:0; margin-top:1px; }
.contact-lines .ci-k{ font-size:12px; color:var(--ink-faint); display:block; margin-bottom:2px; }
.contact-lines a{ color:var(--ink); font-weight:600; }
.contact-lines a:hover{ color:var(--red); }
.contact-actions{ display:flex; gap:12px; margin-top:24px; flex-wrap:wrap; }
.contact-map{ grid-column:1/13; margin-top:var(--gap); border-radius:var(--radius-lg); overflow:hidden; border:1px solid var(--line-strong); }
.contact-map iframe{ width:100%; height:clamp(300px,40vw,440px); border:0; display:block; filter:grayscale(.25) contrast(1.02); }
.contact-card{ background:var(--surface); border-radius:var(--radius-lg); box-shadow:var(--shadow-md); padding:clamp(24px,3vw,44px); }
@media (max-width: 980px){ .contact-info-cell,.contact-form-cell{ grid-column:1/7; } .contact-form-cell{ margin-top:24px; } }
@media (max-width: 620px){ .contact-info-cell,.contact-form-cell{ grid-column:1/5; } }

/* trust badges strip (contact / quote) */
.trust-badges{ grid-column:1/13; display:flex; flex-wrap:wrap; gap:10px; margin-top:var(--gap); }
.trust-badge{ display:inline-flex; align-items:center; gap:8px; font-size:13px; font-weight:600; color:var(--ink-soft); background:var(--surface); border:1px solid var(--line-strong); border-radius:999px; padding:10px 18px; }
.trust-badge svg{ width:16px; height:16px; color:var(--red); }

/* ---- quote page sidebar ---- */
.quote-side{ grid-column:9/13; }
.quote-main{ grid-column:1/9; }
.quote-side-card{ background:var(--red); color:#fff; border-radius:var(--radius-lg); padding:30px 28px; position:sticky; top:100px; }
.quote-side-card h3{ color:#fff; font-size:1.3rem; margin-bottom:20px; }
.quote-side-card ul{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:18px; }
.quote-side-card li strong{ display:block; font-size:14.5px; color:#fff; margin-bottom:4px; }
.quote-side-card li span{ font-size:13px; color:rgba(241,228,213,.82); line-height:1.6; }
.quote-side-card li{ padding-left:26px; position:relative; }
.quote-side-card li::before{ content:""; position:absolute; left:0; top:4px; width:15px; height:15px; border-radius:50%; background:rgba(255,255,255,.16); background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:center; }
@media (max-width: 980px){ .quote-main,.quote-side{ grid-column:1/7; } .quote-side{ margin-top:24px; } .quote-side-card{ position:static; } }
@media (max-width: 620px){ .quote-main,.quote-side{ grid-column:1/5; } }

/* ---- textarea (shared) ---- */
.f-group textarea{ width:100%; border:1px solid var(--line-strong); background:var(--paper); border-radius:var(--radius-sm); padding:12px 14px; font-family:inherit; font-size:14.5px; color:var(--ink); resize:vertical; min-height:96px; }
.f-group textarea:focus{ outline:2px solid var(--red); outline-offset:1px; }

/* ---- legal / privacy prose ---- */
.legal-head{ grid-column:1/13; margin-bottom:clamp(28px,4vw,44px); }
.legal-head h1{ font-size:clamp(2rem,3.6vw,2.8rem); }
.legal-head .caption{ display:block; margin-bottom:14px; }
.legal-body{ grid-column:1/9; }
.legal-body p{ font-size:14.5px; color:var(--ink-soft); line-height:1.95; margin-bottom:18px; }
.legal-body h2{ font-size:1.25rem; color:var(--heading); margin:36px 0 12px; }
.legal-body ul{ margin:0 0 18px; padding-left:20px; }
.legal-body li{ font-size:14.5px; color:var(--ink-soft); line-height:1.85; margin-bottom:8px; }
.legal-toc{ grid-column:10/13; }
.legal-toc-inner{ position:sticky; top:100px; border-left:1px solid var(--line); padding-left:18px; }
.legal-toc a{ display:block; font-size:13px; color:var(--ink-soft); padding:6px 0; }
.legal-toc a:hover{ color:var(--red); }
@media (max-width: 980px){ .legal-body{ grid-column:1/7; } .legal-toc{ display:none; } }
@media (max-width: 620px){ .legal-body{ grid-column:1/5; } }

/* ---- generic page hero (about/other/contact/quote/legal) ---- */
.page-hero{ padding-block:clamp(40px,5vw,64px) clamp(28px,3vw,40px); }
.page-hero .grid12{ align-items:end; }
.page-hero-cell{ grid-column:1/9; }
.page-hero-cell .caption{ display:block; margin-bottom:14px; }
.page-hero-cell h1{ font-size:clamp(2.1rem,4.4vw,3.4rem); }
.page-hero-cell h1 em{ font-style:normal; color:var(--red); }
.page-hero-cell p{ color:var(--ink-soft); font-size:15.5px; margin-top:18px; max-width:56ch; line-height:1.85; }
.page-hero-aside{ grid-column:9/13; }
@media (max-width: 980px){ .page-hero-cell,.page-hero-aside{ grid-column:1/7; } .page-hero-aside{ margin-top:20px; } }
@media (max-width: 620px){ .page-hero-cell,.page-hero-aside{ grid-column:1/5; } }

/* ---- WhatsApp floating button (site-wide, client-requested) ---- */
.wa-float{
  position:fixed; right:22px; bottom:30px; z-index:50;
  width:56px; height:56px; border-radius:50%;
  background:#25D366; color:#fff; display:flex; align-items:center; justify-content:center;
  box-shadow:0 10px 26px rgba(0,0,0,.24); transition:transform .2s var(--ease), box-shadow .2s var(--ease);
}
.wa-float:hover{ transform:translateY(-3px) scale(1.04); box-shadow:0 14px 32px rgba(0,0,0,.30); }
.wa-tip{
  position:absolute; right:70px; top:50%; transform:translateY(-50%);
  white-space:nowrap; background:var(--ink); color:var(--paper); font-size:12.5px; font-weight:600;
  padding:8px 14px; border-radius:var(--radius-sm); box-shadow:var(--shadow-sm);
  opacity:0; pointer-events:none; transition:opacity .25s var(--ease);
}
.wa-float:hover .wa-tip, .wa-float.tip-show .wa-tip{ opacity:1; }
@media (max-width:620px){ .wa-float{ right:16px; bottom:20px; width:52px; height:52px; } .wa-tip{ display:none; } }

/* info-note callout (non-offer statements) */
.info-note{ grid-column:1/13; margin-top:var(--gap); background:var(--rose-wash); border:1px solid color-mix(in oklch, var(--rose) 24%, white); border-radius:var(--radius-md); padding:20px 24px; font-size:12.5px; color:var(--rose-deep); line-height:1.75; display:flex; gap:12px; }
.info-note svg{ flex-shrink:0; color:var(--rose-deep); margin-top:1px; }

/* ============================================================
   rev26: PAGE MATERIALS — the homepage/product visual language
   (「信」watermark · halftone / sunburst / tape textures · floating
   cutout objects · photo frames) made reusable so the about /
   other-insurance / contact / quote / privacy pages carry the same
   mixed-media richness instead of reading as flat text.
   ============================================================ */

/* 「信」grey watermark — the hero signature, standalone */
.sign-wm{ position:absolute; inset:0; overflow:hidden; z-index:0; pointer-events:none; }
.sign-wm span{
  position:absolute; top:50%; right:5%; transform:translateY(-50%);
  font-family:var(--font-display); font-weight:900; line-height:.7;
  color:var(--ink); opacity:.045; font-size:clamp(190px, 23vw, 340px);
}
.sign-wm.left span{ right:auto; left:3%; }
.sign-wm.on-red span{ color:#fff; opacity:.07; }

/* red halftone-dot corner texture (bottom-right) */
.mat-halftone::after{
  content:""; position:absolute; right:0; bottom:0; width:min(44%,360px); height:62%;
  background:var(--red); opacity:.13; pointer-events:none; z-index:0;
  -webkit-mask:url('assets/halftone-corner.png') bottom right / contain no-repeat;
          mask:url('assets/halftone-corner.png') bottom right / contain no-repeat;
}

/* pale radiating sunburst — for red grounds */
.mat-sunburst{ position:relative; overflow:hidden; }
.mat-sunburst > *{ position:relative; z-index:1; }
.mat-sunburst::before{
  content:""; position:absolute; z-index:0; top:6%; right:4%; height:88%; aspect-ratio:1;
  background:repeating-conic-gradient(from 0deg at 50% 50%,
     color-mix(in oklch, #F2D98A 50%, transparent) 0deg 4.5deg,
     transparent 4.5deg 9deg);
  -webkit-mask:radial-gradient(circle at 50% 50%, #000 6%, transparent 62%);
          mask:radial-gradient(circle at 50% 50%, #000 6%, transparent 62%);
  pointer-events:none;
}
/* subtler burst for text-dense red panels — keep body text ≥4.5:1 */
.mat-sunburst.subtle::before{ opacity:.42; top:auto; bottom:-18%; right:-14%; height:80%; }

/* coiled measuring-tape multiply texture */
.mat-tape::after{
  content:""; position:absolute; top:50%; right:-3%; transform:translateY(-50%);
  width:min(560px,52%); aspect-ratio:1456/800; z-index:0; pointer-events:none;
  background:url('assets/tape-coil.png') center right / contain no-repeat;
  mix-blend-mode:multiply; opacity:.5;
}

/* a real object cut-out floating past a frame edge */
.cutout-float{ position:absolute; z-index:3; pointer-events:none;
  filter:drop-shadow(0 16px 26px rgba(34,27,23,.22)); }
.cutout-float img{ width:100%; height:100%; object-fit:contain; }

/* photo tile — rounded, hover-scale, optional bottom label (mixed-media) */
.media-frame{ position:relative; border-radius:var(--radius-lg); overflow:hidden; background:var(--surface-sunk); }
.media-frame::after{ content:""; position:absolute; inset:0; box-shadow:inset 0 0 0 1px var(--line-strong); border-radius:inherit; pointer-events:none; }
.media-frame img{ width:100%; height:100%; object-fit:cover; transition:transform .7s var(--ease); }
.media-frame:hover img{ transform:scale(1.04); }
.media-frame .mf-label{ position:absolute; left:0; right:0; bottom:0; z-index:1;
  background:linear-gradient(0deg, rgba(34,27,23,.82), transparent);
  color:#fff; padding:20px 22px 18px; font-size:13px; font-weight:600; }

/* hero aside that holds a cutout on a soft tinted panel (page-hero) */
.hero-object{
  position:relative; aspect-ratio:5/4; border-radius:var(--radius-lg);
  background:radial-gradient(120% 120% at 70% 20%, var(--rose-wash) 0%, var(--surface-sunk) 100%);
  border:1px solid var(--line-strong); overflow:visible;
}
.hero-object.red{ background:var(--red); border-color:transparent; }
.hero-object .cutout-float{ inset:-8% -6% -6% -6%; width:auto; height:auto; }
.hero-object .cutout-float img{ width:100%; height:100%; }
@media (max-width:980px){ .hero-object{ aspect-ratio:16/9; max-width:460px; } }

/* enriched line-of-business icon tile — lavender material tile echoing the
   homepage point-tiles (was a flat red-wash square) */
.lob-icon{ background:#ceb8de; color:var(--red-deep);
  box-shadow:0 8px 16px rgba(150,120,175,.30); position:relative; overflow:hidden; }
.lob-icon::before{
  content:""; position:absolute; top:-40%; right:-40%; width:100%; height:100%;
  border-radius:50%; background:radial-gradient(circle at 40% 60%,
     rgba(255,255,255,.55) 0%, transparent 62%); pointer-events:none;
}
.lob-icon svg{ position:relative; z-index:1; }
.lob-item{ transition:transform .3s var(--ease); }

/* soft dotted rule to break long text pages (privacy) */
.mat-dotline{ height:0; border:0; border-top:1.5px dotted var(--line-strong); margin:0; }

/* wide photo band — don't collapse to a sliver on narrow screens */
.media-frame.band{ aspect-ratio:21/8; }
@media (max-width:760px){ .media-frame.band{ aspect-ratio:16/10; } }
@media (max-width:620px){ .media-frame.band .mf-label{ font-size:13px; padding:18px 18px 16px; } }

/* decorative header seal (privacy) — step back on small screens so it never
   crowds the heading */
@media (max-width:820px){ .hero-seal{ width:100px !important; height:100px !important; opacity:.7 !important; } }
@media (max-width:560px){ .hero-seal{ display:none; } }
@media (max-width:560px){ .sign-wm span{ font-size:150px; } }

@media (prefers-reduced-motion: reduce){
  .media-frame img, .lob-item{ transition:none; }
}

/* ============================================================
   rev27: 其他保險 — line-of-business as a scannable CARD GRID
   (replaces the flat .lob-list). Each service is a white card on
   the cream page (clear figure-ground division), led by a real
   photo so the category reads instantly, with a brand-red icon
   badge — no lavender tile fighting the warm page ground.
   ============================================================ */
.lob-grid{ grid-column:1/13; display:grid; grid-template-columns:repeat(3,1fr); gap:var(--gap); }
@media (max-width:1080px){ .lob-grid{ grid-template-columns:repeat(2,1fr); } }
@media (max-width:640px){ .lob-grid{ grid-template-columns:1fr; } }

.lob-card{
  position:relative; display:flex; flex-direction:column; background:var(--surface);
  border:1px solid var(--line-strong); border-radius:var(--radius-lg); overflow:hidden;
  transition:transform .32s var(--ease), box-shadow .32s var(--ease), border-color .32s var(--ease);
}
.lob-card:hover{ transform:translateY(-5px); box-shadow:var(--shadow-md); border-color:color-mix(in oklch, var(--red) 30%, var(--line-strong)); }

/* media header — photo when present; a warm brand gradient otherwise, so a
   missing image still ships a designed card, never a grey rectangle */
.lob-media{ position:relative; aspect-ratio:16/10; overflow:hidden;
  background:linear-gradient(150deg, var(--red) 0%, var(--red-deep) 78%, #6b0a20 100%); }
.lob-media img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  transition:transform .7s var(--ease); }
.lob-card:hover .lob-media img{ transform:scale(1.055); }
.lob-media::after{ content:""; position:absolute; inset:0;
  background:linear-gradient(180deg, rgba(34,27,23,0) 34%, rgba(34,27,23,.34) 72%, rgba(34,27,23,.62) 100%); }
/* faint sunburst on the gradient fallback so it reads as brand, not a flat block */
.lob-media.plain::before{ content:""; position:absolute; z-index:0; inset:0;
  background:repeating-conic-gradient(from 0deg at 78% 30%,
     rgba(242,217,138,.22) 0deg 4.5deg, transparent 4.5deg 9deg);
  -webkit-mask:radial-gradient(circle at 78% 30%, #000 4%, transparent 58%);
          mask:radial-gradient(circle at 78% 30%, #000 4%, transparent 58%); }

.lob-tag-corner{ position:absolute; z-index:2; top:12px; right:12px;
  font-size:11px; font-weight:700; letter-spacing:.02em; color:#fff;
  background:rgba(34,27,23,.42); -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px);
  border:1px solid rgba(255,255,255,.28); border-radius:999px; padding:5px 12px; }

.lob-badge{ position:absolute; z-index:2; left:16px; bottom:14px; right:16px;
  display:flex; align-items:center; gap:12px; }
.lob-badge-ic{ flex-shrink:0; width:44px; height:44px; border-radius:13px; background:var(--red);
  color:#fff; display:flex; align-items:center; justify-content:center;
  box-shadow:0 6px 16px rgba(0,0,0,.30); border:1px solid rgba(255,255,255,.14); }
.lob-badge-ic svg{ width:23px; height:23px; }
.lob-badge-cat{ font-family:var(--font-caption); font-style:italic; font-size:14.5px;
  color:#fff; text-shadow:0 1px 4px rgba(0,0,0,.5); }

.lob-content{ padding:22px 22px 24px; display:flex; flex-direction:column; gap:12px; flex:1; }
.lob-content h3{ font-size:1.18rem; color:var(--heading); line-height:1.35; }
.lob-content p{ font-size:13.5px; color:var(--ink-soft); line-height:1.8; }
.lob-content .lob-tags{ margin-top:6px; }
.lob-more{ margin-top:auto; padding-top:6px; font-size:13px; font-weight:700; color:var(--red);
  display:inline-flex; align-items:center; gap:6px; }
.lob-more svg{ transition:transform .3s var(--ease); }
.lob-card:hover .lob-more svg{ transform:translateX(4px); }

/* whole-card link overlay (keeps the card one tap target) */
.lob-card > .lob-link{ position:absolute; inset:0; z-index:3; }

@media (prefers-reduced-motion: reduce){
  .lob-card, .lob-media img, .lob-more svg{ transition:none; }
  .lob-card:hover{ transform:none; }
}

/* ============================================================
   rev28: 其他保險 — DRENCHED-RED HERO. Eyebrow + title + intro all
   live on the red ground (a homepage-echoing red moment), the
   cleaned umbrella floats on a sunburst as the protection symbol,
   and the seven lines are surfaced up front as quick-jump chips so
   the visitor sees every option at a glance.
   ============================================================ */
.oi-hero{ position:relative; overflow:hidden; background:var(--red); color:#fff;
  padding-block:clamp(44px,6vw,88px); }
.oi-hero .wrap{ position:relative; z-index:1; }
.oi-hero-grid{ align-items:center; }
.oi-hero-copy{ grid-column:1/7; }
.oi-hero-copy .caption{ color:#F1D6C9; display:block; margin-bottom:14px; }
.oi-hero-copy h1{ color:var(--paper); font-size:clamp(2.2rem,4.4vw,3.5rem); }
.oi-hero-copy h1 em{ font-style:normal; color:#F2D98A; }
.oi-hero-copy p{ color:rgba(241,228,213,.88); font-size:15.5px; margin-top:18px; max-width:48ch; line-height:1.85; }
.oi-hero-ctas{ display:flex; gap:12px; margin-top:26px; flex-wrap:wrap; }
.oi-chips{ display:flex; flex-wrap:wrap; gap:9px; margin-top:28px; }
.oi-chips-lead{ width:100%; font-family:var(--font-caption); font-style:italic; font-size:13.5px;
  color:#F1D6C9; margin-bottom:2px; }
.oi-chip{ display:inline-flex; align-items:center; gap:7px; font-size:13px; font-weight:600; color:var(--paper);
  background:rgba(34,27,23,.55); border:1px solid rgba(241,228,213,.22); border-radius:999px;
  padding:9px 16px; box-shadow:0 4px 12px rgba(0,0,0,.18);
  transition:background .2s var(--ease), border-color .2s var(--ease), transform .2s var(--ease); }
.oi-chip:hover{ background:rgba(34,27,23,.82); border-color:rgba(242,217,138,.55); transform:translateY(-2px); }
.oi-chip svg{ width:15px; height:15px; flex-shrink:0; color:#F2D98A; }

.oi-hero-art{ grid-column:8/13; position:relative; }
.oi-umbrella{ position:relative; width:min(100%,540px); margin-inline:auto; aspect-ratio:1; }
.oi-umbrella img{ position:relative; z-index:1; width:100%; height:100%; object-fit:contain;
  filter:drop-shadow(0 24px 42px rgba(0,0,0,.32)); }
.oi-umbrella::before{ content:""; position:absolute; z-index:0; inset:-4% -6% auto -6%; height:104%; opacity:.4;
  background:repeating-conic-gradient(from 0deg at 50% 46%,
     color-mix(in oklch,#F2D98A 42%,transparent) 0deg 3.6deg, transparent 3.6deg 7.2deg);
  -webkit-mask:radial-gradient(circle at 50% 46%, #000 2%, transparent 66%);
          mask:radial-gradient(circle at 50% 46%, #000 2%, transparent 66%); }
/* umbrella watermark char, very faint, bottom-left of the red hero */
.oi-hero .sign-wm span{ color:#fff; opacity:.06; }

@media (max-width:980px){
  .oi-hero-copy,.oi-hero-art{ grid-column:1/7; }
  .oi-hero-art{ margin-top:20px; order:-1; }
  .oi-umbrella{ width:min(72%,380px); }
}
@media (max-width:620px){ .oi-hero-copy,.oi-hero-art{ grid-column:1/5; } }

/* section intro above the card grid */
.oi-grid-head{ grid-column:1/13; margin-bottom:clamp(28px,4vw,44px); text-align:center; }
.oi-grid-head h2{ font-size:clamp(1.9rem,3.4vw,2.7rem); color:var(--heading); }
.oi-grid-head p{ color:var(--ink-soft); font-size:15px; margin:12px auto 0; max-width:52ch; }

/* ---- rev30: umbrella → faint backdrop, chips move right, red fades down ---- */
/* rev31: fade solid red → transparent over the paper base, so the hero blends
   into the page background with no orange mid-band (oklch red→paper crossed orange) */
.oi-hero{ background:linear-gradient(180deg, var(--red) 0%, var(--red) 45%,
   rgba(178,10,48,0) 100%), var(--paper); }
.oi-hero-art{ position:relative; min-height:clamp(300px,32vw,420px); display:flex; align-items:center; }
.oi-umbrella.bg{ position:absolute; inset:0; width:100%; height:100%; margin:0; opacity:.2; z-index:0; }
.oi-umbrella.bg img{ filter:none; }
.oi-umbrella.bg::before{ opacity:.22; }
.oi-hero-art .oi-chips{ position:relative; z-index:1; width:100%; margin-top:0; align-content:center; }
@media (max-width:980px){
  .oi-hero-art{ order:0; min-height:0; padding-top:6px; }
  .oi-umbrella.bg{ opacity:.14; }
}

/* ============================================================
   rev29: about-page tweaks — second 言 watermark bottom-right;
   the two fact cards overlap like a stacked pair and only the
   hovered card lifts.
   ============================================================ */
.sign-wm.br span{ top:auto; bottom:-22%; right:3%; left:auto; transform:none; }

.about-aside{ gap:0; }
.about-aside .about-fact{ position:relative; box-shadow:var(--shadow-sm);
  transition:transform .32s var(--ease), box-shadow .32s var(--ease); }
.about-aside .about-fact + .about-fact{ margin-top:-22px; }
.about-aside .about-fact:hover{ transform:translateY(-9px); box-shadow:var(--shadow-md); z-index:5; }
@media (max-width:980px){
  .about-aside{ gap:var(--gap); }
  .about-aside .about-fact + .about-fact{ margin-top:0; }
}
@media (prefers-reduced-motion: reduce){ .about-aside .about-fact{ transition:none; } .about-aside .about-fact:hover{ transform:none; } }

/* ============================================================
   rev31 (revised): about — 核心成員 folded into a 下款 sign-off under the
   intro; 信 + 言 watermarks both on the left behind the copy; the 信言保險
   signature is a decorative overlay ON TOP of the fact cards — absolute, so
   it never shifts the layout.
   ============================================================ */
.sign-wm.bl span{ top:auto; bottom:-12%; left:0; right:auto; transform:none; }

.about-signoff{ margin-top:clamp(24px,3vw,40px); position:relative; z-index:2; }
.signoff-label{ font-family:var(--font-caption); font-style:italic; font-size:14px;
  color:var(--rose-deep); margin-bottom:6px; }
.signoff-line{ display:flex; align-items:baseline; gap:12px; flex-wrap:wrap; }
.signoff-name{ font-family:var(--font-display); font-weight:900; font-size:1.35rem; color:var(--ink); }
.signoff-role{ font-family:var(--font-caption); font-style:italic; font-size:13.5px; color:var(--rose-deep); }

/* (rev36: the 信言保險 signature overlay was removed from the lead — it now
   signs the 核心成員 card instead; see .card-signature below.) */

/* ============================================================
   rev32: 其他保險 inverted-L — desktop breathing room (client request)
   The L (紅色其他保險 cell + chips band) was hemmed in by the base
   --gap (10–16px): only ~15px separated it from the 餐飲 card on its
   left and the 安老 card above. Widen the solutions grid's own gutter
   so the L reads as a distinct object. Scoped to .solutions-grid —
   every gutter grows together, so the cell stays aligned to the 安老
   card above it and the L's inner fillet still bridges the corner.
   ============================================================ */
.solutions-grid{
  column-gap:clamp(18px, 2.6vw, 36px);
  row-gap:clamp(18px, 2.6vw, 36px);
}

/* Below the stacking breakpoint the cell and the chips band are both
   full-width and flush — there is no concave corner left to bridge, so the
   rev22 fillet just hangs off the cell's bottom-left as a stray red wedge. */
@media (max-width: 980px){
  .ind-empty.cutout-cell::before{ display:none; }
  /* stacked, the cell sits directly on the band — square the band's top-left
     too, or its radius bites a cream notch out of the merged red block. */
  .other-lines-row{ border-top-left-radius:0; }
}

/* ============================================================
   rev33: 其他保險 hero — rebuilt on style-book:36c82a (VETCITY)
   ------------------------------------------------------------
   What the reference actually does, and what we ported:
   · an oversized wordmark running the full width of the content
     well, treated as a GROUND layer rather than a headline;
   · an opaque panel laid ON TOP of it, occluding its lower half —
     the occlusion is what makes the type read as a layer instead
     of as shouting;
   · small body paragraphs parked in the leftover space either
     side of that panel, acting as counterweights;
   · exactly ONE accent surface, inverted against the ground.

   Two deliberate departures:
   1. The reference crops its wordmark at the viewport edge
      ("PetcityClinic" → "etcityClinic"). That does not transfer to
      Chinese — Latin letters are sequential so the eye completes a
      sliced word, but a Hanzi is a self-contained square and half
      of one reads as a broken glyph, not as a designed crop. So the
      wordmark runs COMPLETE, edge to edge of the content well, and
      all the layering comes from the panel occluding it.
   2. The reference sets its counterweight paragraphs at ~8px. CJK
      needs 15px minimum before complex glyphs (鬱 麗 龍) silt up, so
      ours are 15px. Scale loses to legibility.

   The ground stays brand red (client-approved 2026-07-30, incl. the
   red→paper fade above), so the reference's white/black/blue maps to
   red/paper/paper-panel: on a drenched ground the ONE accent surface
   is the light one.
   ============================================================ */

/* The stage is the container the wordmark measures itself against, so the
   lockup is exactly as wide as the content well at EVERY viewport with no
   breakpoints — 4 glyphs x 24.5cqw + 3 gaps = 100cqw, guaranteed. */
.oi-stage{ container-type:inline-size; position:relative; z-index:1; }

.oi-hero{ padding-block:clamp(52px,6.5vw,104px) clamp(64px,8vw,116px); }

/* Chinese is never italicised — CJK has no italic tradition and the faux
   oblique breaks the strokes. (The shared .caption device is English-only;
   this line is Chinese, so it gets its own upright treatment.) */
.oi-kicker{
  font-family:var(--font-body);
  font-style:normal;
  font-size:14.5px;
  font-weight:600;
  letter-spacing:.12em;
  color:rgba(248,242,237,.78);
  margin:0 0 clamp(14px,1.6vw,22px);
}

/* ---- the ground layer ---- */
.oi-wordmark{
  position:relative; z-index:1;
  display:flex; justify-content:space-between;
  margin:0;
  font-family:var(--font-display);
  font-weight:900;
  font-size:24.5cqw;
  line-height:.92;
  letter-spacing:0;              /* CJK body/display tracking is never positive… */
  color:var(--paper);
  font-feature-settings:"palt";  /* …optical widths do the tightening instead */
  -webkit-user-select:none; user-select:none;
}
.oi-wordmark span{ display:block; }

/* ---- the content laid over it ---- */
.oi-mosaic{
  position:relative;
  display:grid; grid-template-columns:repeat(12,1fr);
  column-gap:var(--gap);
  align-items:start;
}

/* Panel spans cols 4-9, which lands almost exactly on glyphs 2 and 3: 其 and 險
   stay whole at either end while 他 and 保 are cut at the waist — the reference's
   "fragments visible at both ends" read, with no broken glyphs. */
.oi-panel{
  grid-column:4/10;
  position:relative; z-index:2;
  margin-top:-8cqw;
  background:var(--paper);
  border-radius:var(--radius-lg);
  padding:clamp(22px,2.4vw,34px);
  box-shadow:0 24px 60px rgba(60,4,16,.28);
}
.oi-panel-label{
  display:inline-block;
  font-size:12.5px; font-weight:700; letter-spacing:.06em;
  color:var(--paper);
  background:var(--ink);
  border-radius:999px;
  padding:5px 13px;
  margin-bottom:14px;
}
.oi-panel-claim{
  margin:0;
  font-family:var(--font-display); font-weight:900;
  font-size:clamp(1.05rem,1.55vw,1.4rem);
  line-height:1.5;
  color:var(--ink);
  text-wrap:balance;
}
.oi-panel-ctas{ display:flex; flex-wrap:wrap; align-items:center; gap:14px; margin-top:clamp(16px,1.8vw,22px); }
.oi-panel-btn{
  display:inline-flex; align-items:center; gap:9px;
  background:var(--red); color:#fff;
  font-size:15px; font-weight:700;
  border-radius:var(--radius-sm);
  padding:13px 22px;
  transition:background .28s var(--ease), transform .28s var(--ease);
}
.oi-panel-btn svg{ width:15px; height:15px; transition:transform .28s var(--ease); }
.oi-panel-btn:hover{ background:var(--red-deep); transform:translateY(-2px); }
.oi-panel-btn:hover svg{ transform:translateX(3px); }
.oi-panel-wa{
  font-size:14.5px; font-weight:700; color:var(--rose-deep);
  border-bottom:1px solid rgba(122,89,78,.4);
  padding-bottom:2px;
  transition:color .28s var(--ease), border-color .28s var(--ease);
}
.oi-panel-wa:hover{ color:var(--ink); border-color:var(--ink); }

/* counterweights — parked either side of the panel, below the wordmark's
   baseline so they never collide with it */
.oi-note{
  margin:clamp(18px,2.2vw,30px) 0 0;
  font-size:15px; line-height:1.85;
  color:rgba(248,242,237,.9);   /* 5.3:1 on the red ground */
}
.oi-note-l{ grid-column:1/4; }
.oi-note-r{ grid-column:10/13; text-align:right; }

.oi-chips{ margin-top:clamp(30px,3.6vw,50px); }
/* same Chinese-italic fix as the kicker */
.oi-chips-lead{ font-family:var(--font-body); font-style:normal; font-weight:600; letter-spacing:.06em; }

/* the umbrella stays a faint backdrop (client's call) — pushed right and
   scaled so it sits behind the panel rather than behind the wordmark */
.oi-hero > .oi-umbrella.bg{
  inset:auto 0 0 auto; width:min(46%,560px); height:88%; opacity:.16;
}

/* ---- entrance: the glyphs rise, then the panel settles onto them ----
   Pure CSS animation, not a class-toggled reveal, so the hero is never
   gated on an observer that may not fire. */
@keyframes oi-rise{ from{ opacity:0; transform:translateY(.16em); } to{ opacity:1; transform:none; } }
@keyframes oi-settle{ from{ opacity:0; transform:translateY(26px); } to{ opacity:1; transform:none; } }
.oi-wordmark span{ animation:oi-rise .9s var(--ease) both; }
.oi-wordmark span:nth-child(2){ animation-delay:.07s; }
.oi-wordmark span:nth-child(3){ animation-delay:.14s; }
.oi-wordmark span:nth-child(4){ animation-delay:.21s; }
.oi-panel{ animation:oi-settle .8s var(--ease) .3s both; }
.oi-note-l{ animation:oi-settle .7s var(--ease) .42s both; }
.oi-note-r{ animation:oi-settle .7s var(--ease) .5s both; }

@media (prefers-reduced-motion: reduce){
  .oi-wordmark span,.oi-panel,.oi-note-l,.oi-note-r{ animation:none; }
}

/* ---- responsive ---- */
@media (max-width:980px){
  .oi-mosaic{ grid-template-columns:repeat(6,1fr); }
  .oi-panel{ grid-column:1/7; margin-top:-5cqw; }
  .oi-note-l,.oi-note-r{ grid-column:1/7; text-align:left; }
  .oi-note-r{ margin-top:14px; }
  .oi-hero > .oi-umbrella.bg{ width:min(62%,420px); opacity:.12; }
}
@media (max-width:620px){
  .oi-mosaic{ grid-template-columns:repeat(4,1fr); }
  .oi-panel,.oi-note-l,.oi-note-r{ grid-column:1/5; }
  .oi-panel{ margin-top:-4cqw; }
}

/* ---- rev33b: proportion correction ----
   First pass set 4 glyphs at 24.5cqw (301px measured) — 2.3x the reference's
   own wordmark-to-column ratio, which reads as shouting rather than as a
   ground layer, and pushed the quick-jump chips off the first screen.
   Six glyphs (其他保險方案 — the page's real title) at 16.2cqw lands ~199px and
   restores the reference's proportion while still spanning the full well.
   Panel moves to cols 3-10, which measures out at x207-1020 against glyph
   bounds of 206-1023: glyphs 2-5 are cut at the waist, 其 and 案 stay whole. */
.oi-wordmark{ font-size:16.2cqw; }
.oi-hero{ padding-block:clamp(40px,4.5vw,72px) clamp(56px,6.5vw,96px); }
.oi-panel{ grid-column:3/11; margin-top:-7cqw; }
.oi-note-l{ grid-column:1/3; }
.oi-note-r{ grid-column:11/13; }

@media (max-width:980px){
  .oi-panel{ grid-column:1/7; margin-top:-5cqw; }
  .oi-note-l,.oi-note-r{ grid-column:1/7; }
}
@media (max-width:620px){
  .oi-panel,.oi-note-l,.oi-note-r{ grid-column:1/5; }
  .oi-panel{ margin-top:-4cqw; }
}

/* ---- rev33c: panel density + composition asymmetry + chips-lead contrast ----
   · Panel narrows to cols 3-8 (measures 207-813 against 險's right edge at 816,
     so it still lands on a glyph boundary): 其 whole at the left, 方案 whole at
     the right, only 他保險 cut. Denser, and the composition stops being
     symmetrical — the reference clusters its cards left-of-centre, not centred.
   · The right counterweight widens to 3 cols to take the freed space.
   · .oi-chips-lead was #F1D6C9 — a paper tone. It sits near the bottom of the
     hero where the red has faded ~50% toward paper, so it measured about 1.5:1.
     It goes ink: high contrast on the transition zone, and consistent with the
     cream page it hands off to. (Also drops the faux-italic — same Chinese
     -is-never-italicised rule as the kicker.) */
.oi-panel{
  grid-column:3/9;
  padding:clamp(20px,2.1vw,28px) clamp(20px,2.2vw,30px) clamp(22px,2.3vw,30px);
}
.oi-panel-claim{ font-size:clamp(1.02rem,1.4vw,1.28rem); }
.oi-note-r{ grid-column:10/13; }

.oi-chips-lead{ color:var(--ink); font-weight:700; font-size:13px; letter-spacing:.08em; margin-bottom:6px; }

@media (max-width:980px){
  .oi-panel{ grid-column:1/7; }
  .oi-note-r{ grid-column:1/7; }
}
@media (max-width:620px){
  .oi-panel,.oi-note-r{ grid-column:1/5; }
}

/* ---- rev33d: AA contrast on the two paper-toned texts ----
   Both sit over the red→paper fade, which lightens the ground under them and
   quietly eats the ratio. Measured against the blended background at their own
   y-positions: .oi-note was rgba(paper,.9) → 4.29:1, .oi-kicker rgba(paper,.78)
   → 4.26:1. Both are under 18.66px so both owe 4.5:1. Raised to 4.95:1 and
   5.14:1 respectively. */
.oi-note{ color:var(--paper); }
.oi-kicker{ color:rgba(248,242,237,.88); }

/* ---- rev33e: stacked-layout ordering bug ----
   Below 980px all three mosaic items go full-width and stack in DOM order —
   note, panel, note. The panel's negative margin-top is meant to pull it over
   the WORDMARK, but stacked it pulled over the note above it instead and, being
   opaque with z-index 2, hid it completely (measured: note at 337-365, panel at
   325-504). The panel has to lead the stack for the device to work at all. */
@media (max-width:980px){
  .oi-panel{ order:-1; }
}

/* ============================================================
   rev34: 其他保險 hero — rebuilt on style-book:2588b9 (hero-led)
   ------------------------------------------------------------
   Supersedes rev33's oversized-wordmark hero (rev33 rules are left in place
   but inert — nothing on the page carries .oi-stage / .oi-wordmark any more).

   The reference's device: an INSET ROUNDED PHOTO PANEL floating on a light
   page, photograph carrying the mood, a centred type stack over it, and small
   metadata anchored along the bottom edge. Ported wholesale.

   Key's two asks:
   1. A photo as the base layer, red kept as an overlay. Done as a real
      DUOTONE, not a flat scrim: the photo is desaturated first, then a
      brand-red multiply layer goes over it. Desaturating first is what makes
      the red read as ONE colour — tint a colour photo directly and its greens
      and blues fight the red and you get muddy patches.
   2. The title steps down. It is no longer the ground layer; it is a tracked,
      centred lockup at ~clamp(1.85rem,3.4vw,2.9rem). Tracking is +0.05em, the
      hard ceiling for CJK display type — beyond that the reading rhythm goes.

   Note: the previous hero's red→paper fade is gone, and correctly so. That
   fade existed to blend a full-bleed red band into the cream page. An inset
   panel floats on the cream instead, so there is no edge left to blend.
   ============================================================ */
.oi-hero2{ background:var(--paper); padding-block:clamp(18px,2.2vw,30px) clamp(40px,5vw,68px); }

.oi-frame{
  position:relative; isolation:isolate;
  border-radius:clamp(16px,1.8vw,26px);
  overflow:hidden;
  min-height:clamp(430px,44vw,580px);
  display:flex; flex-direction:column;
  padding:clamp(30px,4vw,60px) clamp(22px,3.4vw,52px) clamp(20px,2.4vw,32px);
  box-shadow:0 30px 70px rgba(60,4,16,.20);
}

/* --- the duotone stack --- */
.oi-frame-photo{
  position:absolute; inset:0; z-index:0;
  width:100%; height:100%; object-fit:cover; object-position:center 58%;
  /* Lift to a LIGHT flat grey before tinting. A multiply layer can only
     darken, so a normally-exposed photo under a red multiply crushes to
     near-black maroon (measured on the first pass). Brightening and flattening
     first is what lets the multiply land on brand red instead of on oxblood. */
  filter:grayscale(1) brightness(1.34) contrast(.76);
}
.oi-frame-tint{
  position:absolute; inset:0; z-index:1; pointer-events:none;
  background:var(--red);
  mix-blend-mode:multiply;
}
/* a second, non-blending pass: lifts the red back up out of the multiply
   (multiply alone crushes toward black) and darkens the foot for the chips */
.oi-frame::after{
  content:""; position:absolute; inset:0; z-index:2; pointer-events:none;
  background:
    radial-gradient(130% 95% at 50% 42%, rgba(120,6,32,.10) 0%, rgba(95,5,26,.34) 100%),
    linear-gradient(180deg, rgba(80,4,20,.34) 0%, rgba(80,4,20,0) 26%,
                            rgba(80,4,20,0) 62%, rgba(64,3,16,.50) 100%);
}
.oi-frame > *:not(.oi-frame-photo):not(.oi-frame-tint){ position:relative; z-index:3; }

/* --- centred type stack --- */
.oi-frame-body{
  flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center;
}
.oi-kicker2{
  font-family:var(--font-body); font-style:normal; font-weight:600;
  font-size:14px; letter-spacing:.16em;
  color:rgba(248,242,237,.92);
  margin:0 0 clamp(14px,1.6vw,20px);
}
.oi-lockup{
  display:flex; justify-content:center; margin:0;
  font-family:var(--font-display); font-weight:900;
  font-size:clamp(1.85rem,3.4vw,2.9rem);
  line-height:1.18;
  letter-spacing:.05em;          /* CJK display ceiling — never go past this */
  color:var(--paper);
  text-shadow:0 2px 18px rgba(60,4,16,.45);
}
/* letter-spacing adds a trailing space after the last glyph; pull it back so
   the lockup is optically centred rather than mathematically centred */
.oi-lockup{ text-indent:.05em; }
.oi-sub{
  margin:clamp(16px,1.9vw,24px) 0 0;
  max-width:44ch;
  font-size:15.5px; line-height:1.9;
  color:var(--paper);
  text-wrap:pretty;
}
.oi-cta-row{ display:flex; flex-wrap:wrap; align-items:center; justify-content:center;
  gap:16px; margin-top:clamp(22px,2.6vw,32px); }
.oi-pill{
  display:inline-flex; align-items:center; gap:9px;
  background:var(--paper); color:var(--ink);
  font-size:15px; font-weight:700;
  border-radius:999px; padding:14px 26px;
  transition:background .28s var(--ease), transform .28s var(--ease), box-shadow .28s var(--ease);
  box-shadow:0 8px 22px rgba(50,3,14,.28);
}
.oi-pill svg{ width:15px; height:15px; transition:transform .28s var(--ease); }
.oi-pill:hover{ background:#fff; transform:translateY(-2px); box-shadow:0 12px 28px rgba(50,3,14,.34); }
.oi-pill:hover svg{ transform:translateX(3px); }
.oi-pill-ghost{
  display:inline-flex; align-items:center;
  border:1px solid rgba(248,242,237,.55); color:var(--paper);
  font-size:15px; font-weight:700;
  border-radius:999px; padding:13px 24px;
  transition:background .28s var(--ease), border-color .28s var(--ease);
}
.oi-pill-ghost:hover{ background:rgba(248,242,237,.14); border-color:var(--paper); }

/* --- metadata band along the bottom edge --- */
.oi-frame-foot{
  display:flex; flex-wrap:wrap; align-items:center; gap:9px;
  margin-top:clamp(26px,3vw,40px);
  padding-top:clamp(16px,1.8vw,22px);
  border-top:1px solid rgba(248,242,237,.22);
}
.oi-frame-foot .oi-chips-lead{
  width:auto; margin:0 6px 0 0;
  font-family:var(--font-body); font-style:normal; font-weight:700;
  font-size:12.5px; letter-spacing:.1em;
  color:rgba(248,242,237,.86);
}
.oi-frame-foot .oi-chip{
  background:rgba(20,2,8,.30); border:1px solid rgba(248,242,237,.34);
  color:var(--paper); box-shadow:none;
  backdrop-filter:blur(2px);
}
.oi-frame-foot .oi-chip:hover{ background:rgba(20,2,8,.55); border-color:var(--paper); transform:translateY(-2px); }
.oi-frame-foot .oi-chip svg{ color:rgba(248,242,237,.9); }

/* --- entrance: the frame settles, then the stack rises. Pure CSS, not an
       observer-gated reveal, so the hero can never ship blank. --- */
@keyframes oi2-frame{ from{ opacity:0; transform:scale(.985); } to{ opacity:1; transform:none; } }
@keyframes oi2-up{ from{ opacity:0; transform:translateY(16px); } to{ opacity:1; transform:none; } }
.oi-frame{ animation:oi2-frame .9s var(--ease) both; }
.oi-kicker2{ animation:oi2-up .7s var(--ease) .22s both; }
.oi-lockup{ animation:oi2-up .8s var(--ease) .3s both; }
.oi-sub{ animation:oi2-up .8s var(--ease) .4s both; }
.oi-cta-row{ animation:oi2-up .8s var(--ease) .5s both; }
.oi-frame-foot{ animation:oi2-up .8s var(--ease) .6s both; }
@media (prefers-reduced-motion: reduce){
  .oi-frame,.oi-kicker2,.oi-lockup,.oi-sub,.oi-cta-row,.oi-frame-foot{ animation:none; }
}

@media (max-width:620px){
  .oi-frame{ min-height:0; }
  .oi-lockup{ letter-spacing:.03em; }
  .oi-frame-foot{ gap:7px; }
  .oi-frame-foot .oi-chip{ font-size:12px; padding:8px 13px; }
}

/* ---- rev34b: hero photo = the real Unsplash harbour frame (Key's pick) ----
   Crop sits lower than 關於我們's band (which uses center 46%) so the two pages
   don't read as the same picture — that plus the red duotone is what separates
   them. */
.oi-frame-photo{ object-position:center 62%; }

/* ---- rev34c: CJK measure on the hero sub ----
   `ch` is the width of the "0" glyph, so 44ch sized itself off the LATIN digit
   and gave the Chinese paragraph a 379px column — about 22 characters a line,
   half the comfortable measure. For CJK, `em` IS the character box, so em is the
   honest unit for line length. 40em ≈ 40 characters, mid-range for Chinese. */
.oi-sub{ max-width:40em; }

/* ============================================================
   rev35: 其他保險 hero → the shared .hero-product pattern
   ------------------------------------------------------------
   Client review on the rev34 duotone frame: 「太強硬而且同其他版面不太配合」.
   Correct call — a full-bleed red photo panel was the loudest thing on a site
   whose other pages open on cream with red as an accent only. This page now
   reuses .hero-product / .hp-headline / .hp-photo / .hp-stat-strip verbatim
   from 餐飲業保險, so there is almost no page-specific hero CSS left to drift.

   rev33 (VETCITY wordmark) and rev34 (duotone frame) are both retained but
   inert — nothing carries their classes. Kept so either can be restored by
   swapping the markup back. Delete both once a direction is locked.
   ============================================================ */

/* quick-jump strip — occupies the slot 餐飲業保險 gives its .savings strip, so
   the two product pages share a rhythm. Deliberately quiet: rose-wash, not the
   homepage's red chip block, because the loudness is what was rejected. */
.oi-jump{ background:var(--rose-wash); border-block:1px solid rgba(122,89,78,.14); }
.oi-jump-row{
  grid-column:1/13; display:flex; flex-wrap:wrap; align-items:center; gap:9px;
  padding-block:clamp(14px,1.6vw,20px);
}
.oi-jump-lead{
  margin-right:6px; font-size:12.5px; font-weight:700; letter-spacing:.08em;
  color:var(--rose-deep);
}
.jump-chip{
  display:inline-flex; align-items:center; gap:7px;
  font-size:13px; font-weight:600; color:var(--ink);
  background:var(--surface); border:1px solid rgba(122,89,78,.22);
  border-radius:999px; padding:8px 15px;
  transition:border-color .22s var(--ease), color .22s var(--ease), transform .22s var(--ease);
}
.jump-chip svg{ width:15px; height:15px; flex-shrink:0; color:var(--red); }
.jump-chip:hover{ border-color:var(--red); color:var(--red); transform:translateY(-2px); }
@media (max-width:620px){
  .oi-jump-row{ gap:7px; }
  .jump-chip{ font-size:12px; padding:7px 12px; }
}

/* ============================================================
   rev36: 七大範疇 grid — size hierarchy instead of seven equal cards
   ------------------------------------------------------------
   Seven identical photo+title+pills cards in a flat 3-col grid is the
   textbook uniform-card-grid, and the 7th card orphaned in a row of three
   with two empty cells.

   Base grid goes to SIX columns so both halves (span 3) and thirds (span 2)
   are expressible, giving three rows with no orphan:

     row 1   物業管理 · 商鋪中小企          half-width FEATURE, horizontal
     row 2   出入口貿易 · 建築工程 · 裝修工程   third-width standard, vertical
     row 3   家居 · 個人                    half-width FEATURE, horizontal

   The split falls exactly on the 商業/個人 tags already in the markup — rows
   1-2 are the five 商業 lines, row 3 is the two 個人 ones — so the size
   hierarchy and the meaning agree instead of merely coexisting.

   A feature card is not a stretched small card: it turns HORIZONTAL (photo
   beside the copy, not above it). Same content, genuinely different object —
   which is what makes it read as hierarchy rather than as an inconsistency.
   ============================================================ */
.lob-grid{ grid-template-columns:repeat(6,1fr); }
.lob-card{ grid-column:span 2; }
.lob-card.feature{ grid-column:span 3; flex-direction:row; align-items:stretch; }

.lob-card.feature .lob-media{ aspect-ratio:auto; flex:0 0 42%; align-self:stretch; }
.lob-card.feature .lob-content{ flex:1; min-width:0; padding:24px 24px 26px; justify-content:center; }
.lob-card.feature .lob-content h3{ font-size:1.28rem; }
/* the badge reads as a caption under a portrait photo in the tall crop, so it
   moves in a touch and loses the stretched-wide look */
.lob-card.feature .lob-badge{ left:14px; right:14px; bottom:12px; }
.lob-card.feature .lob-badge-ic{ width:40px; height:40px; border-radius:12px; }
.lob-card.feature .lob-badge-ic svg{ width:21px; height:21px; }

/* two-up: features stay horizontal at half width, standards pair up */
@media (max-width:1080px){
  .lob-grid{ grid-template-columns:repeat(6,1fr); }
  .lob-card{ grid-column:span 3; }
  .lob-card.feature{ grid-column:span 6; }
}
/* stacked: everything full width and back to vertical — a horizontal card at
   phone width leaves the copy column too narrow to set Chinese in */
@media (max-width:640px){
  .lob-card,.lob-card.feature{ grid-column:span 6; }
  .lob-card.feature{ flex-direction:column; }
  .lob-card.feature .lob-media{ aspect-ratio:16/10; flex:none; }
  .lob-card.feature .lob-content{ padding:22px 22px 24px; justify-content:flex-start; }
}

/* ============================================================
   rev37: 七大範疇 cards — softened photo edge + red bleed, tags removed
   ------------------------------------------------------------
   Client asks: dissolve the hard seam between the photo and the card's white
   block into a gradient; bleed a blurred red geometric shape into the white so
   colour seems to seep out of the image; drop the per-card tag pills.

   The seam is softened by MASKING the media container rather than stacking a
   white gradient over it. The mask fades the photo, its scrim and the fallback
   gradient together, and works whatever colour the card is — a surface-coloured
   overlay would have to be re-tuned every time the card background changed.
   ============================================================ */

/* fade direction follows the seam: standard cards meet the copy below them,
   feature cards meet it to their right */
.lob-media{
  position:relative; z-index:1;
  --seam:linear-gradient(180deg, #000 0%, #000 56%, rgba(0,0,0,.62) 80%, transparent 100%);
  -webkit-mask-image:var(--seam); mask-image:var(--seam);
}
.lob-card.feature .lob-media{
  --seam:linear-gradient(90deg, #000 0%, #000 54%, rgba(0,0,0,.62) 80%, transparent 100%);
}

/* the scrim flips top-heavy: the bottom is now doing the fade, so a dark band
   down there would just read as mud */
.lob-media::after{
  background:linear-gradient(180deg, rgba(34,27,23,.52) 0%, rgba(34,27,23,.20) 30%,
                                     rgba(34,27,23,0) 58%);
}

/* …which means the badge has to leave the bottom edge, or it dissolves with it */
.lob-badge{ top:13px; bottom:auto; left:14px; right:auto; max-width:calc(100% - 92px); }
.lob-badge-cat{ white-space:nowrap; }
/* feature cards fade on the RIGHT, so the corner tag drops to the left instead
   of sitting in the fade */
.lob-card.feature .lob-tag-corner{ top:auto; right:auto; bottom:13px; left:14px; }

/* red geometric bleed — a rotated square and a disc, heavily blurred, sited on
   the seam so the colour reads as seeping out of the photo into the white.
   Both sit at z-index 0: the card's own white background paints below them and
   media/content above, so they glow between the two. */
.lob-card::before,
.lob-card::after{
  content:""; position:absolute; z-index:0; pointer-events:none;
}
.lob-card::before{
  width:60%; aspect-ratio:1; left:4%; top:38%;
  transform:rotate(34deg);
  background:linear-gradient(142deg, rgba(178,10,48,.30), rgba(178,10,48,.07) 62%, rgba(178,10,48,0) 86%);
  filter:blur(36px);
}
.lob-card::after{
  width:44%; aspect-ratio:1; right:-6%; top:52%;
  border-radius:50%;
  background:radial-gradient(circle, rgba(176,132,118,.34), rgba(176,132,118,0) 70%);
  filter:blur(30px);
}
.lob-card.feature::before{ width:38%; left:34%; top:26%; }
.lob-card.feature::after{ width:30%; right:2%; top:56%; }

.lob-content{ position:relative; z-index:1; }
/* tag pills are gone, so the copy no longer needs the gap that separated them */
.lob-content{ gap:10px; }

@media (max-width:640px){
  .lob-card.feature .lob-media{ --seam:linear-gradient(180deg, #000 0%, #000 56%, rgba(0,0,0,.62) 80%, transparent 100%); }
  .lob-card.feature .lob-tag-corner{ top:12px; right:12px; bottom:auto; left:auto; }
  .lob-card.feature::before{ width:60%; left:4%; top:38%; }
  .lob-card.feature::after{ width:44%; right:-6%; top:52%; }
}

/* ---- rev37b: the seam softening was overcooked ----
   First pass faded from 56% and blobbed at .30/.34 alpha: the photos lost their
   bottom half and the white went visibly pink. Same effect, dialled back — the
   fade zone drops from ~44% of the media to ~28%, and the bleed roughly halves. */
.lob-media{ --seam:linear-gradient(180deg, #000 0%, #000 72%, rgba(0,0,0,.55) 90%, transparent 100%); }
.lob-card.feature .lob-media{ --seam:linear-gradient(90deg, #000 0%, #000 66%, rgba(0,0,0,.55) 88%, transparent 100%); }
.lob-media::after{ background:linear-gradient(180deg, rgba(34,27,23,.60) 0%, rgba(34,27,23,.22) 30%, rgba(34,27,23,0) 58%); }
.lob-card::before{ background:linear-gradient(142deg, rgba(178,10,48,.18), rgba(178,10,48,.04) 62%, rgba(178,10,48,0) 86%); }
.lob-card::after{ background:radial-gradient(circle, rgba(176,132,118,.20), rgba(176,132,118,0) 70%); }
@media (max-width:640px){
  .lob-card.feature .lob-media{ --seam:linear-gradient(180deg, #000 0%, #000 72%, rgba(0,0,0,.55) 90%, transparent 100%); }
}

/* ============================================================
   rev38: quick-jump bar rides UP across the hero photo (client request)
   ------------------------------------------------------------
   The bar lifts out of its own row and straddles the harbour photo's lower
   edge, tying hero and bar into one composition instead of two stacked blocks.

   It clears the stat strip rather than colliding with it: the bar lands across
   the photo's bottom, and the stat strip sits BELOW that band, so the right
   column reads photo → bar → stats.

   margin-top and margin-bottom are equal and opposite, so the bar moves
   visually without dragging the rest of the page up with it.
   ============================================================ */
.hero-overlap{ padding-bottom:clamp(30px,3.4vw,48px); }
.oi-jump{
  position:relative; z-index:3;
  --lift:clamp(150px, 17vw, 250px);
  margin-top:calc(var(--lift) * -1);
  margin-bottom:var(--lift);
  box-shadow:0 10px 34px rgba(60,4,16,.16);
}
/* over a photograph the bar needs to read as laid ON the image, so it stays
   opaque — a translucent bar would drop the chips' contrast onto whatever the
   photo happens to be doing behind them */
@media (max-width:980px){
  /* stacked, the photo sits below the copy and there is nothing to straddle */
  .oi-jump{ --lift:0px; box-shadow:none; }
  .hero-overlap{ padding-bottom:clamp(40px,6vw,72px); }
}

/* ============================================================
   rev39: bleed moves to the corner and strengthens; feature-card
          badge/tag get anchored (client feedback)
   ------------------------------------------------------------
   1. The red bleed was too faint and sat mid-card, so it read as a smudge
      rather than as colour seeping in. It now blooms out of the BOTTOM-RIGHT
      corner — inside the white block on both card shapes — at roughly double
      the previous strength, clipped by the card's own rounded corner.
   2. On the horizontal cards the icon + italic label floated at mid-height
      over the photo with nothing to anchor it. Both chips now sit on the
      photo's left edge — tag top-left, badge bottom-left — because the right
      edge is where that card's seam fades and nothing can live there.
   ============================================================ */
.lob-card::before{
  width:68%; aspect-ratio:1;
  left:auto; right:-15%; top:auto; bottom:-18%;
  transform:rotate(34deg);
  background:linear-gradient(142deg, rgba(178,10,48,.44), rgba(178,10,48,.13) 58%, rgba(178,10,48,0) 84%);
  filter:blur(34px);
}
.lob-card::after{
  width:42%; aspect-ratio:1;
  left:auto; right:-9%; top:auto; bottom:6%;
  border-radius:50%;
  background:radial-gradient(circle, rgba(178,10,48,.32), rgba(178,10,48,0) 70%);
  filter:blur(26px);
}
/* the feature card is wider, so the bloom is scaled down to keep the same
   visual weight rather than the same percentage */
.lob-card.feature::before{ width:40%; right:-10%; bottom:-20%; left:auto; top:auto; }
.lob-card.feature::after{ width:26%; right:-5%; bottom:6%; left:auto; top:auto; }

/* horizontal cards: both chips anchor to the photo's left edge */
.lob-card.feature .lob-badge{ top:auto; bottom:14px; left:14px; right:auto; }
.lob-card.feature .lob-tag-corner{ top:13px; left:14px; right:auto; bottom:auto; }
/* …so that photo needs a scrim at BOTH ends, not just the top */
.lob-card.feature .lob-media::after{
  background:linear-gradient(180deg, rgba(34,27,23,.46) 0%, rgba(34,27,23,.04) 32%,
                                     rgba(34,27,23,.04) 60%, rgba(34,27,23,.56) 100%);
}
@media (max-width:640px){
  /* stacked, feature cards are vertical again — back to the standard chip slots */
  .lob-card.feature .lob-badge{ top:13px; bottom:auto; }
  .lob-card.feature .lob-tag-corner{ top:12px; right:12px; left:auto; }
  .lob-card.feature .lob-media::after{
    background:linear-gradient(180deg, rgba(34,27,23,.60) 0%, rgba(34,27,23,.22) 30%, rgba(34,27,23,0) 58%);
  }
  .lob-card.feature::before{ width:68%; right:-15%; bottom:-18%; }
  .lob-card.feature::after{ width:42%; right:-9%; bottom:6%; }
}

/* ---- rev39b: corner bloom tightened; bar height and stat-strip spacing ----
   The bloom was sized as a % of card width with a 1:1 ratio, so on the wide
   feature cards it grew into a right-side WASH instead of a corner. Smaller and
   pushed harder into the corner on both shapes, with the alpha kept up so the
   corner still reads. Plus two client adjustments: the quick-jump bar loses
   height, and the stat strip drops away from it. */
.lob-card::before{ width:52%; right:-12%; bottom:-15%; }
.lob-card::after{ width:34%; right:-7%; bottom:2%; }
.lob-card.feature::before{ width:30%; right:-8%; bottom:-16%; }
.lob-card.feature::after{ width:19%; right:-4%; bottom:4%; }
@media (max-width:640px){
  .lob-card.feature::before{ width:52%; right:-12%; bottom:-15%; }
  .lob-card.feature::after{ width:34%; right:-7%; bottom:2%; }
}

/* slimmer bar */
.oi-jump-row{ padding-block:clamp(9px,1.05vw,13px); }
.oi-jump-lead{ font-size:12px; }
.jump-chip{ padding:7px 14px; }

/* the stat strip drops clear of the bar now riding above it */
.hero-overlap .hp-stat-strip{ margin-top:clamp(26px,3vw,44px); }

/* ---- rev39c: deepen the straddle, close the gap under the hero ----
   Measured: the bar crossed the photo by only 36px of its 713px height, which
   reads as touching rather than straddling; and 135px sat between the stat card
   and the 七大保障範疇 heading.

   The lift goes up so the bar sits properly across the image. The bar's
   margin-bottom is then 65px SHORT of the lift instead of equal to it, which
   pulls everything below up by that much — the equal-and-opposite pair kept the
   page exactly as tall as it was before the bar moved, and it doesn't need to be. */
.oi-jump{
  --lift:clamp(180px, 20vw, 290px);
  margin-bottom:calc(var(--lift) - 65px);
}
@media (max-width:980px){
  .oi-jump{ --lift:0px; margin-bottom:0; }
}

/* ---- rev39d: bleed pushed into the corner, showing ~a quarter, stronger ----
   Offsetting with right/bottom percentages was imprecise because those resolve
   against the CARD, not the shape. Anchoring at the corner and translating by
   half the shape's own size puts its centre exactly on the card corner, so
   exactly one quarter stays inside — independent of how the shape is sized.
   Colour roughly doubles again now that only a corner of it shows. */
.lob-card::before{
  width:80%; aspect-ratio:1;
  left:auto; top:auto; right:0; bottom:0;
  transform:translate(50%,50%) rotate(34deg);
  background:linear-gradient(142deg, rgba(178,10,48,.72), rgba(178,10,48,.30) 52%, rgba(178,10,48,0) 82%);
  filter:blur(32px);
}
.lob-card::after{
  width:54%; aspect-ratio:1;
  left:auto; top:auto; right:0; bottom:0;
  border-radius:50%;
  transform:translate(46%,46%);
  background:radial-gradient(circle, rgba(178,10,48,.55), rgba(178,10,48,.14) 55%, rgba(178,10,48,0) 74%);
  filter:blur(24px);
}
.lob-card.feature::before{ width:40%; right:0; bottom:0; }
.lob-card.feature::after{ width:27%; right:0; bottom:0; }
@media (max-width:640px){
  .lob-card.feature::before{ width:80%; }
  .lob-card.feature::after{ width:54%; }
}

/* ---- rev39e: bleed scaled to 40% (client) — which also fixes a contrast fail ----
   Sampling the rendered pixels behind the card copy: at the previous size the
   body text's right end sat on the bloom at 2.89:1 against --ink-soft, well
   under the 4.5:1 it owes. (The feature cards were fine at ~5.2:1 — their copy
   column sits further from the corner.) Scaling the shapes to 40% pulls the
   strong part of the bloom back into the corner, clear of the text. Blur scales
   with them, or a small shape is nothing but blur. */
.lob-card::before{ width:32%; filter:blur(20px); }
.lob-card::after{ width:22%; filter:blur(15px); }
.lob-card.feature::before{ width:16%; }
.lob-card.feature::after{ width:11%; }
@media (max-width:640px){
  .lob-card.feature::before{ width:32%; }
  .lob-card.feature::after{ width:22%; }
}

/* ---- rev39f: bar drops clear of the CTA buttons ----
   The deeper straddle from rev39c brought the bar's top to within a few px of
   the 立即免費諮詢 button. There is only ~80px between the CTA row's bottom and
   the photo's bottom edge, and the bar is 66px tall, so straddle depth and CTA
   clearance trade directly against each other. Clearance wins: the bar sits
   about 55px below the buttons and still crosses the photo's lower edge. */
.oi-jump{ --lift:clamp(140px, 16vw, 230px); }
@media (max-width:980px){ .oi-jump{ --lift:0px; } }

/* ---- rev39g: resolve the three-way collision round the bar ----
   The bar's top, the CTA clearance and the stat strip all key off the same
   edge, so tuning one moved the others. Measured constraints at desktop:
     · bar top must be < photo bottom (887) to straddle at all
     · bar top must be > ~860 to stay clear of the CTA row (ends 818)
       → a 27px window, so the straddle is inherently shallow here
     · bar bottom must clear the stat strip
   Because hero height = photo + stat gap + stat height + padding, and the bar
   is lifted from the hero's bottom, growing the stat gap dragged the bar down
   with it. Solved simultaneously: stat gap ~69px, hero bottom padding ~8px,
   lift ~199px. That lands bar 864-930, stats at 955 (25px clear), CTA gap 46px. */
.hero-overlap{ padding-bottom:clamp(6px,0.8vw,12px); }
.hero-overlap .hp-stat-strip{ margin-top:clamp(46px,5.4vw,76px); }
.oi-jump{ --lift:clamp(150px, 15.5vw, 220px); }

/* rev68 — rev67 嘅手尾。上面嗰 6–12px 底部留白係為「有相片」嗰版度身調 ——
   跳轉列靠 --lift 爬上去疊住相片框，所以 hero 底部唔需要留位。客戶第六輪叫
   拎走其他保險嘅 hero 相片之後，lift 歸零（見 .hero-product.no-photo +
   .oi-jump），嗰 6–12px 就變成「立即免費諮詢」兩粒掣同跳轉列之間僅有嘅距離
   —— 實測 11px，掣幾乎貼實條棕色列。冇相片就冇得疊，底部留白要回復正常。
   實測 11px → 1440px 43px · 1180px 35px · 1024px 32px。三個語言一樣，因為
   呢個同文字長短無關，係版面本身。 */
.hero-product.no-photo.hero-overlap{ padding-bottom:clamp(32px,3vw,44px); }
@media (max-width:980px){
  .oi-jump{ --lift:0px; }
  .hero-overlap{ padding-bottom:clamp(40px,6vw,72px); }
  .hero-overlap .hp-stat-strip{ margin-top:var(--gap); }
}

/* ============================================================
   rev40: card texture reworked — homepage geometry over a soft wash
   ------------------------------------------------------------
   The blurred red disc read as a smudge on the vertical cards. Replaced with
   the language the homepage's four 核心優勢 cards already use, so the two pages
   share one texture system instead of inventing a second:

     · a CIRCLE and a ROTATED ROUNDED-SQUARE, anchored outside a corner and
       filled with color-mix gradients that fade to transparent — crisp shapes,
       no blur filter, exactly as .point-card does it;
     · laid over a SUBTLE bottom-to-top red wash carried on the card's own
       background, which is what the blurred disc was failing to do.

   Homepage anchors these to the top-right; here they go bottom-right, because
   on these cards the top is photograph and only the lower half is white.
   ============================================================ */
.lob-card{
  background:
    linear-gradient(0deg, rgba(178,10,48,.115) 0%, rgba(178,10,48,.04) 24%, rgba(178,10,48,0) 50%),
    var(--surface);
}
/* rotated rounded-square — mirrors .point-card::after */
.lob-card::before{
  width:96px; height:96px; aspect-ratio:auto;
  left:auto; top:auto; right:-40px; bottom:-12px;
  border-radius:22px; transform:rotate(20deg);
  filter:none;
  background:linear-gradient(150deg,
     color-mix(in oklch, var(--red) 26%, white) 0%,
     color-mix(in oklch, var(--red) 11%, white) 42%, transparent 76%);
}
/* circle — mirrors .point-card::before */
.lob-card::after{
  width:120px; height:120px; aspect-ratio:auto;
  left:auto; top:auto; right:-22px; bottom:-44px;
  border-radius:50%; transform:none;
  filter:none;
  background:radial-gradient(circle at 34% 34%,
     color-mix(in oklch, var(--rose) 36%, white) 0%,
     color-mix(in oklch, var(--rose) 15%, white) 46%, transparent 72%);
}
/* the wide cards get the same pair a size down, so the corner keeps its weight
   rather than its percentage */
.lob-card.feature::before{ width:78px; height:78px; right:-32px; bottom:-10px; }
.lob-card.feature::after{ width:96px; height:96px; right:-18px; bottom:-36px; }

/* ---- bar: final clearance pass ----
   Slimmer bar + a shorter lift buys back room in the 69px window between the
   CTA row and the photo's bottom edge. Straddle stays shallow by necessity —
   clearing the buttons and the stat strip both win over crossing deeper. */
.oi-jump-row{ padding-block:clamp(7px,0.85vw,11px); }
.oi-jump{ --lift:clamp(130px, 13.5vw, 195px); }
.hero-overlap .hp-stat-strip{ margin-top:clamp(52px,6vw,84px); }
@media (max-width:980px){
  .oi-jump{ --lift:0px; }
  .hero-overlap .hp-stat-strip{ margin-top:var(--gap); }
}

/* ---- rev40b: lift corrected ----
   rev40 overshot — measured straddle -14px (bar sat clear BELOW the photo) and
   only 1px above the stat strip. Raising the lift ~33px puts it back across the
   photo's edge while keeping both clearances: CTA ~31px, stats ~34px. */
.oi-jump{ --lift:clamp(155px, 16vw, 225px); }
@media (max-width:980px){ .oi-jump{ --lift:0px; } }

/* ---- rev40c: the two shapes go translucent so they blend where they meet ----
   They were filled with color-mix(..., white), which is opaque — the square
   simply covered the circle and the overlap showed as a hard edge. Switching to
   rgba fills plus multiply means the overlap accumulates into a deeper tone, so
   the pair reads as one blended form instead of two stacked cut-outs. */
.lob-card::before{
  mix-blend-mode:multiply;
  background:linear-gradient(150deg, rgba(178,10,48,.34) 0%, rgba(178,10,48,.12) 44%, rgba(178,10,48,0) 78%);
}
.lob-card::after{
  mix-blend-mode:multiply;
  background:radial-gradient(circle at 34% 34%, rgba(176,132,118,.44) 0%, rgba(176,132,118,.17) 46%, rgba(176,132,118,0) 72%);
}

/* ---- rev40d: the two shapes eased back ~a third (client) ---- */
.lob-card::before{
  background:linear-gradient(150deg, rgba(178,10,48,.22) 0%, rgba(178,10,48,.08) 44%, rgba(178,10,48,0) 78%);
}
.lob-card::after{
  background:radial-gradient(circle at 34% 34%, rgba(176,132,118,.29) 0%, rgba(176,132,118,.11) 46%, rgba(176,132,118,0) 72%);
}

/* ---- rev40e: another 30% off both shapes (client) ---- */
.lob-card::before{
  background:linear-gradient(150deg, rgba(178,10,48,.154) 0%, rgba(178,10,48,.056) 44%, rgba(178,10,48,0) 78%);
}
.lob-card::after{
  background:radial-gradient(circle at 34% 34%, rgba(176,132,118,.203) 0%, rgba(176,132,118,.077) 46%, rgba(176,132,118,0) 72%);
}

/* ---- rev41: jump bar on rose-deep, centred; card icon tiles removed ----
   Contrast note: --rose-deep is mid-dark (L .118), so the chip fill has to stay
   light or paper text stops clearing AA at 13px. Measured: a white fill at .10
   lands 4.45:1 — just under. At .07 it is 4.88:1, so that is what ships. The
   lead label takes full --paper (5.64:1) rather than a faded one, which came
   out at 4.00:1. */
.oi-jump{
  background:var(--rose-deep);
  border-block:1px solid rgba(248,242,237,.16);
}
.oi-jump-row{ justify-content:center; }
.oi-jump-lead{ color:var(--paper); }
.jump-chip{
  background:rgba(255,255,255,.07);
  border-color:rgba(248,242,237,.40);
  color:var(--paper);
}
.jump-chip svg{ color:var(--paper); }
.jump-chip:hover{
  background:rgba(255,255,255,.17);
  border-color:var(--paper);
  color:var(--paper);
}
.jump-chip:hover svg{ color:var(--paper); }

/* the red icon tile is gone from every card — the badge is now just the
   italic category label, so it no longer needs the flex gap */
.lob-badge{ gap:0; }

/* ---- rev41b: chips take a dark fill on the rose-deep bar ----
   A light fill (white .07) put the chip labels at a measured 4.68:1, and 3.74:1
   on the two-character ones where antialiasing on fine CJK strokes eats into
   the effective contrast. Flipping the fill dark instead of light, with white
   text, takes it to ~7.7:1 and gives the chips more definition against a
   mid-tone bar than a barely-lighter-than-background fill did. */
.jump-chip{
  background:rgba(28,14,10,.20);
  border-color:rgba(248,242,237,.44);
  color:#fff;
}
.jump-chip svg{ color:#fff; }
.jump-chip:hover{ background:rgba(28,14,10,.38); border-color:#fff; color:#fff; }

/* ---- rev42: cards are informational only — no per-card CTA, no link ----
   With the whole-card link overlay and the 查詢報價 CTA both gone, the hover
   lift, shadow and photo zoom are affordances promising an interaction that no
   longer exists, so they go too. The single 立即免費諮詢 in the hero and the CTA
   banner at the foot of the page carry the conversion. */
.lob-card{ transition:none; }
.lob-card:hover{ transform:none; box-shadow:none; border-color:var(--line-strong); }
.lob-card:hover .lob-media img{ transform:none; }
/* copy no longer needs to reserve room for a CTA pinned to the card's bottom */
.lob-content{ padding-bottom:22px; }
.lob-card.feature .lob-content{ padding-bottom:24px; }

/* ---- rev43: drop the photo→card seam fade (client) ----
   The mask goes; the photo keeps a clean hard edge against the white again.
   The scrim stays top-weighted and the badge stays at the top of the photo —
   that pairing is independent of the fade and still reads correctly. */
.lob-media{ -webkit-mask-image:none; mask-image:none; }

/* ============================================================
   rev44: some cards invert — ink and red grounds among the white
   ------------------------------------------------------------
   An inverted card is already part of this direction (catering's
   .cov-card.featured runs on ink), so this extends the existing system
   rather than inventing one.

   Placement is a deliberate rhythm, not a scatter — one inverted card per
   row, never two side by side:
     row 1  物業管理 white   · 商鋪中小企 INK
     row 2  出入口 white · 建築工程 RED · 裝修 white   (red centred, the accent)
     row 3  家居 white     · 個人 INK

   Measured contrast on the inverted grounds: heading paper-on-ink 15.3:1,
   body 10.2:1; paper-on-red 6.35:1, body 5.6:1.
   ============================================================ */
.lob-card.tone-ink,
.lob-card.tone-red{ border-color:rgba(248,242,237,.18); }

.lob-card.tone-ink{
  background:linear-gradient(0deg, rgba(255,255,255,.05) 0%, rgba(255,255,255,0) 50%), var(--ink);
}
.lob-card.tone-red{
  background:linear-gradient(0deg, rgba(0,0,0,.10) 0%, rgba(0,0,0,0) 50%), var(--red);
}
.lob-card.tone-ink .lob-content h3,
.lob-card.tone-red .lob-content h3{ color:var(--paper); }
.lob-card.tone-ink .lob-content p{ color:rgba(248,242,237,.80); }
.lob-card.tone-red .lob-content p{ color:rgba(248,242,237,.88); }

/* the corner shapes multiply on white, which would simply vanish into a dark
   ground — on the inverted cards they screen instead, so they read as light
   blooms rather than disappearing */
.lob-card.tone-ink::before,
.lob-card.tone-red::before,
.lob-card.tone-ink::after,
.lob-card.tone-red::after{ mix-blend-mode:screen; }
.lob-card.tone-ink::before{
  background:linear-gradient(150deg, rgba(178,10,48,.62) 0%, rgba(178,10,48,.20) 44%, transparent 78%);
}
.lob-card.tone-ink::after{
  background:radial-gradient(circle at 34% 34%, rgba(176,132,118,.40) 0%, rgba(176,132,118,.13) 46%, transparent 72%);
}
.lob-card.tone-red::before{
  background:linear-gradient(150deg, rgba(255,214,214,.22) 0%, rgba(255,214,214,.07) 44%, transparent 78%);
}
.lob-card.tone-red::after{
  background:radial-gradient(circle at 34% 34%, rgba(255,226,214,.26) 0%, rgba(255,226,214,.09) 46%, transparent 72%);
}

/* ---- rev44b: ink cards' corner shapes eased back further (client) ---- */
.lob-card.tone-ink::before{
  background:linear-gradient(150deg, rgba(178,10,48,.30) 0%, rgba(178,10,48,.09) 44%, transparent 78%);
}
.lob-card.tone-ink::after{
  background:radial-gradient(circle at 34% 34%, rgba(176,132,118,.19) 0%, rgba(176,132,118,.06) 46%, transparent 72%);
}

/* ============================================================
   rev34: about — 言 watermark moved out of the lead (it was
   cropped by the section and collided with 信) and re-hung
   behind the RIGHT edge of the story paragraph, fully visible.
   The harbour band loses its frame: it is now a sky-removed
   cut-out whose skyline IS the top edge, pulled up so the
   持牌保險代理機構 card lands over it.
   ============================================================ */

/* --- 言 behind the story copy ------------------------------ */
.about-story-sec{ position:relative; }
.sign-wm.story span{
  top:auto; bottom:auto; left:auto;
  right:clamp(30%,34vw,40%);
  transform:none;
  font-size:clamp(160px,19vw,290px);
  opacity:.055;
}
/* the glyph box hangs from the top of the section so it reads as
   "behind the right shoulder of the paragraph", never clipped */
.sign-wm.story{ overflow:visible; }
.sign-wm.story span{ top:6%; }
.about-story, .about-aside{ position:relative; z-index:1; }
@media (max-width:980px){
  .sign-wm.story span{ right:2%; font-size:clamp(140px,22vw,220px); opacity:.045; }
}

/* --- harbour cut-out band ---------------------------------- */
.about-band{ padding-block:0 clamp(8px,1.1vw,14px); position:relative; z-index:0; }
/* 合作保險公司 sits tight under the skyline — the photo introduces it */
.about-partners{ padding-top:clamp(20px,2.6vw,36px); }
.about-band-cell{ grid-column:1/13; position:relative; }
/* the sky is gone so the skyline IS the top edge; the bottom + sides keep
   the house rounded-rectangle so it still reads as a placed photo */
.about-band-cell img{
  display:block; width:100%; height:auto;
  margin-top:clamp(-190px,-13vw,-90px);
  border-radius:var(--radius-lg);
}
.about-band-cap{
  font-family:var(--font-caption); font-style:italic;
  font-size:clamp(14px,1.4vw,16.5px); color:var(--rose-deep);
  max-width:40em; margin-top:16px; position:relative; z-index:1;
}
/* whichever card sits lowest is the one that lands on the skyline — it must
   paint above the photo */
.about-aside .about-fact:last-child{ z-index:4; }
@media (max-width:980px){
  .about-band-cell img{ margin-top:0; }
}

/* ============================================================
   rev35: about — 核心成員 / 梁榮洸 moves out of the lead and signs
   off the white fact card, taking the "Since 2018" label slot.
   ============================================================ */
.about-signoff.in-card{
  margin-top:16px; padding-top:14px;
  border-top:1px solid var(--line-strong);
}
.about-signoff.in-card .signoff-line{ align-items:baseline; gap:10px; }
.about-signoff.in-card .signoff-name{ font-size:1.15rem; }
.about-signoff.in-card .signoff-role{ font-size:12.5px; }

/* the 信言保險 signature comes off the lead (where it collided with the
   sub-headline) and signs the card instead — small, right-aligned, sitting
   just above the name it belongs to */
.about-signoff.in-card{ position:relative; }
.card-signature{
  position:absolute; right:0; top:-32px; z-index:1;
  width:min(38%,112px); height:auto;
  transform:rotate(-3deg); transform-origin:right center;
  opacity:.55; pointer-events:none;
}

/* ============================================================
   rev36: homepage solutions — 餐飲業 / 安老院舍 cards sit on their
   own light card (white surface + hairline, matching .point-card
   and .review-card) instead of floating on the paper. Photo keeps
   an inset radius so it reads as placed inside the container.
   ============================================================ */
.ind-card.catering,
.ind-card.elderly{
  background:var(--surface);
  border:1px solid var(--line-strong);
  border-radius:var(--radius-lg);
  padding:clamp(14px,1.6vw,18px);
}
.ind-card.catering .ind-photo,
.ind-card.elderly .ind-photo{ border-radius:var(--radius-md); }
/* the body no longer needs the rev23 gap-fill padding — the container
   supplies the outer breathing room now */
.ind-card.catering .ind-body,
.ind-card.elderly .ind-body{ padding:22px 6px 6px; }

/* ============================================================
   rev37: solutions cards — borderless, warmer-than-white surface,
   and the tall 餐飲 card stops short of the red chips block so it
   carries the same breathing gap the 安老院舍 card has.
   ============================================================ */
.ind-card.catering,
.ind-card.elderly{
  border:0;
  background:color-mix(in oklch, var(--surface) 55%, var(--paper));
}
/* the catering cell spans two grid rows, so without this it stretches flush
   against the chips block below — reserve one grid gap at its foot */
.ind-card.catering{ margin-bottom:var(--gap); }

/* ============================================================
   rev38: solutions cards — tighter inner padding so the two photos
   don't drift apart; 其他保險方案 umbrella dropped a little; the
   seven chips shrunk enough to hold one row on desktop.
   ============================================================ */
/* the padding sits INSIDE the card, so it read as extra gutter between
   餐飲 / 安老 and between 安老 / 其他保險方案 — pull it back in */
.ind-card.catering,
.ind-card.elderly{ padding:clamp(9px,1.05vw,12px); }
.ind-card.catering .ind-body,
.ind-card.elderly .ind-body{ padding:18px 5px 5px; }

/* umbrella hangs a little lower over the red cell */
.cutout-cell-graphic{ top:-8px; }

/* chips: all seven must hold ONE row across the whole desktop range.
   This was previously done with fixed px, which only fitted at the width it
   was tuned on and re-wrapped everywhere else. The seven labels total 43 CJK
   glyphs, so the row's width is dominated by font-size — scale type, padding,
   gap and icon together with the viewport and the row stays single-line from
   the 980px breakpoint up to the 1360px .wrap ceiling. Below 980px the
   layout goes narrow and wrapping is correct.
   The floors are set so the scaling runs all the way DOWN to the 980px
   breakpoint rather than bottoming out early — a floor that engages at, say,
   1050px leaves a 980–1050 band where the type has stopped shrinking but the
   container hasn't, and the row wraps again there. */
.other-lines-row{ gap:clamp(5px,0.7vw,10px); padding:30px clamp(13px,2vw,30px); }
.line-chip{
  padding:clamp(8px,0.95vw,12px) clamp(9px,1.15vw,16px);
  font-size:clamp(11px,1.12vw,14px);
  gap:clamp(4px,0.6vw,8px);
  white-space:nowrap;
}
.line-chip svg{ width:clamp(13px,1.3vw,17px); height:clamp(13px,1.3vw,17px); }

/* ============================================================
   rev39: solutions cluster — one uniform gutter.
   rev32 had widened this grid's gutter to 36px so the red L would
   read as a distinct object against the bare paper. Now that 餐飲 /
   安老 carry their own light surface the L separates by colour on
   its own, and the wide gutter only made the two white blocks drift
   apart — visibly wider than the 16px the 餐飲 card keeps above the
   chips band. Every gap in the cluster is now var(--gap).
   ============================================================ */
.solutions-grid{ column-gap:var(--gap); row-gap:var(--gap); }

/* ============================================================
   rev40: the seven chips are links now — each one deep-links to its
   own section on 其他保險. They keep the flat chip look; hover just
   warms the fill so the affordance reads without adding a border or
   underline that would break the row's evenness.
   ============================================================ */
a.line-chip{ text-decoration:none; transition:background .2s var(--ease), border-color .2s var(--ease); }
a.line-chip:hover{ background:rgba(255,255,255,.18); border-color:var(--paper); }
/* the sticky nav would otherwise sit on top of the section a chip lands on */
.lob-card{ scroll-margin-top:96px; }

/* ============================================================
   rev41: close the run-out between the solutions block and the red
   了解您的業務 band. rev22 had given .other-lines a 140px foot when
   the L sat on bare paper and needed the room; the band below brings
   its own generous top padding, so the two stacked into ~260px of
   dead cream.
   ============================================================ */
.other-lines{ padding-bottom:clamp(40px,4.6vw,58px); }

/* ============================================================
   rev42: 餐飲業保險 — the coverage bento gets bolder.

   Three problems, one pass:
   (a) The 財物損害 featured card carried a label, a heading, two
       lines of copy and five pills inside a 710×624 ink panel —
       roughly 400px of dead black in the middle of the page's
       most important section.
   (b) 02/03/04 sat at span-6, which orphaned an empty half-row
       under 金錢損失. They are span-4 now: one clean row of three,
       no orphan, and each card gets a taller, narrower canvas.
   (c) The three white cards were pure text. Each now carries a
       cut-out object as a background key visual with a radial
       bloom under it, so the bento reads as a composed set rather
       than one big dark box plus three notes.

   The featured card's five pills are no longer decoration — they
   ARE the slideshow's tabs. Binding the two is what earns the
   space: the risk vocabulary and the imagery explain each other.
   ============================================================ */

/* ---- one row of three for the small cards ---- */
.cov-card.keyvis{ grid-column:span 4; }

/* ---- 01 財物 — the risk stage ---- */
.riskstage{ gap:14px; }
.rs-stage{
  position:relative;
  margin-top:6px;
  aspect-ratio:16/10;
  border-radius:var(--radius-md);
  overflow:hidden;
  background:#15100D;
  /* the photos are dark; a hairline keeps the panel from dissolving
     into the ink card behind it */
  box-shadow:inset 0 0 0 1px rgba(241,214,201,.12);
}
.rs-slide{
  position:absolute; inset:0; margin:0;
  opacity:0; visibility:hidden;
  transition:opacity .7s var(--ease), visibility 0s linear .7s;
}
.rs-slide.is-on{ opacity:1; visibility:visible; transition:opacity .7s var(--ease); }
.rs-slide img{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; will-change:transform;
}
.rs-slide figcaption{
  position:absolute; left:0; right:0; bottom:0;
  padding:56px clamp(16px,2vw,24px) clamp(14px,1.6vw,20px);
  font-size:14.5px; font-weight:500; line-height:1.6; color:#FFF;
  text-shadow:0 1px 3px rgba(0,0,0,.5);
  background:linear-gradient(0deg,
    rgba(14,10,8,.92) 0%, rgba(14,10,8,.86) 38%, rgba(14,10,8,.42) 72%, transparent 100%);
}

/* ---- the pills become the tab bar ---- */
.rs-nav{ gap:8px; margin-top:2px; }
/* specificity note: `.cov-card.featured .pill` already sets the ink-card
   pill colours, so every chip rule here has to be scoped the same depth
   or the active state silently loses. */
.cov-card.featured .rs-chip{
  position:relative; overflow:hidden;
  font-family:inherit; font-size:12px; font-weight:700; line-height:1.7;
  padding:6px 13px 8px; border:0; border-radius:999px; cursor:pointer;
  background:rgba(241,214,201,.14); color:#F1D6C9;
  transition:background .25s var(--ease), color .25s var(--ease);
}
.cov-card.featured .rs-chip:hover{ background:rgba(241,214,201,.28); color:#FFF; }
.cov-card.featured .rs-chip.is-on{ background:var(--red); color:#FFF; }
.cov-card.featured .rs-chip:focus-visible{ outline:2px solid #F1D6C9; outline-offset:2px; }
.rs-rail{
  position:absolute; left:0; right:0; bottom:0; height:2px;
  background:rgba(255,255,255,.9);
  transform:scaleX(0); transform-origin:left center;
}

/* ---- 02/03/04 — cut-out key visual over a radial bloom ----
   Card reads top-down: label → title → copy → [visual zone] → pills.
   The object owns the bottom-right of that zone and bleeds off both
   edges, so it reads as a key visual the card is built around rather
   than an icon dropped into a corner. The pills sit in front of it at
   bottom-left, capped in width so the two never collide. */
.cov-card.keyvis{
  position:relative; overflow:hidden; isolation:isolate;
  min-height:412px; padding-bottom:28px;
}
/* NB: must exclude .cov-key — a bare `> *` here outranks the element's
   own `position:absolute` and drops the object back into the flow, which
   inflates every card by the height of its artwork. */
.cov-card.keyvis > :not(.cov-key){ position:relative; z-index:1; }
.cov-card.keyvis .cov-tags{ max-width:58%; }

.cov-key{
  position:absolute; z-index:0; pointer-events:none;
  right:-28px; bottom:-22px; height:64%;
  transition:transform .5s var(--ease);
}
/* the bloom — a red-into-rose radial sitting UNDER the object, so the
   cut-out reads as lit from the page rather than pasted on top of it */
.cov-key::before{
  content:''; position:absolute; inset:-42% -34% -30% -46%; border-radius:50%;
  background:radial-gradient(circle at 50% 56%,
    color-mix(in oklch, var(--red) 26%, transparent) 0%,
    color-mix(in oklch, var(--rose) 24%, transparent) 36%,
    color-mix(in oklch, var(--rose) 9%, transparent) 58%,
    transparent 74%);
  transition:transform .5s var(--ease);
}
.cov-key img{
  position:relative; display:block; height:100%; width:auto;
  /* pull all three objects into one tonal family with the palette —
     the yellow sign in particular is far too loud at full saturation */
  filter:saturate(.7) contrast(1.03) drop-shadow(0 18px 28px rgba(34,27,23,.2));
}
/* a touch of per-card rotation so the three don't read as a stamped set */
.kv-liability .cov-key{ transform:rotate(-4deg); height:68%; }
/* the caution sign is the one loud chroma in the set — held back further
   so the three objects sit in the same tonal family */
.kv-liability .cov-key img{ filter:saturate(.52) contrast(1.05) drop-shadow(0 18px 28px rgba(34,27,23,.2)); }
.kv-employee  .cov-key{ transform:rotate(2.5deg); }
.kv-money     .cov-key{ transform:rotate(-2deg); }

.cov-card.keyvis:hover .cov-key::before{ transform:scale(1.08); }
.kv-liability:hover .cov-key{ transform:rotate(-4deg)  translateY(-8px) scale(1.04); }
.kv-employee:hover  .cov-key{ transform:rotate(2.5deg) translateY(-8px) scale(1.04); }
.kv-money:hover     .cov-key{ transform:rotate(-2deg)  translateY(-8px) scale(1.04); }

/* ---- responsive ---- */
@media (max-width: 980px){
  .cov-card.keyvis{ grid-column:span 6; min-height:400px; }
  .cov-key{ height:52%; }
  .kv-liability .cov-key{ height:56%; }
}
@media (max-width: 620px){
  .cov-card.keyvis{ grid-column:span 4; min-height:390px; }
  .cov-card.keyvis .cov-tags{ max-width:62%; }
  .rs-stage{ aspect-ratio:4/3; }
  .rs-slide figcaption{ font-size:13.5px; padding-top:48px; }
  .cov-key{ height:46%; right:-18px; bottom:-16px; }
  .kv-liability .cov-key{ height:50%; }
}

@media (prefers-reduced-motion: reduce){
  .rs-slide,
  .rs-slide.is-on{ transition:opacity .01ms linear; }
  .cov-key::before,
  .cov-key img{ transition:none; }
}

/* ============================================================
   rev43: FIX — .coverage-grid was never responsive.

   .grid12 steps 12 → 6 → 4 columns, but .coverage-grid is a
   nested grid that declared repeat(12,1fr) once and kept it at
   every width. So the breakpoint rules that read `span 6` and
   `span 4` were still spanning a TWELVE-column track: half width
   at tablet (which looked right by luck) and one-THIRD width at
   mobile, where every card was meant to go full-bleed. Cards came
   out ~115px wide on a 405px viewport.

   The nested grid now steps with its parent, and the spans are
   restated against each new track count. Shared CSS, so this fixes
   安老院舍保險's coverage bento in the same move.
   ============================================================ */
@media (max-width: 980px){
  .coverage-grid{ grid-template-columns:repeat(6,1fr); }
  .cov-card,
  .cov-card.featured,
  .cov-card.photo,
  .cov-card.keyvis{ grid-column:span 3; }
}
@media (max-width: 620px){
  .coverage-grid{ grid-template-columns:repeat(4,1fr); }
  .cov-card,
  .cov-card.featured,
  .cov-card.photo,
  .cov-card.keyvis{ grid-column:span 4; }
}

/* ============================================================
   rev45: close the run-out above 餐飲業客戶的真實評價 (client).
   .form-section ends on 120px of padding and .reviews opened with
   another 120px, stacking into 240px of empty cream before the
   heading even starts. The form card already reads as finished;
   the testimonials only need enough air to register as a new
   section, not a full section break twice over.

   Measured card-bottom → heading-top: 196px before, 144px after.
   Most of it belonged to .form-section, so the trim is split across
   both rather than crushing the testimonials' own top padding to
   nothing and leaving the heading crowding the form card.
   ============================================================ */
.form-section{ padding-bottom:clamp(48px,5.4vw,72px); }
.reviews{ padding-top:clamp(40px,4.6vw,60px); }

/* ============================================================
   rev46: 餐飲業客戶的真實評價 takes the homepage's red-band device
   (client). Same three layers as index.html's 「了解您的業務」:

     1. solid brand red ground
     2. a deep-red radial bloom, so the ground has depth rather than
        reading as a flat swatch
     3. an oversized cut-out anchored off the right edge, multiply-
        blended and held back in opacity

   The cut-out here is the milk-tea kettle pour — the one object on the
   site that means "a cha chaan teng actually running", which is what a
   page of operator testimonials is claiming. Same asset the homepage
   headline uses, so the two red bands read as one device rather than
   two coincidences.

   The white review cards stay white: they are the content, and on red
   they read as pinned-up notes. Only the section head inverts.
   ============================================================ */
.reviews{
  position:relative; overflow:hidden;
  background:var(--red);
}
.reviews::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(58% 92% at 78% 50%, var(--red-deep) 0%, transparent 62%);
}
.reviews::after{
  content:""; position:absolute; top:50%; right:-4%; transform:translateY(-50%);
  width:min(470px,42%); aspect-ratio:1/1; z-index:0; pointer-events:none;
  background:url('assets/cutouts/kettle-pour.png') center right / contain no-repeat;
  mix-blend-mode:multiply; opacity:.34;
}
.reviews .wrap{ position:relative; z-index:1; }

/* section head inverts onto the red ground — .section-head h2 is brand red
   by default, which would vanish here */
.reviews .section-head h2{ color:var(--paper); }
/* .86 measured 4.46:1 on the plain red ground — a hair under AA. .92 → 4.79:1 */
.reviews .section-head p{ color:rgba(241,228,213,.92); }

/* the cards lift off the ground rather than sitting flat on it */
.reviews .review-card{
  border-color:transparent;
  box-shadow:0 14px 34px rgba(60,4,16,.22);
}
@media (max-width: 980px){
  .reviews::after{ width:min(420px,62%); opacity:.3; }
}


/* ============================================================
   rev47: 專為餐飲業而設 — the SPLIT band.

   This replaces the whole rev44a–g stack, which has been deleted rather
   than retained. Four rounds of client notes on that band all circled the
   same conflict and none of them could resolve it, because it wasn't a
   tuning problem:

     dark scrim        → 「too dark」
     .93 paper veil    → 「too pale」
     veil + copy pane  → photo survived, but the pane was an object
                          nobody asked for
     asymmetric veil   → 「still too washed out」

   Dark text sitting ON a photograph needs a veil, and any veil strong
   enough to carry AA text takes the life out of the image. The tension is
   structural. So the copy comes OFF the photograph: it sits on the cream
   ground in columns 1–6, and the photograph takes columns 7–12 at full
   strength with no veil, no filter and no scrim of any kind. Contrast
   stops being a negotiation and the picture is finally just a picture.

   The panel bleeds off the right edge — deliberately overshooting its
   track and letting the section's own clip do the cutting, which avoids
   any 100vw/scrollbar arithmetic. Left corners stay soft (radius-lg) per
   the direction's corner system; the right side has no corners to round.

   NOTE vs. the approved sketch: that showed a feathered inner edge. A
   feather fights the rounded corner and reads as a smudge against cream,
   so the join is a crisp soft-cornered edge instead — the same language
   as the hero's photo frame.
   ============================================================ */
.why-split{ position:relative; overflow:clip; background:var(--paper); }
.why-split .section-head{ grid-column:1/7; align-self:center; margin-bottom:0; }

.why-photo-col{
  grid-column:7/13;
  position:relative;
  margin:0 0 clamp(36px,5vw,56px);
  min-height:clamp(300px,30vw,440px);
  overflow:hidden;
  border-radius:var(--radius-lg) 0 0 var(--radius-lg);
  /* bleed to the viewport edge: the wrap's own inline padding, plus the
     centring gutter once the viewport passes .wrap's 1360px max-width.
     100vw counts the scrollbar, so this overshoots by a few px — which
     .why-split's clip absorbs. A blunt fixed overshoot would work too, but
     it inflates the column's real width and re-crops the photograph. */
  margin-right:calc(-1 * (var(--pad) + max(0px, (100vw - 1360px) / 2)));
  background:var(--surface-sunk);
}
.why-photo-col img{
  position:absolute; inset:-9% 0; width:100%; height:118%;
  object-fit:cover; object-position:54% 46%;
  will-change:transform;
}

/* parallax: the photo pans inside its own column. CSS scroll-driven, so it
   runs off the main thread and simply sits still where unsupported. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .why-photo-col img{
      animation:whyColPan linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
    }
    @keyframes whyColPan{
      from{ transform:translate3d(0,-4%,0); }
      to  { transform:translate3d(0, 4%,0); }
    }
  }
}

/* the four cells keep 其他保險's .lob-card corner-bloom texture — rotated
   rounded-square + circle anchored outside the bottom-right over a soft red
   wash, at the rev40e strengths already signed off there. */
.why-split .why-item{
  position:relative; overflow:hidden; isolation:isolate;
  border:1px solid var(--line-strong);
  background:
    linear-gradient(0deg, rgba(178,10,48,.115) 0%, rgba(178,10,48,.04) 24%, rgba(178,10,48,0) 50%),
    var(--surface);
  box-shadow:0 6px 20px rgba(34,27,23,.08);
  transition:background .35s var(--ease), box-shadow .35s var(--ease), transform .35s var(--ease);
}
.why-split .why-item > *{ position:relative; z-index:1; }
.why-split .why-item::before,
.why-split .why-item::after{
  content:''; position:absolute; z-index:0; mix-blend-mode:multiply; pointer-events:none;
}
.why-split .why-item::before{      /* rotated rounded-square */
  width:96px; height:96px; right:-40px; bottom:-12px;
  border-radius:22px; transform:rotate(20deg);
  background:linear-gradient(150deg,
    rgba(178,10,48,.154) 0%, rgba(178,10,48,.056) 44%, rgba(178,10,48,0) 78%);
}
.why-split .why-item::after{       /* circle */
  width:120px; height:120px; right:-22px; bottom:-44px; border-radius:50%;
  background:radial-gradient(circle at 34% 34%,
    rgba(176,132,118,.203) 0%, rgba(176,132,118,.077) 46%, rgba(176,132,118,0) 72%);
}
.why-split .why-item:hover{
  background:
    linear-gradient(0deg, rgba(178,10,48,.14) 0%, rgba(178,10,48,.05) 24%, rgba(178,10,48,0) 50%),
    var(--surface);
  box-shadow:0 14px 34px rgba(34,27,23,.14);
  transform:translateY(-3px);
}

/* stacked: photo first, then copy, then the cells */
@media (max-width: 980px){
  /* NB 1/13, not 1/-1: `-1` resolves to the last EXPLICIT line, and .grid12
     drops to 6 (then 4) explicit columns while .why-list-grid's own 1/13
     keeps generating implicit tracks. 1/-1 stops short of the row's real
     width — the panel came out 325px inside a 365px row. */
  .why-split .section-head{ grid-column:1/13; margin-bottom:clamp(28px,4vw,40px); }
  .why-photo-col{
    grid-column:1/13; order:-1;
    min-height:clamp(220px,42vw,320px);
    margin-right:calc(-1 * var(--pad));
    margin-left:calc(-1 * var(--pad));
    border-radius:0;
    margin-bottom:clamp(28px,4vw,40px);
  }
}

/* ============================================================
   rev48: .section-head's title and its intro were flush.

   The global reset zeroes every p margin and .section-head never put one
   back, so the intro paragraph sat hard against the h2 on EVERY section of
   EVERY page — it only became obvious on the two-line headings. Fixed at
   the component rather than per-section (client raised it on 專為餐飲業而設
   and again on 一份保單).
   ============================================================ */
.section-head p{ margin-top:clamp(14px,1.5vw,20px); }

/* Client-specified line counts for three section ledes. The global 46ch measure
   broke each of them one line later than they wanted, so these widen past it —
   scoped per section, never globally, so every other lede keeps its measure. */
.partners-section .section-head p{ max-width:74ch; }   /* 合作保險公司 — 2 lines */
#coverage .section-head p{ max-width:none; }           /* 餐飲業保障 — 1 line   */
.section-head.lede-2 p{ max-width:68ch; }              /* 不只是一般院舍保險 — 2 */
/* hard break the client asked for after 「風險。」; dropped on phones, where the
   line wraps on its own and the forced break would strand a two-word line */
@media (max-width:620px){ .br-md{ display:none; } }

/* ============================================================
   rev49: 安老院舍保險 brought onto the same system.

   The care-home page carried the same three weaknesses the catering
   page had, so it takes the same three answers rather than new ones:

     · 財物全險's five pills become the risk stage's tabs, over its own
       care-home risk set (laundry-room ignition, a flooded corridor, a
       prised office cabinet, care equipment). The .riskstage / .rs-*
       rules are already generic — nothing new was needed for it.
     · the four remaining cells take the cut-out key visual + radial
       bloom. They stay span-6 (2×2) rather than catering's span-4 row
       of three, because there are four of them and a row of four at
       span-3 leaves the object no room.
     · 專為院舍營運風險設計 becomes the split band, with the home's
       manager in her own common room — the direct counterpart to the
       catering owner in his doorway.

   金錢損失 and 業務影響 reuse catering's cash box and the homepage's
   clock. Same coverage, same object: that is the system working, not
   a shortcut.
   ============================================================ */
.cov-card.keyvis.kv-wide{ grid-column:span 6; min-height:392px; }
.kv-wide .cov-key{ height:60%; right:-24px; bottom:-20px; }
.kv-wide .cov-tags{ max-width:64%; }

.kv-liability-eh .cov-key{ transform:rotate(-3deg); height:66%; }
.kv-employee-eh  .cov-key{ transform:rotate(2.5deg); height:54%; }
.kv-money-eh     .cov-key{ transform:rotate(-2deg); }
.kv-interrupt-eh .cov-key{ transform:rotate(3deg); height:56%; }

.kv-liability-eh:hover .cov-key{ transform:rotate(-3deg)  translateY(-8px) scale(1.04); }
.kv-employee-eh:hover  .cov-key{ transform:rotate(2.5deg) translateY(-8px) scale(1.04); }
.kv-money-eh:hover     .cov-key{ transform:rotate(-2deg)  translateY(-8px) scale(1.04); }
.kv-interrupt-eh:hover .cov-key{ transform:rotate(3deg)   translateY(-8px) scale(1.04); }

/* the walker is bare aluminium and the tunic is a cool blue — both sit
   outside the page's warm red/rose family, so they are pulled in harder
   than catering's objects needed */
.kv-liability-eh .cov-key img{ filter:saturate(.5) contrast(1.06) sepia(.16) drop-shadow(0 18px 28px rgba(34,27,23,.2)); }
.kv-employee-eh  .cov-key img{ filter:saturate(.42) contrast(1.04) sepia(.2) drop-shadow(0 18px 28px rgba(34,27,23,.2)); }

@media (max-width: 980px){
  .cov-card.keyvis.kv-wide{ grid-column:span 3; min-height:360px; }
  .kv-wide .cov-key{ height:52%; }
}
@media (max-width: 620px){
  .cov-card.keyvis.kv-wide{ grid-column:span 4; min-height:340px; }
  .kv-wide .cov-tags{ max-width:62%; }
}

/* the testimonials band's cut-out is per-page: the kettle pour means a
   cha chaan teng actually running; here it is a pair of caring hands */
/* the hands are a WIDE cut-out (1400×760) and a pale skin tone, so the
   square box + .30 opacity the kettle uses rendered them as a smudge.
   Given their own ratio and reaching in from the right edge, they carry
   the band the way the kettle does on 餐飲業保險. */
.reviews-care::after{
  background-image:url('assets/cutouts/caring-hands.png');
  background-position:center right;
  width:min(700px,58%); aspect-ratio:1400/760; right:-7%; opacity:.55;
}
@media (max-width: 980px){
  .reviews-care::after{ width:min(480px,72%); opacity:.42; }
}

/* ---- rev49b: the bloom was tuned for a 399px card ----
   On these 606px cells the same ::before spread renders a much larger
   disc, and it read as a pink patch sitting NEXT to the object rather
   than light coming from under it. Tightened and eased back, and the
   objects pushed further into the corner so the artwork leads. */
.kv-wide .cov-key{ right:-40px; bottom:-26px; }
.kv-wide .cov-key::before{
  inset:-26% -20% -22% -26%;
  background:radial-gradient(circle at 52% 58%,
    color-mix(in oklch, var(--red) 17%, transparent) 0%,
    color-mix(in oklch, var(--rose) 15%, transparent) 36%,
    color-mix(in oklch, var(--rose) 6%, transparent) 58%,
    transparent 74%);
}

/* the 盜竊 frame's foreground paperwork carries generated pseudo-Chinese.
   Crop toward the cabinet so it sits at the edge and under the scrim. */
#rs-p2 img{ object-position:32% 42%; }

/* ---- rev49c: the walker needs its own contrast, not the bloom's ----
   rev49b eased the bloom and the aluminium walking frame disappeared —
   light grey tubing on a white card, previously only legible because a
   strong pink disc sat behind it. That is the wrong dependency: an
   object should read on its own and the bloom should only add warmth.
   The frame is darkened and given a real contact shadow; the bloom comes
   back a third of the way, not all the way. */
.kv-wide .cov-key::before{
  inset:-32% -24% -26% -32%;
  background:radial-gradient(circle at 52% 58%,
    color-mix(in oklch, var(--red) 21%, transparent) 0%,
    color-mix(in oklch, var(--rose) 18%, transparent) 36%,
    color-mix(in oklch, var(--rose) 7%, transparent) 58%,
    transparent 74%);
}
.kv-liability-eh .cov-key img{
  filter:saturate(.4) contrast(1.2) brightness(.82) sepia(.2)
         drop-shadow(0 14px 22px rgba(34,27,23,.3));
}
.kv-employee-eh .cov-key img{
  filter:saturate(.45) contrast(1.08) brightness(.94) sepia(.22)
         drop-shadow(0 16px 26px rgba(34,27,23,.26));
}

/* ============================================================
   rev50: 聯絡我們's hero takes a photograph.

   Every other hero on the site is photographic — both product pages,
   其他保險, 關於我們. 聯絡我們 was the exception: a flat illustrated
   calendar glyph inside the rose-wash frame, which read as clip-art
   next to the site's real photography and next to its OWN cut-outs,
   which are photographic product shots (obj-contract, obj-badge), not
   drawings.

   The frame stays exactly as it is — same 5/4 ratio, same radius, same
   grid cell — and simply carries a photograph now: a licensed adviser
   taking a call at a Wan Chai desk, harbour beyond. That is literally
   what the page asks the visitor to do, and it echoes the harbour band
   on 關於我們 without repeating its photo.
   ============================================================ */
.hero-object.photo{
  overflow:hidden;
  border-color:transparent;
  background:var(--surface-sunk);
  box-shadow:var(--shadow-sm);
}
.hero-object.photo img{
  display:block; width:100%; height:100%;
  object-fit:cover; object-position:42% 46%;
}

/* ============================================================
   rev51: CTA routing unified + 聯絡我們 gets the red assurance panel.

   ROUTING. 索取報價 pointed at five different things depending on which
   page you were on (quote.html / #quote / contact.html / #enquiry). The
   rule now: **the nav button always goes to `quote.html#quote`**, on all
   eight pages — a same-page anchor on quote.html itself, a navigation
   everywhere else. In-page buttons keep pointing at the in-page form, so
   the two product pages still route their own hero / savings-strip / CTA
   traffic into their 3-step forms, which ask 座位數目 / 員工人數 /
   保單到期日 that quote.html's generic form cannot. 其他保險's hero CTA
   is 「立即免費諮詢」, not a quote — it still goes to contact.html.

   PANEL. 聯絡我們's 「您的查詢，由持牌顧問親自跟進」 band was a heading and
   a badge row on white. It now runs as copy-left / red-panel-right,
   reusing quote.html's `.quote-side-card` verbatim. The panel's CONTENT is
   contact-specific (what happens after you write in) rather than a copy of
   quote.html's 為何選擇 bullets — the design is shared, the words are not,
   so neither page reads as filler and the client proofs each once.
   ============================================================ */
.contact-assure .ca-lead{ grid-column:1/7; align-self:center; margin-bottom:0; }
.contact-assure .ca-side{ grid-column:7/13; }
.contact-assure .quote-side-card{ position:static; }
@media (max-width: 980px){
  .contact-assure .ca-lead,
  .contact-assure .ca-side{ grid-column:1/7; }
  .contact-assure .ca-side{ margin-top:28px; }
}
@media (max-width: 620px){
  .contact-assure .ca-lead,
  .contact-assure .ca-side{ grid-column:1/5; }
}

/* ---- rev51b: the badge row was a GRID SIBLING ----
   .trust-badges carried its own full-width span, so it opened a new grid
   row between the copy and the panel and shoved the aside underneath —
   and its widest badge pushed 10px of horizontal overflow. It belongs
   inside the copy cell. */
.contact-assure .trust-badges{ grid-column:auto; margin-top:24px; }

/* ---- rev51c: FIX — the sunburst escaped its own overflow:hidden ----
   .mat-sunburst clips with overflow:hidden, and the card reported
   scrollWidth == clientWidth, so the burst looked contained. It wasn't:
   the pseudo carries a `mask`, and a masked layer inside an
   overflow:hidden box still extended the DOCUMENT's scrollWidth in
   Chrome — 25px of real horizontal scroll on 聯絡我們, where the panel
   is short and wide so `aspect-ratio:1; height:80%` makes the burst
   far wider than on quote.html's tall sticky sidebar.

   `overflow:clip` does NOT fix it; `contain:paint` does, and it is what
   overflow:hidden was already trying to express here. Applied to the
   material itself so every panel using it is safe. */
.mat-sunburst{ contain:paint; }

/* ---- rev52: quote.html's hero takes a photograph too ----
   The last illustrated hero on a primary page. `obj-contract` (a drawn
   contract + pen) sat in the same frame 聯絡我們 just vacated, and the two
   form pages read as the site's illustrated corner while every other page
   was photographic. Same `.hero-object.photo` treatment — the frame, ratio
   and grid cell are untouched.

   The two photographs are deliberately different moments so the adjacent
   pages don't twin: 聯絡我們 is one adviser on the phone (reach us),
   quote.html is an adviser and an owner across a desk going through the
   proposal together (what a quote actually is).

   NB the crop is per-photo, so it is scoped to a modifier — an unscoped
   `.hero-object.photo img{object-position}` here would silently re-crop
   聯絡我們's hero, which sets its own. */
.hero-object.photo.hero-quote img{ object-position:50% 44%; }

/* ============================================================
   rev54: 不只是一般院舍保險 was locked to 3 columns on mobile.

   The markup carried an INLINE grid-template-columns:repeat(3,1fr).
   Inline styles outrank stylesheet rules, so the
   `@media (max-width:620px){ .highlight-row{ grid-template-columns:1fr } }`
   below could never fire — the three cells stayed 3-up and crushed to
   ~100px each on a phone. Moved to a modifier class so the breakpoints
   can do their job.
   ============================================================ */
.highlight-row.cols-3{ grid-template-columns:repeat(3,1fr); }
@media (max-width:980px){ .highlight-row.cols-3{ grid-template-columns:1fr 1fr; } }
@media (max-width:620px){ .highlight-row.cols-3{ grid-template-columns:1fr; } }

/* ============================================================
   rev55: fonts move to Google-hosted webfonts.

   The self-hosted set was 46 MB of raw TTF — NotoSansTC ×4 at 6.8 MB
   and NotoSerifTC ×2 at 9.5 MB — and every face was referenced, so a
   client opening the preview on a phone was pulling tens of megabytes
   of font before the page settled. That is not a deploy-weight problem,
   it is a page-speed problem on the deliverable itself.

   Google serves the identical Noto families as woff2 split into 643
   unicode-range slices, so a browser fetches only the slices whose
   glyphs actually appear — a few hundred KB for a page of Traditional
   Chinese instead of 46 MB. Same typefaces, same weights (Sans
   400/500/600/700, Serif 700/900), same `display=swap`.

   `Freight Text` was only ever an alias for EB Garamond Italic; the
   token now names the real family.

   TRADE-OFF, deliberate: the folder is no longer self-contained — it
   needs a network for type. That is right for a hosted preview and
   wrong for the offline zip. If a zip handoff is ever needed again,
   subset the TTFs to woff2 locally rather than restoring the 46 MB.
   ============================================================ */

/* ============================================================
   rev57: 安老院舍 hero — aim the crop at the pair, not the centre.
   .hp-photo-frame is a 4:5 portrait window onto a 16:9 photograph, so
   `object-fit:cover` with the default centre origin throws away most of
   the left third — which is exactly where the resident stands. The
   replacement photo puts the resident's face at ~45% and the caregiver's
   at ~70% of the source width. The 4:5 window only spans ~45% of that
   width, so it has to be aimed at the midpoint of the two faces (~57%)
   rather than at the image centre — a centre crop drops the caregiver,
   which is the one element the client specifically asked for. Scoped by
   filename so the 餐飲 hero, which shares .hp-photo-frame, is untouched.
   ============================================================ */
.hp-photo-frame img[src*="tile-elderly"]{ object-position:62% center; }

/* ============================================================
   rev58: 關於我們 — the two aside cards get a slight counter-rotation
   and a small horizontal offset, so the pair reads as two cards laid
   down by hand rather than a flush-stacked column.

   The tilt and shift live in custom properties instead of being baked
   into `transform`, because .about-fact ALREADY uses transform for the
   hover lift. If the rotation were written straight into the base rule,
   the hover's translateY would overwrite it and each card would snap
   upright on mouse-over. Composing from vars keeps the rest angle intact
   through the lift, and through the reduced-motion case below.
   ============================================================ */
.about-aside .about-fact{
  --rot:0deg; --tx:0px;
  transform:translate(var(--tx),0) rotate(var(--rot));
}
.about-aside .about-fact:nth-child(1){ --rot:-1.5deg; --tx:-12px; }
.about-aside .about-fact:nth-child(2){ --rot:1.2deg;  --tx:14px;  }
.about-aside .about-fact:hover{ transform:translate(var(--tx),-9px) rotate(var(--rot)); }

/* side-by-side from tablet down — the tilt reads as misalignment there,
   so the pair goes back to square. */
@media (max-width:980px){
  .about-aside .about-fact{ --rot:0deg; --tx:0px; }
}
/* keep the resting angle; only the lift is dropped */
@media (prefers-reduced-motion: reduce){
  .about-aside .about-fact:hover{ transform:translate(var(--tx),0) rotate(var(--rot)); }
}

/* ============================================================
   rev59: 餐飲業 / 安老院舍 hero — the three-stat strip's caption line
   (保費節省, 核心保障, 綜合保障 …) goes up 20%, 11.5 → 13.8px. At 11.5
   it sat below the page's smallest body size and read as a footnote
   rather than as the label for the figure above it.
   ============================================================ */
.hp-stat span{ font-size:13.8px; }

/* ============================================================
   rev60: 為何選擇我們 — .section-head gains an eyebrow above the H2,
   matching the .caption treatment already used on every page hero.
   .caption is inline by default (it sits inline in some contexts), so
   it needs display:block here to sit on its own line; .section-head h2
   already carries a 12px top margin for exactly this case.
   ============================================================ */
.section-head .caption{ display:block; }

/* ============================================================
   rev61: COVERAGE EXPLORER — the coverage bento becomes ONE stage
   driven by a row of buttons (4 on 餐飲, 5 on 安老院舍).

   Only coverage 01 keeps the risk slideshow; 02+ show a single key
   visual. The panels are stacked in ONE grid cell rather than being
   shown/hidden with `display`, so the stage takes the height of its
   TALLEST panel and never jumps as you tab between coverages — and
   the inactive panels can still cross-fade.
   ============================================================ */
.cov-explorer{
  grid-column:1/13;
  display:grid;
  /* the original bento's two columns are kept: the stage on the left where
     the featured 財物 card used to be, and the right column — which used to
     hold the photo + the smaller coverage cards — now holding the buttons
     that drive the stage. */
  grid-template-columns:minmax(0,1fr) clamp(250px, 27%, 330px);
  gap:var(--gap);
  /* stretch, not start: the button column should run the full height of the
     stage. Left at its natural height it ended a third of the way down and
     left a dead void beside the visual — the old bento filled that column
     with cards, so the eye expects it filled. */
  align-items:stretch;
}

.cx-stage{
  display:grid;                      /* single-cell stack, see above */
  background:var(--ink);
  border-radius:var(--radius-lg);
  padding:clamp(20px,2.4vw,34px);
  overflow:hidden;
}
.cx-panel{
  grid-area:1/1;                     /* every panel occupies the same cell */
  display:flex;
  flex-direction:column;
  gap:clamp(16px,1.8vw,22px);
  opacity:0; visibility:hidden;
  transition:opacity .38s var(--ease), visibility 0s linear .38s;
}
.cx-panel.is-on{ opacity:1; visibility:visible; transition:opacity .38s var(--ease); }
/* copy sits ABOVE the visual, as it did on the original featured card —
   the stage column is too narrow now to read well side-by-side. */
.cx-copy{ order:-1; }

.cx-copy .cov-label{ color:#F1D6C9; display:block; margin-bottom:10px; }
.cx-copy h3{ font-size:clamp(1.35rem,2.1vw,1.85rem); color:#fff; line-height:1.3; }
.cx-copy p{ font-size:14.5px; color:rgba(248,242,237,.74); margin-top:12px; line-height:1.75; }
.cx-copy .cov-tags{ margin-top:18px; }
.cx-copy .pill{ background:rgba(241,214,201,.14); color:#F1D6C9; }

/* the slideshow panel's media column — .rs-stage keeps its own styling */
.cx-media{ min-width:0; }
.cx-media .rs-stage{ margin-top:0; }
.cx-media .rs-nav{ display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }

/* the chip styling was scoped to `.cov-card.featured`, which no longer
   exists — re-scope it to the panel at the same specificity depth so the
   active state still wins. */
.cx-panel .rs-chip{
  position:relative; overflow:hidden;
  font-family:inherit; font-size:12px; font-weight:700; line-height:1.7;
  padding:6px 13px 8px; border:0; border-radius:999px; cursor:pointer;
  background:rgba(241,214,201,.14); color:#F1D6C9;
  transition:background .25s var(--ease), color .25s var(--ease);
}
.cx-panel .rs-chip:hover{ background:rgba(241,214,201,.28); color:#FFF; }
.cx-panel .rs-chip.is-on{ background:var(--red); color:#FFF; }
.cx-panel .rs-chip:focus-visible{ outline:2px solid #F1D6C9; outline-offset:2px; }

/* single-key-visual panels — same 16:10 window as the slideshow so the
   stage height doesn't shift between coverage 01 and the rest */
.cx-key{
  position:relative;
  aspect-ratio:16/10;
  border-radius:var(--radius-md);
  overflow:hidden;
  display:grid; place-items:center;
  background:
    radial-gradient(circle at 50% 58%,
      color-mix(in oklch, var(--red) 30%, transparent) 0%,
      color-mix(in oklch, var(--rose) 22%, transparent) 38%,
      transparent 70%),
    rgba(241,214,201,.06);
  box-shadow:inset 0 0 0 1px rgba(241,214,201,.12);
}
/* sized with max-* rather than a flat `height:%`: the box's height comes
   from aspect-ratio, and a percentage height resolved against it was
   computing far larger than the box, pushing the cut-out out of frame.
   max-height/max-width let the intrinsic ratio drive and simply cap it. */
.cx-key img{
  width:auto; height:auto;
  max-width:56%; max-height:64%;
  object-fit:contain;
  filter:saturate(.72) contrast(1.03) drop-shadow(0 18px 28px rgba(0,0,0,.34));
  transition:transform .5s var(--ease);
}
/* reuse the per-coverage tilt that the old cards had, so the objects
   don't read as one stamped set */
.cx-key.kv-liability img,
.cx-key.kv-liability-eh img{ transform:rotate(-4deg); }
.cx-key.kv-employee img,
.cx-key.kv-employee-eh img{ transform:rotate(2.5deg); }
.cx-key.kv-money img,
.cx-key.kv-money-eh img{ transform:rotate(-2deg); }
.cx-key.kv-interrupt-eh img{ transform:rotate(3deg); }

/* ---- the button row ---- */
.cx-nav{
  display:grid;
  /* one column, one row per coverage — the count differs per page (4 vs 5)
     and a plain stack handles both without a per-page rule. 1fr rows split
     the stage's height evenly, so 4 buttons and 5 buttons both fill it. */
  grid-auto-rows:1fr;
  gap:clamp(8px,1vw,12px);
}
.cx-btn{
  position:relative; overflow:hidden;
  display:flex; align-items:center; gap:clamp(12px,1.4vw,18px);
  padding:16px clamp(16px,1.6vw,20px);
  font-family:inherit; text-align:left; cursor:pointer;
  background:var(--surface);
  border:1px solid var(--line-strong);
  border-radius:var(--radius-md);
  transition:border-color .25s var(--ease), background .25s var(--ease), transform .25s var(--ease);
}
.cx-btn:hover{ transform:translateY(-2px); border-color:color-mix(in oklch, var(--red) 42%, var(--line-strong)); }
.cx-btn.is-on{ background:var(--red); border-color:var(--red); }
.cx-btn:focus-visible{ outline:2px solid var(--red); outline-offset:2px; }

/* --- the two background layers, both bottom-right, both behind the text ---
   ::before is the halftone corner already used elsewhere in the system
   (.mat-halftone), reused here so the buttons carry the same texture
   vocabulary as the rest of the page rather than a new invented one.
   ::after is the soft bloom that the old coverage cards put under their
   cut-out, so the object reads as lit by the card instead of pasted on. */
.cx-btn::before{
  content:''; position:absolute; right:0; bottom:0; width:58%; height:76%;
  background:var(--red); opacity:.12; z-index:0; pointer-events:none;
  -webkit-mask:url('assets/halftone-corner.png') bottom right / contain no-repeat;
          mask:url('assets/halftone-corner.png') bottom right / contain no-repeat;
}
.cx-btn::after{
  content:''; position:absolute; right:-8%; bottom:-34%; width:54%; aspect-ratio:1;
  border-radius:50%; z-index:0; pointer-events:none;
  background:radial-gradient(circle at 50% 50%,
    color-mix(in oklch, var(--rose) 46%, transparent) 0%,
    color-mix(in oklch, var(--rose) 18%, transparent) 46%,
    transparent 72%);
}
/* on the active red ground the red halftone and rose bloom both vanish —
   swap both to white so the texture survives the state change */
.cx-btn.is-on::before{ background:#fff; opacity:.16; }
.cx-btn.is-on::after{
  background:radial-gradient(circle at 50% 50%,
    rgba(255,255,255,.30) 0%, rgba(255,255,255,.12) 46%, transparent 72%);
}

/* the cut-out snaps INTO the bottom-right corner and is cropped by the
   button's overflow, exactly as .cov-key did on the old cards */
.cx-btn-art{
  position:absolute; right:-6px; bottom:-10px;
  /* BOTH axes are capped. Sizing on height alone let a wide object
     (docs-key is 1408x768) grow to ~280px and run straight under the
     title; the tall cut-outs need the height cap, the wide ones the
     width cap, so the box constrains both and the image fits inside. */
  width:44%; height:82%; z-index:1; pointer-events:none;
  display:flex; align-items:flex-end; justify-content:flex-end;
}
.cx-btn-art img{
  display:block; width:auto; height:auto; max-width:100%; max-height:100%;
  filter:saturate(.72) contrast(1.03) drop-shadow(0 10px 16px rgba(34,27,23,.24));
  transition:transform .35s var(--ease);
}
.cx-btn:hover .cx-btn-art img{ transform:translateY(-3px) scale(1.04); }
/* the same per-coverage tilt the cards used, so the objects don't read
   as one stamped set */
.cx-btn-art.kv-liability img, .cx-btn-art.kv-liability-eh img{ transform:rotate(-5deg); }
.cx-btn-art.kv-employee  img, .cx-btn-art.kv-employee-eh  img{ transform:rotate(3deg); }
.cx-btn-art.kv-money     img, .cx-btn-art.kv-money-eh     img{ transform:rotate(-3deg); }
.cx-btn-art.kv-interrupt-eh img{ transform:rotate(4deg); }

/* --- the emphasised figure on the left --- */
.cx-btn-no{
  position:relative; z-index:2; flex:0 0 auto;
  font-family:var(--font-display);
  font-size:clamp(28px,2.6vw,38px); font-weight:900; line-height:.9;
  letter-spacing:-.02em;
  color:color-mix(in oklch, var(--red) 30%, var(--line-strong));
}
.cx-btn.is-on .cx-btn-no{ color:rgba(255,255,255,.86); }

/* text is capped so it never runs under the corner object */
.cx-btn-text{ position:relative; z-index:2; display:flex; flex-direction:column; gap:3px; min-width:0; max-width:56%; }
.cx-btn-num{
  font-family:var(--font-caption); font-style:italic;
  font-size:13px; color:var(--rose-deep);
}
.cx-btn-name{ font-size:15px; font-weight:700; color:var(--ink); white-space:nowrap; }
.cx-btn.is-on .cx-btn-num{ color:rgba(255,255,255,.78); }
.cx-btn.is-on .cx-btn-name{ color:#fff; }

@media (max-width: 900px){
  /* stack media over copy, and let the buttons wrap two-up rather than
     squeezing five tracks into a phone width */
  .cov-explorer{ grid-template-columns:1fr; }
  /* back to natural heights once the nav sits under the stage */
  .cx-nav{ grid-template-columns:repeat(2,1fr); grid-auto-rows:auto; }
}
@media (max-width: 460px){
  .cx-nav{ grid-template-columns:1fr; }
}
@media (prefers-reduced-motion: reduce){
  .cx-panel, .cx-btn, .cx-key img, .cx-btn-art img{ transition:none; }
  .cx-btn:hover{ transform:none; }
}

/* ============================================================
   rev62: the three trust points become their own banded block.

   They previously sat as bare text on the same white --surface as the
   coverage section above, separated only by a hairline and 8px — so they
   read as a stray footer of that section rather than as their own row.
   A rose-wash ground (the same tint the hero stat strip uses, so this is
   an existing surface rather than a new one) plus real internal padding
   separates them without introducing another colour to the palette.
   ============================================================ */
.trust-row{
  grid-column:1/13;
  display:grid;
  grid-auto-flow:column;
  grid-auto-columns:1fr;
  gap:0;
  margin-top:clamp(28px,3.4vw,48px);
  background:var(--rose-wash);
  border-radius:var(--radius-lg);
  padding:clamp(22px,2.4vw,32px) clamp(18px,2vw,30px);
}
.trust-item{
  display:flex; align-items:center; gap:14px;
  padding:2px clamp(16px,2vw,30px);
  border-left:1px solid color-mix(in oklch, var(--rose) 32%, transparent);
}
.trust-item:first-child{ border-left:none; padding-left:0; }
.trust-item:last-child{ padding-right:0; }
.trust-item svg{ flex-shrink:0; color:var(--red); }
.trust-item strong{ display:block; font-size:15px; line-height:1.35; }
.trust-item span{ display:block; margin-top:3px; font-size:13px; color:var(--ink-soft); line-height:1.5; }

@media (max-width: 760px){
  .trust-row{ grid-auto-flow:row; gap:0; }
  .trust-item{
    border-left:none; padding:16px 0;
    border-top:1px solid color-mix(in oklch, var(--rose) 32%, transparent);
  }
  .trust-item:first-child{ border-top:none; padding-top:0; }
  .trust-item:last-child{ padding-bottom:0; }
}

/* ============================================================
   rev63: 七大保障範疇 — the seven blocks stop being a bento.

   Client read the old grid as "jumpy and playful", and fairly: FOUR things
   varied across seven cards and none of them meant anything.
     · two card anatomies (feature = horizontal, standard = vertical)
     · three tones (cream / ink / red) in no pattern
     · row heights swinging 117 → 235 → 85px
     · two label layers stacked on every photo
   The answer is not less variety but variety that CLASSIFIES. The content
   already splits 5 商業 / 2 個人 — the old corner tags were saying so while
   the layout ignored it. So: one card anatomy, one tone, and the only
   remaining size difference comes from the grid maths inside each group.
   Colour variety now comes from the photography, which already has plenty.
   ============================================================ */
.lob-group{ grid-column:1/13; }
.lob-group + .lob-group{ margin-top:clamp(38px,4.6vw,68px); }

.lob-group-head{ display:flex; align-items:center; gap:18px; margin-bottom:clamp(16px,2vw,26px); }
.lob-group-head h3{
  font-family:var(--font-display); font-size:clamp(1.05rem,1.5vw,1.3rem);
  color:var(--heading); white-space:nowrap; line-height:1.2;
}
/* the rule runs out to the grid edge so the group reads as a band */
.lob-group-head::after{ content:''; flex:1; height:1px; background:var(--line-strong); }

/* 6 columns: 5 cards fall 3-up then 2-up, 2 cards sit half-width. Both
   groups therefore share a column edge, which is what keeps the rhythm
   reading as intentional rather than as a ragged last row. */
.lob-grid{ display:grid; grid-template-columns:repeat(6,1fr); gap:var(--gap); }
.lob-grid.is-5 > .lob-card:nth-child(-n+3){ grid-column:span 2; }
.lob-grid.is-5 > .lob-card:nth-child(n+4){ grid-column:span 3; }
.lob-grid.is-2 > .lob-card{ grid-column:span 3; }

/* one anatomy, one tone — these override the retired .feature / .tone-* rules
   for good, since those class names are gone from the markup */
.lob-card{ flex-direction:column; background:var(--surface); }
.lob-media{ aspect-ratio:16/10; flex:none; align-self:auto; }
/* no copy sits on the photo any more, so the darkening scrim only muddied it */
.lob-media::after{ background:none; }

.lob-content{ padding:20px 22px 24px; gap:9px; }
.lob-cat{
  font-family:var(--font-caption); font-style:italic; font-size:13px;
  color:var(--rose-deep); line-height:1.2;
}
.lob-content h4{ font-size:1.12rem; color:var(--heading); line-height:1.4; }
.lob-content p{ font-size:13.5px; color:var(--ink-soft); line-height:1.78; }

@media (max-width:1080px){
  .lob-grid.is-5 > .lob-card:nth-child(-n+3),
  .lob-grid.is-5 > .lob-card:nth-child(n+4),
  .lob-grid.is-2 > .lob-card{ grid-column:span 3; }
}
@media (max-width:640px){
  .lob-grid.is-5 > .lob-card:nth-child(-n+3),
  .lob-grid.is-5 > .lob-card:nth-child(n+4),
  .lob-grid.is-2 > .lob-card{ grid-column:span 6; }
}

/* ---- rev65: the four 核心優勢 cards are keyed to their ARTWORK, not to
   their position. Every per-card rule above tunes one specific cut-out
   (its tile size, its corner-shape geometry); with :nth-child those
   tunings belonged to a slot, so the client's re-ordering of the cards
   would have silently mis-sized all four icons. Class-keyed, the card
   carries its own tuning wherever it sits. ---- */

/* ============================================================
   rev64: hero stat figures scale with the viewport.

   The client-approved copy lengthened one figure from 「1 天」 to
   「1 個工作天」. At a fixed 1.5rem that no longer fitted its third of
   the strip below ~1100px — it wrapped to two lines while the other two
   stayed on one, so the row went visibly uneven. Scaling the figure and
   forbidding the wrap keeps all three on a single baseline; the floor is
   set low enough for the longest approved string (「1 個工作天」) at the
   narrowest width the three-up strip is used.
   ============================================================ */
/* rev69 — nowrap 拎走。rev64 禁止斷行係為咗令三格企喺同一條基線，但嗰陣三格
   闊度唔等（flex 項目嘅 min-width:auto 令最長嗰格霸多咗位），唯有靠禁斷行硬撐。
   而家 .hp-stat 加咗 min-width:0，三格真等闊，rev64 想要嘅齊整由 clamp 字級
   自己做到 —— 繁中／简中喺 1440 / 1100 / 980 / 700px 全部量到一行。
   相反，等格之後 nowrap 由「剪走」變成「疊字」：英文「Transparent Premiums」
   一行要 240px，格得 202px，字直接壓落隔籬格個「4」上面。所以要准斷行。
   （下面 rev66 嗰條 ≤620px 嘅 white-space:normal 而家變咗同值，留住無害。） */
.hp-stat strong{ font-size:clamp(1.15rem, 1.75vw, 1.5rem); }
/* rev66 — the 客戶第五輪 copy turned the first 餐飲 stat from a figure（平均 15%+）
   into a phrase（保費一目了然）, and a phrase of six full-width characters is about
   38px wider. The cells are flex items that will not shrink below their content
   and the strip is overflow:hidden, so on a 360px phone the third stat lost its
   right edge — measured 29px over at 360, 53px at 336. Wrapping is allowed at the
   narrow tier only: the strip grows 118px → 149px there, which is the right trade
   against clipping copy the client just approved. */
@media (max-width: 620px){ .hp-stat strong{ white-space:normal; } }

/* ============================================================
   rev66: no orphaned glyph in the solution-card taglines.

   「公眾責任 · 建築物保障 · 度身訂造方案」 overran its 231px box by about
   two pixels, and because CJK breaks between ANY two characters by
   default, the browser dropped the single trailing 案 onto its own line.
   `word-break:keep-all` makes CJK runs unbreakable, so these taglines can
   only break at the spaces around the 「·」 separators — the line splits
   between phrases instead of mid-phrase, at any card width. Scoped to the
   three solution-card taglines; it would be wrong on long body prose,
   which needs to break freely.
   ============================================================ */
.solutions-grid .ind-body p,
.solutions-grid .cutout-cell-body p{ word-break:keep-all; }

/* ============================================================
   rev67: 關於我們 核心成員 card becomes a person card.

   The sheet moves 梁榮洸 / 執行董事 up into the card's headline slot and
   deletes the duplicate sign-off line beneath. The name keeps the display
   face at full size; the role can't sit in the same <strong> — set at
   1.5rem it wrapped and competed with the name — so it drops to its own
   caption-styled line. The signature image stays (S2-08 is Approved).
   ============================================================ */
.about-fact .fact-role{
  display:block; margin-top:5px;
  font-family:var(--font-caption); font-style:italic;
  font-size:13.5px; color:var(--rose-deep); line-height:1.4;
}

/* ============================================================
   rev68: no orphaned last lines anywhere.

   A render audit across all eight pages flagged two places where a wrapped
   line ended with a single glyph — the 安老院舍 testimonial attribution
   (「…總幹事」) and the 聯絡我們 office address (「…06 室」), both at ~1024px
   where their columns are narrowest. CJK offers a break opportunity
   between almost any two characters, so a box only a few pixels too narrow
   strands one character on its own line.

   `text-wrap:pretty` asks the engine to avoid short final lines and is
   inherited, so one declaration covers every text block including ones
   added later. Unlike `word-break:keep-all` (used on the solution-card
   taglines, where the phrases must stay intact) this only changes which
   break opportunity is chosen — it can never cause overflow.
   ============================================================ */
body{ text-wrap:pretty; }

/* ============================================================
   rev69: 英文版嘅手寫簽名板。

   繁中／简中 用 `sign-xinyin.png`（信言保險，1540×415，約 3.7:1）；英文版
   改用 `sign-asiagate.png`（1540×749，約 2.1:1）—— 拉丁草書有上下伸展同一條
   收筆長撇，字身以外佔嘅位多好多，所以同一個 width 落去會高成一倍。

   .card-signature 係按 width 落尺寸嘅，所以英文嗰塊收窄啲，令兩個語言嘅「字身」
   高度睇落一致；top 亦都推高少少，等「g」條腳唔會跌落卡片個名度。
   ============================================================ */
.card-signature.en{
  width:min(30%,88px);
  top:-38px;
}
