/* about.css - the 關於我們 page replica, scoped under .ab.
   Ported from the owner-approved draft docs/design/wireframe-about-v2.html (CSS lines
   973-1390, draft 4e / artifact v11). Shared blocks are NOT restyled here except for the
   page-scoped rules at the end of this file, which only ever load on /about/. */

/* ===== this page's own aldena replica, scoped under .ab ===== */

/* ============================================================
   關於我們 draft 2: literal aldena replica.
   Tokens/sections: docs/design/about-page-spec-v2-replica.md
   Copy: docs/design/about-page-spec-v1.md
   Geometry/motion: docs/research/2026-09-18-aldena-decode/
   One visual world: no dark-theme overrides, every colour painted.
   ============================================================ */
.ab{
  --green:#0B2E24;          /* aldena black sections */
  --green-deep:#061412;     /* scrim base */
  --paper:#F2F3EE;          /* aldena white sections */
  --sand:#E9ECE4;           /* aldena cream (services, footer) */
  --on-dark:#F2F3EE;
  --on-dark-mute:rgba(242,243,238,.62);
  --on-dark-dim:rgba(242,243,238,.34);
  --ink:#10201B;
  --mute:rgba(16,32,27,.68);
  --hair-dark:rgba(242,243,238,.18);
  --hair-light:rgba(16,32,27,.14);
  --amber:#E9A93A;
  --serif:"Noto Serif HK","Songti TC","PMingLiU",serif;
  --sans:"Noto Sans HK","PingFang HK","Microsoft JhengHei",system-ui,sans-serif;
  --ground-dark:linear-gradient(155deg,#1B4A38 0%,#0B2E24 52%,#294A38 100%);
  --ground-light:linear-gradient(155deg,#DFE6D8 0%,#E9ECE4 52%,#D4DCC9 100%);
  --ease:cubic-bezier(.16,1,.3,1);
  --ease-io:cubic-bezier(.65,0,.35,1);
}

.ab .container{width:100%;max-width:1440px;margin:0 auto;padding:0 40px}
.ab .sec{padding:128px 0}
.ab .abdark{background:var(--green);color:var(--on-dark)}
.ab .ablight{background:var(--paper);color:var(--ink)}
.ab .cream{background:var(--sand);color:var(--ink)}

.ab .abeyebrow{font-size:12px;font-weight:700;letter-spacing:.14em;line-height:1.4;text-transform:uppercase}
.ab .abdark .abeyebrow{color:var(--on-dark-mute)}
.ab .ablight .abeyebrow, .ab .cream .abeyebrow{color:rgba(16,32,27,.5)}
.ab .h2{font-family:var(--serif);font-weight:500;font-size:clamp(40px,5vw,72px);line-height:1.12;letter-spacing:-.02em}
.ab .abdark .lead{color:var(--on-dark-mute)}
.ab .ablight .lead, .ab .cream .lead{color:var(--mute)}
.ab .amberword{color:var(--amber)}

.ab .abpill{display:inline-flex;align-items:center;gap:8px;border-radius:40px;padding:14px 26px;font-size:14px;font-weight:700;letter-spacing:.04em;line-height:1}
.ab .pill-light{background:var(--paper);color:var(--ink)}
.ab .pill-dark{background:var(--ink);color:var(--paper)}
.ab .pill-amber{background:var(--amber);color:#1B1206}
.ab .pill-ghost{border:1px solid var(--hair-dark);color:var(--on-dark)}

/* image holders: tinted ground behind every picture, art survives a missing file */
.ab .ph{position:relative;overflow:hidden;background:var(--ground-light)}
.ab .ph-dark{background:var(--ground-dark)}
.ab .ph>img{width:100%;height:100%;object-fit:cover}
.ab .img-fail{visibility:hidden}

/* ---------- chunk reveal (heading + statement) ---------- */
.ab .ck{display:inline-block}
html.js .ab .rv .ck{opacity:.12;transform:translateY(20px);transition:opacity 1s var(--ease),transform 1s var(--ease),color 1s var(--ease)}
html.js .ab .rv.in .ck{opacity:1;transform:none}
.ab .rv-2 .ck{color:var(--on-dark-mute)}
.ab .rv-2.in .ck{color:var(--on-dark)}
.ab .ck.lit{color:var(--on-dark);opacity:1;transform:none}

/* ---------- 2 · HERO ---------- */
.ab .abhero{position:relative;height:100svh;min-height:620px;background:var(--green);color:var(--on-dark);overflow:clip}
.ab .heroshots{position:absolute;left:0;right:0;top:0;bottom:0;background:var(--ground-dark)}
.ab .heroshot{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:50% 50%;opacity:0;transition:opacity 1.2s linear}
/* ROUND 4 · row 1 - per-slide crop, measured off the face box of each source file.
   The Y value only bites where the viewport is wider than 16/9 (2000x1100, 2560x1300):
   there the frame has vertical slack, and a LARGER Y shows more of the picture's lower
   half, which lifts the subject away from the bottom caption band. */
.ab #hs0{object-position:62% 80%}
.ab #hs1{object-position:44% 64%}
.ab #hs2{object-position:50% 60%}
.ab #hs3{object-position:50% 66%}
.ab .heroshot.on{opacity:1}
html.js .ab .heroshot.on{animation:drift 7.2s linear forwards}
@keyframes drift{from{transform:scale(1)}to{transform:scale(1.05)}}
.ab .heroscrim{position:absolute;inset:0;background:
  linear-gradient(to bottom,rgba(6,20,18,.55) 0%,rgba(6,20,18,0) 40%),
  linear-gradient(to top,rgba(6,20,18,.7) 0%,rgba(6,20,18,0) 40%)}
.ab .heroin{position:absolute;inset:0;display:flex;flex-direction:column}
/* ROUND 4 · row 1 - the H1 used to start 3px ABOVE the nav pill's bottom edge (measured
   81 vs 84 at 1440x900). It now clears the pill by a real gap at every height. */
.ab .herotop{padding-top:clamp(124px,14svh,210px)}
.ab .abheroh1{font-family:var(--serif);font-weight:500;font-size:clamp(44px,9.2vw,132px);line-height:1.04;letter-spacing:.012em;color:#fff;white-space:nowrap;text-align:center}
html.js .ab .abheroh1{opacity:0;transform:translateY(242px)}
html.js .ab .abheroh1.in{opacity:1;transform:none;transition:opacity 1.4s var(--ease),transform 1.4s var(--ease)}
.ab .herofoot{margin-top:auto;padding-bottom:4svh;display:flex;align-items:flex-end;justify-content:space-between;gap:32px}
.ab .heroleft{margin-bottom:3svh}
.ab .herosub{font-size:12px;font-weight:700;letter-spacing:.16em;color:rgba(255,255,255,.78);text-transform:uppercase}
.ab .aberotag{font-family:var(--serif);font-weight:500;font-size:clamp(20px,2.3vw,26px);line-height:1.34;color:#fff;margin-top:10px}
.ab .aberotag+.aberotag{margin-top:0}

.ab .herocard{width:330px;flex:0 0 330px;background:rgba(6,20,18,.58);padding:14px 18px 18px;color:#fff}
.ab .hcbar{display:flex;gap:6px;margin-bottom:14px}
.ab .hcseg{flex:1;height:2px;background:rgba(255,255,255,.28);overflow:hidden}
.ab .hcseg b{display:block;height:100%;width:0;background:var(--amber)}
.ab .hcseg.done b{width:100%}
html.js .ab .hcseg.on b{animation:fill 6s linear forwards}
@keyframes fill{from{width:0}to{width:100%}}
.ab .hcrow{display:flex;align-items:center;gap:14px;margin-bottom:12px}
.ab .hcarrow{font-size:16px;line-height:1;color:rgba(255,255,255,.8);padding:2px 2px}
.ab .hcarrow:hover{color:#fff}
.ab .hccount{margin-left:auto;font-size:12px;font-weight:700;letter-spacing:.1em;color:rgba(255,255,255,.8);font-variant-numeric:tabular-nums}
.ab .hctitle{font-size:19px;font-weight:700;line-height:1.3}
.ab .hcdesc{font-size:14px;line-height:1.5;color:rgba(255,255,255,.8);margin-top:4px}
.ab .hcfade{transition:opacity .35s linear}
.ab .hcfade.out{opacity:0}

/* ---------- 3 · INTRO ---------- */
/* aldena home fold-01: picture block in the narrow LEFT column, statement in the wide right one */
/* LANE C · C2 - the homepage §2 geometry: .about{height:calc(var(--screen)*2)} with a
   .about-pin{position:sticky;top:0;height:var(--screen)} inner. Same numbers here, so the
   statement holds still for one screen of scroll while the words light up under the reader. */
.ab .intro{height:calc(var(--screen) * 2);overflow:clip}
.ab .intropin{position:sticky;top:0;height:var(--screen);display:flex;align-items:center;padding:var(--nav-h) 0 24px}
.ab .introgrid{display:grid;grid-template-columns:320px minmax(0,1fr);gap:120px;width:100%}
/* the homepage's .reveal-words, re-coloured for the dark ground: dim is the grey the
   statement already used, revealed is paper white, and the reading cursor keeps the
   homepage's travelling amber underline. */
.ab .revealwords .w{color:var(--on-dark-mute);transition:color .16s linear}
.ab .revealwords .w.revealed{color:var(--on-dark)}
.ab .revealwords .w.active{background-image:linear-gradient(var(--amber),var(--amber));background-position:0 100%;background-repeat:no-repeat;background-size:100% 2px}
.ab .intromain{display:flex;flex-direction:column}
.ab .intromark{display:flex;align-items:center;gap:10px;margin-bottom:40px}
.ab .intromark span.mk{width:22px;height:22px;border-radius:50%;background:var(--amber);flex:0 0 22px}
.ab .intromark span.wd{font-family:var(--serif);font-weight:500;font-size:18px}
.ab .statement{font-family:var(--serif);font-weight:500;font-size:clamp(28px,3.4vw,48px);line-height:1.35;letter-spacing:-.01em;max-width:22em}
.ab .introbtn{margin-top:48px}
.ab .introaside{display:flex;flex-direction:column;justify-content:flex-start;align-items:flex-start}
.ab .avatar{width:320px;height:320px;border-radius:50%;background:var(--ground-dark)}
.ab .avatar img{border-radius:50%}
.ab .introrole{margin-top:22px;font-size:14px;font-weight:700;color:var(--on-dark)}
.ab .introroleline{margin-top:8px;font-size:14px;line-height:1.65;color:var(--on-dark-mute)}

/* ---------- 4 · BELIEFS ---------- */
.ab .belief{position:relative;height:100svh;min-height:640px;overflow:clip;background:var(--green);color:var(--on-dark);display:flex;align-items:center}
/* PORT (probe-clipped-text, 2026-09-19): the draft wrote `inset:-6%` here plus `scale(1.08)` on
   the image. Both overhangs count toward section.belief's SCROLLABLE overflow even though
   `.belief{overflow:clip}` hides them from paint, so the section measured scrollWidth 1591 vs
   clientWidth 1440 at 1440 (431 vs 390 at 390) and the clipped-text gate read it as text clipped
   in a 4-level-up clipper. The box is now the section itself, clipping its own child, and the
   image carries the whole magnification: 1.12 (the -6% box) x 1.08 (the old image scale) =
   1.2096. The box kept the section's aspect ratio, so object-fit:cover crops identically and the
   rendered pixels inside the section are unchanged. */
.ab .beliefbg{position:absolute;inset:0;overflow:clip;background:var(--ground-dark)}
.ab .beliefbg img{width:100%;height:100%;object-fit:cover;filter:blur(24px) brightness(.62) saturate(.85);transform:scale(1.2096)}
.ab .beliefveil{position:absolute;inset:0;background:linear-gradient(to bottom,rgba(6,20,18,.34),rgba(6,20,18,.12) 42%,rgba(6,20,18,.62))}
.ab .ghostwrap{position:absolute;left:0;right:0;top:50%;transform:translateY(-50%);overflow:clip;pointer-events:none}
.ab .ghost{font-family:var(--serif);font-weight:500;font-size:min(26vw,374px);line-height:.9;letter-spacing:-.03em;color:rgba(242,243,238,.16);white-space:nowrap;text-align:center;will-change:transform}
.ab .beliefin{position:relative;width:100%;display:flex;flex-direction:column;align-items:center}
.ab .beliefshot{width:min(820px,calc(100vw - 200px));aspect-ratio:16/9}
/* aldena sets its project title / description / year as plain light text straight on the
   photo ground, aligned to the picture's own left and right edges. No card. */
.ab .beliefcard{width:min(820px,calc(100vw - 200px));color:var(--on-dark);padding:26px 0 0}
.ab .beliefhead{display:flex;align-items:baseline;gap:14px}
.ab .beliefnum{font-size:12px;font-weight:700;letter-spacing:.14em;color:rgba(242,243,238,.5)}
.ab .belieftitle{font-family:var(--serif);font-weight:500;font-size:30px;line-height:1.2;color:var(--on-dark)}
.ab .belieftag{margin-left:auto;font-size:13px;font-weight:700;letter-spacing:.08em;color:var(--on-dark);white-space:nowrap}
.ab .beliefbody{margin-top:10px;font-size:15px;line-height:1.7;color:rgba(242,243,238,.78);max-width:52ch}

/* ---------- 5 · SERVICES ---------- */
/* active card 1128x560: the width measured off aldena home-desktop/fold-05 (x156 to x1284) */
.ab .absvc{--cw:min(1128px,calc(100vw - 120px));padding:150px 0;position:relative;overflow:clip}
.ab .svcstage{position:relative}
.ab .svcview{overflow:hidden;width:100%}
.ab .svctrack{display:flex;gap:40px;padding-inline:calc((100% - var(--cw))/2);transition:transform .6s var(--ease-io);touch-action:pan-y;will-change:transform}
.ab .svctrack.drag{transition:none;user-select:none}
.ab .svccard{flex:0 0 var(--cw);width:var(--cw);height:560px;background:var(--paper);color:var(--ink);padding:32px;display:grid;grid-template-columns:minmax(0,1fr) 522px;gap:40px;opacity:.42;transition:opacity .6s var(--ease-io)}
.ab .svccard.on{opacity:1}
.ab .svcbody{display:flex;flex-direction:column;min-width:0}
.ab .svclabel{font-size:12px;font-weight:700;letter-spacing:.14em;color:rgba(16,32,27,.45)}
.ab .svctitle{font-family:var(--serif);font-weight:500;font-size:48px;line-height:1.14;letter-spacing:-.02em;margin-top:14px}
.ab .svctext{margin-top:22px;font-size:16px;line-height:1.7;color:var(--mute);max-width:46ch}
.ab .svcprice{margin-top:auto}
.ab .svcpricelab{font-size:12px;font-weight:700;letter-spacing:.14em;color:rgba(16,32,27,.45)}
.ab .svcpriceval{font-family:var(--serif);font-weight:500;font-size:40px;line-height:1.2;margin-top:6px}
.ab .svcshot{position:relative;background:var(--svc-ground,var(--ground-light))}
.ab .svcshot>img{object-fit:contain}
.ab .svcindex{position:absolute;top:18px;right:18px;font-size:12px;font-weight:700;letter-spacing:.12em;color:rgba(16,32,27,.45)}
/* ROUND 4e: `top:280px` is the same pixel `top:50%` resolved to while .svcstage held only
   the 560px card row; stating it outright keeps the desktop arrows exactly where they were
   now that the progress bar is also a child of the stage. */
.ab .svcarrow{position:absolute;top:280px;transform:translateY(-50%);width:56px;height:56px;border-radius:50%;background:var(--ink);color:var(--paper);display:flex;align-items:center;justify-content:center;font-size:18px;z-index:3}
.ab .svcprev{left:calc((100% - var(--cw))/2 - 68px)}
.ab .svcnext{right:calc((100% - var(--cw))/2 - 68px)}
.ab .svcfoot{margin-top:40px;text-align:center;font-size:15px;color:var(--mute)}
/* ROUND 4 · row 8 - autoplay progress, same visual language as the hero counter
   segments (.hcseg): one thin segment per card, the active one fills over the dwell. */
.ab .svcbar{display:flex;gap:6px;width:var(--cw);margin:34px auto 0}
.ab .svcarrow:focus-visible{outline:2px solid var(--amber);outline-offset:3px}
.ab .svcseg{flex:1;height:2px;background:rgba(16,32,27,.18);overflow:hidden}
/* ROW 1 (d): the fill is a compositor-only scaleX. Its `width` never changes - the old
   @keyframes animated width, which re-laid-out the bar on every frame. */
.ab .svcseg b{display:block;height:100%;width:100%;background:var(--ink);transform:scaleX(0);transform-origin:left center;will-change:transform}

/* ---------- 6 · PROCESS ---------- */
.ab .prc{padding:128px 0}
/* ROUND 4 · row 4: one line at every width ≥861px. 9 glyphs × 72px = 648px inside the
   1140px the 220px indent leaves at 1440 - the <br> was the only thing wrapping it. */
.ab .prch2{margin-left:220px;max-width:none;white-space:nowrap}
.ab .prclist{margin-top:110px}
.ab .prcrow{display:grid;grid-template-columns:220px minmax(0,1fr) 160px;gap:0 20px;padding:22px 0;border-top:1px solid var(--hair-dark);opacity:.45;transition:opacity .45s var(--ease)}
.ab .prcrow:last-child{border-bottom:1px solid var(--hair-dark)}
.ab .prcrow.open{opacity:1}
.ab .prcnum{font-size:13px;font-weight:700;letter-spacing:.1em;color:var(--on-dark-mute);padding-top:10px}
.ab .prcbtn{text-align:left;width:100%;font-family:var(--serif);font-weight:500;font-size:32px;line-height:1.2;color:inherit}
.ab .prctag{font-size:13px;font-weight:700;letter-spacing:.06em;text-align:right;padding-top:12px;color:var(--on-dark);white-space:nowrap}
.ab .prcpanel{display:grid;grid-template-rows:0fr;transition:grid-template-rows .45s var(--ease-io)}
.ab .prcrow.open .prcpanel{grid-template-rows:1fr}
.ab .prcpanelin{overflow:hidden}
.ab .prctext{padding-top:12px;font-size:15px;line-height:1.75;color:var(--on-dark-mute);max-width:58ch}

/* ---------- 7 · TICKER ---------- */
.ab .ticker{height:76px;display:flex;align-items:center;overflow:hidden;background:var(--green);color:var(--on-dark-dim)}
.ab .mqtrack{display:flex;white-space:nowrap;animation:mq 26s linear infinite}
.ab .ticker:hover .mqtrack{animation-play-state:paused}
@keyframes mq{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.ab .mqset{display:flex;flex:0 0 auto}
.ab .mqchip{display:flex;align-items:center;gap:12px;padding:0 44px;font-size:19px;font-weight:700;letter-spacing:.01em}
.ab .mqdot{width:9px;height:9px;border-radius:50%;background:rgba(242,243,238,.34);flex:0 0 9px}

/* ---------- 8 · CASES ---------- */
.ab .abcases{background:var(--green);color:var(--on-dark)}
/* LANE C · C1 - pin range = wrapper − viewport. aldena runs 3 stacked cards over a
   measured 750px range (README §4: section 1650 tall, viewport 900); two real cases here
   means one transition, so 700px of scrub keeps the same unhurried per-card rhythm.
   ROUND 4c · row 2: three cards again - back to aldena's measured 750px range.
   ROUND 4d: six cards = five transitions, ~250px of scroll each = 1250px range. */
.ab .caseswrap{height:calc(100svh + 1250px);position:relative}
.ab .casesframe{position:sticky;top:0;height:100svh;overflow:clip;display:flex;flex-direction:column}
.ab .casesstage{position:relative;flex:1;min-height:0}
/* C1: the incoming card rides up OVER the one beneath it. Without an edge the two paper
   grounds merge into one sheet and the travel is invisible, so each card casts a soft
   shadow upward - the same read as aldena's stacked testimonial cards. */
.ab .casecard{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:min(896px,calc(100vw - 200px));height:min(545px,72svh);background:var(--paper);color:var(--ink);padding:38px 40px;display:flex;flex-direction:column;will-change:transform;box-shadow:0 -20px 54px rgba(6,20,18,.5)}
.ab .caseq{font-size:34px;line-height:1.28;color:rgba(16,32,27,.28);font-family:var(--serif);font-weight:500}
/* row 2: the typical-situation card puts a plain label where the quote mark sits, on the
   same baseline, so the three cards still line up. No 「」 in its body. */
.ab .caselabel{font-size:12px;font-weight:700;letter-spacing:.14em;line-height:43.5px;color:rgba(16,32,27,.5)}
.ab .casequote{font-family:var(--serif);font-weight:500;font-size:clamp(22px,2.2vw,31px);line-height:1.42;letter-spacing:-.01em;margin-top:2px;max-width:24em}
.ab .casebottom{margin-top:auto;display:flex;align-items:flex-end;justify-content:space-between;gap:24px}
.ab .casename{font-family:var(--serif);font-weight:500;font-size:22px;line-height:1.3}
.ab .caserole{font-size:14px;color:var(--mute);margin-top:4px}
.ab .casebadge{display:inline-block;margin-left:8px;font-size:11px;font-weight:700;padding:2px 8px;border:1px solid var(--hair-light);border-radius:40px;color:var(--mute);vertical-align:middle}
.ab .caseshot{width:160px;height:120px;flex:0 0 160px}
/* en-route find, C1: this rule's shorthand `padding:0 0 34px` was overriding the
   .container's own `0 40px`, so the eyebrow and the tabs ran to the bare viewport edge. */
.ab .casesfoot{padding:0 40px 34px;display:flex;align-items:center;justify-content:space-between;gap:24px}
.ab .casesfoot .abeyebrow{color:var(--on-dark)}
.ab .casetabs{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}
.ab .casetab{font-size:12px;font-weight:700;letter-spacing:.06em;padding:9px 16px;border-radius:40px;border:1px solid var(--hair-dark);color:var(--on-dark-mute)}
.ab .casetab.on{background:var(--paper);color:var(--ink);border-color:var(--paper)}

/* ---------- 11 · BOOKING (row 7) ----------
   The booking block's own markup and CSS already match dist verbatim (.book padding
   0 24px 104px, .book-card padding 48px, .book-form padding 36px - all measured equal).
   The page-specific defect is the NEIGHBOUR: on the real site the order is
   numbers → faq → book, so a paper section always separates the dark stats band from
   the dark booking card. This page runs faq → numbers → book, which butted the
   full-bleed green stats band (square bottom corners, per .block-numbers) straight into
   the green booking card - one undivided dark mass with the white form floating in it.
   Give the stats band its card corners back and the paper gap the real site has. */
.block-numbers.numbers-card{border-radius:var(--r-sec);margin-bottom:var(--sec)}


/* ============================ PHONE ============================ */
@media (max-width:860px){
  .ab .container{padding:0 20px}
  .ab .sec{padding:72px 0}

  /* ROUND 4 · row 2 - 新活離婚事務所 stays on ONE line at 390 too. 7 glyphs at 11.4vw
     = 44.5px each = 311px inside the 350px content box; the letter-spacing spends the
     remaining room instead of leaving it dead at the two ends. */
  .ab .abheroh1{font-size:min(11.4vw,132px);white-space:nowrap;letter-spacing:.055em}
  .ab .abheroh1 .l2{display:inline}
  .ab .herotop{padding-top:clamp(104px,13svh,170px)}
  /* the photo occupies the top of the fold only, so the whole face sits above the
     caption band instead of behind it (row 1). */
  .ab .heroshots{bottom:25svh}
  .ab .heroscrim{background:linear-gradient(to bottom,rgba(6,20,18,.5) 0%,rgba(6,20,18,0) 34%),linear-gradient(to top,rgba(6,20,18,.92) 0%,rgba(6,20,18,.55) 26%,rgba(6,20,18,0) 46%)}
  .ab .heroshot{object-position:50% 34%}
  .ab #hs0, .ab #hs1, .ab #hs2, .ab #hs3{object-position:50% 34%}
  .ab .heroleft{margin-top:26px;margin-bottom:0}
  .ab .heroin{display:flex;flex-direction:column}
  .ab .herofoot{flex-direction:column;align-items:stretch;padding-bottom:3.4svh}
  .ab .herocard{width:100%;flex:0 0 auto;margin-top:auto}
  .ab .aberotag{font-size:24px}

  /* phone keeps the pin (the reveal must still scrub at 390) but a shorter one, the way
     the homepage shortens .about to 1.5 screens. */
  .ab .intro{height:calc(var(--screen) * 1.9)}
  .ab .intropin{padding:calc(var(--nav-h) - 24px) 0 20px;align-items:center}
  .ab .introgrid{grid-template-columns:1fr;gap:44px}
  /* ROUND 4 · row 3: the portrait is centred on phone, not pinned to the left rail. */
  .ab .introaside{align-items:center;text-align:center}
  .ab .introroleline{max-width:34em}
  .ab .avatar{width:220px;height:220px}
  .ab .introbtn{margin-top:32px}

  .ab .belief{height:auto;min-height:0;padding:64px 0;align-items:stretch}
  .ab .ghostwrap{top:14%}
  .ab .ghost{font-size:34vw}
  .ab .beliefshot, .ab .beliefcard{width:100%}
  .ab .beliefcard{padding:18px 0 0}
  .ab .belieftitle{font-size:24px}
  .ab .beliefhead{flex-wrap:wrap}
  .ab .belieftag{margin-left:0;width:100%}

  /* ROUND 4 · row 8 - phone card rebuilt: drawing on top and never cropped, then
     label, title, paragraph, price. One 22px padding all round. The card is narrow
     enough that the next one always peeks 42px, so it reads as a carousel. */
  .ab .absvc{--cw:calc(100vw - 76px);padding:72px 0}
  .ab .svctrack{gap:14px;padding-inline:20px}
  .ab .svccard{height:auto;display:flex;flex-direction:column;gap:18px;padding:22px}
  .ab .svctitle{font-size:28px;margin-top:10px}
  .ab .svctext{margin-top:14px;font-size:15px}
  .ab .svcshot{order:-1;width:100%;height:160px;aspect-ratio:auto;margin-left:0;flex:0 0 auto}
  .ab .svcshot>img{width:100%;height:100%;object-fit:contain;padding:10px}
  .ab .svcbody{flex:1 1 auto}
  .ab .svcprice{margin-top:18px}
  .ab .svcpriceval{font-size:28px}
  .ab .svcbottom{display:flex;align-items:flex-end;justify-content:space-between;gap:16px}
  /* ROUND 4e - the owner's row: the arrows were killed outright by `.ab .svcarrow{display:none}`
     in this phone block, so a phone had no way to move the carousel except a swipe. They come
     back as 48x48 tap targets in ONE row with the progress segments, inside the 20px gutters:
     segments take the free column, the two arrows sit on the right. */
  .ab .svcstage{display:grid;grid-template-columns:20px minmax(0,1fr) 48px 48px 20px;grid-template-areas:"v v v v v" ". bar prev next .";row-gap:24px;column-gap:10px;align-items:center}
  .ab .svcview{grid-area:v}
  .ab .svcbar{grid-area:bar;width:auto;margin:0}
  .ab .svcprev{grid-area:prev}
  .ab .svcnext{grid-area:next}
  .ab .svcarrow{display:flex;position:static;top:auto;left:auto;right:auto;transform:none;width:48px;height:48px;font-size:17px}
  .ab .svcfoot{margin-top:28px;text-align:left}

  .ab .prc{padding:72px 0}
  .ab .prch2{margin-left:0;white-space:normal}
  .ab .prclist{margin-top:52px}
  .ab .prcrow{grid-template-columns:56px minmax(0,1fr);gap:0 12px}
  .ab .prcbtn{font-size:26px}
  .ab .prctag{grid-column:2;text-align:left;padding-top:10px;font-size:12px}
  .ab .prcpanel{grid-column:2}

  .ab .mqchip{font-size:15px;padding:0 24px}

  .ab .caseswrap{height:auto}
  .ab .casesframe{position:static;height:auto;overflow:visible;padding:72px 0 0}
  /* ROW 3: three normal-flow cards, full width inside the 20px container gutters, no
     pin, no stacking shadow, wrapper height auto - nothing overlaps and nothing is
     off-screen. syncCasesLayout() strips the pin's inline transform so these win. */
  /* ================= ROUND 4d · PHONE = the HOMEPAGE folding cards =================
     Lifted from public/components/stack-cards.js + .css (the homepage process deck):
       · card box          - stack-cards.css:41-45  position:sticky;
                             top: calc(var(--nav-h) + 24px + var(--stack-i) * 16px);
                             min-height: var(--stack-card-vh)   [60vh on phone, :30]
       · dim               - stack-cards.css:51-60  an opaque ::after overlay driven by
                             --stack-dim, never the card's own opacity
       · the fold maths    - stack-cards.js:29-34   STACK_STEP = 0.04,
                             stackFold(i, arrivals) → scale 1 − Σ_{j>i} arrivals[j]·0.04,
                             dim = min(drop × 3, 0.4)
       · the arrival vector- stack-cards.js:65      arrivals[j] = clamp(stackProgress − (j−1)),
                             stackProgress = scrollProgress × n  (js:84, scrub over the root)
     The original needs GSAP ScrollTrigger; this page has none, so the same numbers are
     re-derived from scrollY in the page's single rAF handler. Identical behaviour, and
     the 16px per-card top offset is what makes the deck read as a folder.
     ============================================================================ */
  .ab .casesstage{display:block;padding-inline:20px}
  .ab .casecard{
    position:sticky;
    top:calc(var(--nav-h) + 24px + var(--stack-i,0) * 16px);
    left:auto;
    min-height:var(--stack-card-vh,60vh);
    width:100%;height:auto;
    padding:24px 22px 26px;
    box-shadow:0 -14px 34px rgba(6,20,18,.28);
    transform-origin:top center;
    backface-visibility:hidden;
    will-change:transform;
    --stack-dim:0;
  }
  .ab .casecard + .ab .casecard{margin-top:0}
  .ab .casecard::after{content:"";position:absolute;inset:0;z-index:1;background:#000;opacity:var(--stack-dim);pointer-events:none}
  .ab .casecard > *{position:relative;z-index:2}
  .ab .caselabel{line-height:1.4;margin-bottom:8px}
  .ab .casetabs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;padding-bottom:2px}
  .ab .casetabs::-webkit-scrollbar{display:none}
  .ab .casetab{flex:0 0 auto}
  .ab .casequote{font-size:21px;max-width:none}
  .ab .casebottom{margin-top:26px}
  .ab .caseshot{width:120px;height:92px;flex:0 0 120px}
  .ab .casesfoot{flex-direction:column;align-items:flex-start;gap:14px;padding:34px 0}
  .ab .casetabs{justify-content:flex-start}


}

/* ============================ REDUCED MOTION ============================ */
@media (prefers-reduced-motion:reduce){
  html.js .ab .rv .ck, html.js .ab .rv.in .ck{opacity:1;transform:none;transition:none}
  html.js .ab .abheroh1, html.js .ab .abheroh1.in{opacity:1;transform:none;transition:none}
  html.js .ab .heroshot.on{animation:none}
  html.js .ab .hcseg.on b{animation:none;width:100%}
  /* ROUND 4 · row 8: reduced motion means no autoplay and no filling bar. */
  html.js .ab .svcseg.on b{transform:scaleX(1)}
  .ab .heroshot{transition:none}
  .ab .hcfade{transition:none}
  .ab .mqtrack{animation:none}
  .ab .svctrack{transition:none}
.ab .prcpanel{transition:none}
  .ab .ghost{transform:none !important}
  /* nothing scrubs, so the pins would hide case 1 and 2 behind case 3 and leave
     100vh of dead scroll at the close. Fall back to the phone's static stack. */
  .ab .caseswrap{height:auto}
  /* C2: no pin and every word already revealed under reduced motion. */
  .ab .intro{height:auto}
  .ab .intropin{position:static;height:auto;padding:96px 0}
  .ab .revealwords .w{color:var(--on-dark);transition:none;background:none}
  .ab .casesframe{position:static;height:auto;overflow:visible;padding:72px 0 0}
  /* 4d: reduced motion is a plain stacked list - no sticky, no fold, no dim. */
  .ab .casesstage{display:flex;flex-direction:column;align-items:center;gap:20px;padding-inline:20px}
  .ab .casecard{position:relative !important;top:auto !important;min-height:0 !important;transform:none !important;box-shadow:none}
  .ab .casecard::after{opacity:0 !important}
  .ab .casecard{position:relative;left:auto;top:auto;transform:none !important;height:auto;min-height:0}
}

/* ---------- page-scoped rules that touch shared markup (this file loads only on /about/) ---------- */
.ab{display:block}
/* site.css gives every .dark a 40px radius; the replica sections paint their own ground. */
.ab .abdark{border-radius:0}
/* site.css:146 makes the solid nav dark green (rgb(8,57,52)), not paper, so the one white
   tint reads in both the transparent and the solid nav state. */
.nav .links .link[aria-current="page"]{background:rgba(255,255,255,.12);font-weight:600}
/* faq-person-b.webp carries a flat ground measured at rgb(231,228,220); the section takes
   rgb(232,228,221) through the Faq.astro ground prop and the image box matches it. */
.faq .faq-person img{background:rgb(232,228,221)}
.block-numbers .paint video{background:transparent}
