/* ==========================================================================
   Uniweb Pay — rebrand style layer.

   Single source of truth, loaded by ALL three pages (/, /about/, /solutions/).
   It used to be an inline <style id="uniweb-trim"> copied into each HTML, which
   drifted apart; keep it here so a change lands on every page at once.

   Every rule is self-scoping: it targets an id/class that exists only on the
   page(s) it applies to, so one file can safely serve all three. Page-specific
   rules are grouped at the bottom and labelled with the page they hit.

   Loaded last in <head>, so it wins ties against the template stylesheet
   (/_astro/uw-styles.1F171F48.css).
   ========================================================================== */


/* --- Poppins (wordmark) --------------------------------------------------- */

/* Self-hosted. This used to be three requests to two third-party origins on
   EVERY page — preconnect to fonts.googleapis.com, preconnect to
   fonts.gstatic.com, then a RENDER-BLOCKING stylesheet from googleapis that
   itself has to be parsed before the woff2 is even discovered. Two extra
   DNS+TLS handshakes and a serialised round trip, for one weight used by one
   element (the header wordmark, plus the preloader name's fallback).

   The file is the same latin subset Google was serving (Poppins 500, 7.7 KB,
   SIL OFL). Same-origin, preloaded from <head>, one request, no handshake.

   `swap` rather than the template's `block`: worst case the wordmark shows in
   the fallback for a moment instead of being invisible. */
@font-face{
  font-family:'Poppins';
  font-style:normal;
  font-weight:500;
  font-display:swap;
  src:url('/assets/fonts/Poppins-Medium.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}


/* --- Chinese: a deterministic system face, and no webfont ------------------

   NO CJK WEBFONT, deliberately. A usable Simplified Chinese subset is 1–3 MB
   even aggressively cut — more than every byte the recent performance work
   saved (#68 and #69 together took the homepage from 11.4 MB to 8.57 MB and
   phones from 974 KB of card texture to 463 KB). Every reader of these pages
   already has a good Chinese face installed; the job is to name it rather than
   to ship one.

   Until now nothing named it. UniwebSans and IBMPlexMono have no CJK glyphs
   and are declared with no fallback family, so a Chinese page rendered in
   whatever the browser happened to reach for — Songti on one Mac, SimSun on
   one Windows box, a serif on one and a sans on the next. That is not a font
   choice, it is an absence of one.

   THE MECHANISM IS unicode-range, NOT A font-family OVERRIDE, and the reason
   is specificity. The template sets `font-family` by id and by class on
   essentially every text element; a `:lang(zh-Hans){font-family:…}` rule loses
   every one of those ties, and winning them would mean restating the whole
   template's typography. Extending the EXISTING families instead sidesteps it:
   these faces carry the same family names, they are declared later than the
   template's (uniweb.css is linked last), and they claim only the CJK
   codepoints — so for a Chinese character the browser picks this face, and for
   a Latin one the template's woff2 still wins on the same element.

   It also survives the one SPA defect we cannot patch out: the router never
   updates <html lang> after an in-app hop, so a rule keyed on :lang() would
   stop applying mid-session. A codepoint cannot go stale.

   The stack is macOS/iOS first, then Windows, then the Noto/Source Han family
   that covers Linux and Android. All local() — this downloads nothing. */
@font-face{
  font-family:UniwebSans;
  font-style:normal;
  font-weight:100 900;
  src:local('PingFang SC'),local('Hiragino Sans GB'),local('Microsoft YaHei UI'),local('Microsoft YaHei'),
      local('Noto Sans CJK SC'),local('Noto Sans SC'),local('Source Han Sans SC'),local('Heiti SC'),
      local('Droid Sans Fallback');
  unicode-range:U+2E80-2EFF,U+3000-303F,U+31C0-31EF,U+3200-9FFF,U+F900-FAFF,U+FE30-FE4F,U+FF00-FFEF;
}
@font-face{
  font-family:IBMPlexMono;
  font-style:normal;
  font-weight:100 900;
  src:local('PingFang SC'),local('Hiragino Sans GB'),local('Microsoft YaHei UI'),local('Microsoft YaHei'),
      local('Noto Sans CJK SC'),local('Noto Sans SC'),local('Source Han Sans SC'),local('Heiti SC'),
      local('Droid Sans Fallback');
  unicode-range:U+2E80-2EFF,U+3000-303F,U+31C0-31EF,U+3200-9FFF,U+F900-FAFF,U+FE30-FE4F,U+FF00-FFEF;
}
@font-face{
  font-family:UniwebMono;
  font-style:normal;
  font-weight:100 900;
  src:local('PingFang SC'),local('Hiragino Sans GB'),local('Microsoft YaHei UI'),local('Microsoft YaHei'),
      local('Noto Sans CJK SC'),local('Noto Sans SC'),local('Source Han Sans SC'),local('Heiti SC'),
      local('Droid Sans Fallback');
  unicode-range:U+2E80-2EFF,U+3000-303F,U+31C0-31EF,U+3200-9FFF,U+F900-FAFF,U+FE30-FE4F,U+FF00-FFEF;
}

/* NO `:lang(zh-Hans){font-family:…}` RULE, and this one is worth a warning
   rather than a shrug: it was written, measured and taken back out. The
   template sets font-family on some elements and lets others INHERIT from the
   root — #about-who-title-left, the About page's h1, is one of the inheriting
   ones. A root-level :lang rule therefore wins on exactly those elements, and
   the result is "UNIWEB", "Sunmi P3" and "Dynamic PayNow" rendering in PingFang
   SC on the Chinese pages while their neighbours stay in the display face.
   Measured at 1440x900: the About h1 computed to the CJK stack outright. The
   unicode-range faces above already do the whole job, per character, with the
   Latin still coming from UniwebSans.

   What :lang IS used for is the size and tracking below. The template's small
   labels are set for tracked-out uppercase Latin — `.62em` with
   `letter-spacing:.16em`. At .62em a CJK glyph is about 10px, which is under
   the size Chinese stops being readable at, and .16em of tracking on
   characters that are already full-width reads as broken spacing rather than
   as emphasis. text-transform:uppercase is a no-op on CJK and is left alone.
   These rules only have to hold on a direct load, which is what they get. */
:lang(zh-Hans) #footer-enquires-header,:lang(zh-Hans) #footer-business-header,
:lang(zh-Hans) #footer-inquiry-header,:lang(zh-Hans) #uw-footer-phone-header,
:lang(zh-Hans) #uw-footer-trust .uw-ft-h,:lang(zh-Hans) .uw-food-name,
:lang(zh-Hans) .uniweb-paychannels-label,:lang(zh-Hans) .project-details-side-list-title,
:lang(zh-Hans) .uniweb-load__cap{
  font-size:.78em;
  letter-spacing:.06em;
}
:lang(zh-Hans) .uniweb-paychannels-label{font-size:13px;}
:lang(zh-Hans) .uniweb-load__cap{font-size:11px;}


/* --- Chinese display type: the boxes are sized for Latin capitals ----------

   Naming the CJK face (above) fixed *which* glyphs get drawn. This block fixes
   *where they fit*, which is a separate problem and the one a reader actually
   notices: on the Chinese pages the big type was being sliced flat along the
   top, the two headline lines touched, a ghost of a third line hung below, and
   the decorative bar ran straight through the glyphs.

   All four are one root cause, and it is worth writing the measurement down
   because everything below is derived from it. Canvas TextMetrics, at 100px in
   the display face, on the strings this site actually sets:

     string                              ascent  descent   ink
     ALL WAYS ALWAYS PAID (Latin caps)    .707     .006    .713   <- what the
     By the book, in your bank. (mixed)   .707     .200    .907      template
     合规经营钱进账户，。 (CJK)            .823     .161    .984      was tuned
     全部方案…持牌支付服务机构 (CJK)        .826     .108    .934      for
     ，。 (full-width punctuation alone)   .142     .161    .303

   So CJK asks for .984em of ink where the template budgeted .713em — .27em
   more — and unlike Latin capitals it has a real descender, which is what the
   full-width comma and period carry (.161em on their own).

   The second measured fact is where the baseline sits. UniwebSans reports
   fontBoundingBoxAscent 1.0em and descent .2em, so its content area is 1.2em
   and the half-leading in a line box of height L is (L - 1.2)/2. The baseline
   therefore lands at

       L/2 + .4  em below the top of the line box

   — .9em when L is 1, which is what `line-height:1` gives the whole display
   set. Verified: 129.5px measured in a 144px box. Every number below is that
   formula plus the table above; none of it is taste.

   Two consequences fall straight out, and they are the whole bug:

   - In a 1em box the CJK ink runs from .077em to 1.061em. It **overflows the
     bottom of its own line box** by .061em, while leaving .077em of dead air
     at the top. Latin capitals sit at .193em-.906em in the same box, which is
     why nobody upstream ever saw this.
   - Boxes that hold two stacked copies of a character for a roll-up effect
     only hide the second copy because Latin caps are short. The copies are one
     row apart, so the clip box has `row pitch - ink height` to play with:
     .287em for Latin caps, and **.016em** for CJK. That is not a margin, it is
     a coincidence, and on CJK it fails.

   Scoped `:lang(zh-Hans)` throughout, so the English pages are untouched —
   verified by diffing the rendered geometry, not by eye. Unlike `font-family`
   (see the warning above) `:lang()` is safe here: every crossing between the
   two language trees is a full page load, so the attribute can only go stale
   *within* one language, where it is still correct.                         */


/* THE END-SECTION HEADLINE — the worst of it, and the one with a clone.

   The engine splits the headline into `.end-section-title-link-line` >
   `.end-section-title-link-word` > `.char-wrapper` > two identical `.char`
   copies. `.char-wrapper` rolls up by `-100%` of its own height every couple
   of seconds on one random character, and the clone lands where the original
   was; the entry reveal separately slides each `.char` up by 100% of its own
   height. So THREE numbers have to stay equal or the roll misses its mark:
   the row pitch (`.char` line-height), the wrapper height (what -100% resolves
   against, `var(--font-size)` in the template) and the reveal travel. The
   template sets all three to 1em independently; this sets all three to 1.3em
   independently, which is why the wrapper height is restated here.

   Measured at 1440x900 on /zh/about/ (144px display size), before -> after,
   in em of that size, relative to the top of the clip window:

     ink of the resting copy   [-.123, .861]  ->  [.127, 1.111]
     clip window height              .95em    ->      1.25em
     cut off the top of every glyph  .123em (17.6px)  ->  none, .127em clear
     clone showing below             .073em (10.4px)  ->  none, .177em clear
     gap between the two lines      -.004em (they overlapped)  ->  .196em
     decoration bar vs the glyphs   through both lines  ->  .059em under line 1

   English, for comparison, is unchanged: ink [-.007, .706] in the .95em
   window (the template shaves 1px off the cap tops on purpose), clone starting
   .043em below it, .267em between the lines.

   1.3em for the row pitch rather than the 1.111em the ink strictly needs: the
   clone has to clear the window too, and the slack then splits three ways
   (.127 / .139 / .177em) instead of being spent entirely on one edge. It also
   leaves room for a CJK face with taller ink than PingFang SC — ascent up to
   .95em and descent up to .32em still fit, against the .823/.161 measured
   here, which matters because the face comes from whatever the reader has
   installed.                                                                */
:lang(zh-Hans) #end-section-title-link .char{line-height:1.3;}
:lang(zh-Hans) #end-section-title-link .char-wrapper{height:1.3em;top:-.1em;}
:lang(zh-Hans) #end-section-title-link .end-section-title-link-word{height:1.25em;}
:lang(zh-Hans) #end-section-title-link .end-section-title-link-line{height:1.2em;}

/* The bars are positioned against their own line, and the template puts them
   where Latin capitals stop: `top:.85em` for the first and `bottom:.1em` for
   the last, which on CJK is .06em INSIDE the glyphs — the reported "bar
   through 营 and 户". Give both the same offset from their line's top instead,
   centred in the .196em the new leading opens up: .059em below the ink above
   it, .067em above the ink below it. `bottom:auto` because the second bar is
   positioned off the bottom edge and both ends must not resolve at once.
   uniweb.js section 4 still measures and sets their `left`/`width` per line;
   only the vertical position is decided here.                               */
:lang(zh-Hans) #end-section-title-top-decoration,
:lang(zh-Hans) #end-section-title-bottom-right-decoration{top:1.17em;bottom:auto;}

/* THE SAME HEADLINE'S WIDTH — a separate bug from its height, and the one a
   phone shows first.

   Below 560px the template swaps this title from `width:fit-content` to
   `width:100%` and takes the type up to 19.5vw, because at phone width it
   wants the headline edge to edge. That is a Latin measurement. A Latin line
   is only as wide as the glyphs in it and the browser rewraps it at the
   spaces, so at 390x844 our English lines come out 232-317px wide and stand
   37-114px off both screen edges — 19.5vw is chosen against the longest
   WORD, not the longest line.

   A CJK line has no spaces to break at and every full-width character is
   exactly one em, so the line is N x --font-size and nothing else:

     5 characters at 19.5vw = 97.5vw -> 4.84px off each edge at 390
     6 characters at 19.5vw = 117vw  -> 33px past each edge

   The mobile branch therefore holds floor(100/19.5) = 5 characters a line,
   /zh/ and /zh/about/ have five, and /zh/solutions/ has six (每一种方式，).
   The overflow does not bleed off-screen either, which is why it reads as a
   copy bug rather than a layout one: `.end-section-title-link-word` is
   `overflow:hidden`, so the browser pushes the sixth character onto a second
   row inside the clip window and the window swallows it. The rendered
   headline was 每一种方式 with its comma gone and five glyphs touching both
   bezels. (The nine /zh/solutions/<id>/ pages carry the same markup but their
   #end-section parent is display:none and the engine never splits the title,
   so this was one page, not ten.)

   15vw states the rule in the arithmetic instead of leaving it in someone's
   head: six characters are 90vw, which clears 5vw a side at every width in
   the branch — 19.5px at 390, past the 15px --base-padding-x the rest of that
   page indents to, and 28px at 560. It is a size, not a guarantee: a seventh
   character would be 105vw and would break this again.

   What it costs is the two five-character shells, which come down from
   380.25px to 292.5px wide at 390. That is not a loss — 292.5px sits inside
   the 232-317px the three English headlines already occupy at the same
   viewport, so the Chinese stops being the one headline on the site with no
   margin. Measured at 390x844, ink from the left edge:

     /zh/           4.84px -> 48.75px      /zh/about/     4.84px -> 48.75px
     /zh/solutions/  line 1 wrapped and clipped -> 19.5px, whole and unwrapped

   Nothing else reads --font-size: the template spends it on this title's
   font-size, on `.end-section-title-link-line`'s and `.char-wrapper`'s
   heights (both restated in em above, so they scale with it) and on
   #home-reel-video-title, which declares its own. The vertical clearances
   from the block above scale with the type and stay positive — .127em/.139em
   /.177em is the same fraction at any size.                                 */
@media (max-width:560px){
  :lang(zh-Hans) #end-section-title{--font-size:15vw;}
}

/* THE ABOUT PAGE'S CAPABILITY TITLE (专长 / 领域, 12vw). A plain 1em box, so
   the .061em bottom overflow above lands as a flat slice across 领's and 域's
   lower strokes — measured 1.9px at 172.8px, and clearly visible.

   Here the box height IS the layout (the title is two of them stacked), so the
   fix moves the ink up inside the box rather than growing the box: .043em,
   which is exactly half the .086em difference between the .074em of dead air
   at the top and the -.012em at the bottom. Ink ends up at .031em/.968em in
   the 1em box. Applied to `.line` and not to `.word` because the engine writes
   `transform` on `.word` every frame; `top` on the line moves the pre-reveal
   position by the same .043em, so the word is still hidden below the window
   when the reveal starts (.034em to spare).                                 */
:lang(zh-Hans) #about-capability-title .line{position:relative;top:-.043em;}

/* THE ABOUT PAGE'S MERCHANTS HEADING (我们服务的商户, 14vw below 812px). The
   same failure as the end headline, in a box that fails for a different
   reason — and this one was eating a character in production.

   The template's phone rule is `font-size:14vw; height:inherit;
   flex-wrap:wrap; row-gap:.1em` over a base of `display:flex;
   overflow:hidden`. It is a flex row of words, and it survives a long title
   by wrapping BETWEEN words: "MERCHANTS WE POWER" is three flex items, the
   widest of which (MERCHANTS) wants 321px in the 360px box at 390, so it
   fits on one row and WE POWER drops to a second. `height:inherit` resolves
   to auto, the container grows to two rows — measured 115px at 390 — and
   nothing is lost.

   我们服务的商户 is ONE flex item. There is nothing to wrap between, so the
   container stays one row (55px at 390) while the item itself is 7 x 14vw =
   98vw of text squeezed into a box that is 100vw minus the page gutter. The
   surplus wraps INSIDE the item, onto a row that `overflow:hidden` and
   `.about-clients-title-word-wrapper{height:1em}` then hide. The heading
   rendered as 我们服务的商 — the 户 was simply gone, on every phone width:

     viewport   320   360   390   414   480   560
     box        290   330   360   364   430   510
     wants      314   353   382   406   470   549   (7 x 14vw)
     over        24    23    22    42    40    39

   English fits at all six, so this is CJK-only and the English pages need
   nothing.

   12vw because the item has to fit on one row — the clip window is the
   roll-up window (three stacked copies of the word, same mechanism as the
   end headline), so growing it is not available. 7 x 12vw = 84vw fits from
   320 all the way to the 812px branch edge, with 16-48px to spare, and it
   lands the Chinese at 91-96% of its available box against English's 89-94%
   — the same density, not a smaller heading. Like the end headline this is a
   size and not a guarantee: it holds seven characters, which is what the
   title is. An eighth would need 11vw or a shorter line.                   */
@media (max-width:812px){
  :lang(zh-Hans) #about-clients-title{font-size:12vw;}
}

/* THE SOLUTION CARD LABELS (微信支付 · 支付宝, 3vw). The mirror image: the
   template shifts the label up .2em inside its 1em window so Latin capitals
   sit high in it, and on CJK that puts the ink at -.049em — 2.1px of every
   glyph's top stroke sliced off, which on 宝 removes the whole 宀 roof.

   A 1em window holds .923em of CJK ink, so there is .077em to distribute and
   the only defensible answer is to split it: .0385em top and bottom, which is
   .113em of upward shift instead of .2em. The engine's per-letter hover roll
   is unaffected — it translates each column by a percentage of the column's
   own height, which this does not touch — and the second copy of each
   character stays 8.1px below the window.                                   */
:lang(zh-Hans) .project-item-line-2-inner{bottom:.113em;}

/* THE HOME TUNNEL TITLE (您的资金，安全准时, 8vw). Same shape as the card
   labels — `#home-goal-title .line{bottom:.135em}` lifts the text for Latin
   capitals and takes 7.2px off the top of every CJK glyph, which on 安 removes
   the whole 宀 roof. .066em puts the ink back in the middle of the box.

   This one has the least room on the page and it is worth saying why: the line
   carries a full-width comma, so its ink is .987em — a 1em box has .013em
   left, and there is no arrangement with more, because the reveal slides the
   word up by exactly its own height (1em) and a taller box does not change
   that. Splitting the .013em leaves .75px each side. Sub-pixel, but a real
   clearance, and the state it replaces had 7.2px cut off at rest AND 7.2px of
   glyph already showing when the reveal started. Both improve. Growing the
   line box the way the end-section headline does would buy ~9px, at the price
   of retuning `#home-goal-title`'s margin against a scroll-driven tunnel
   timeline — not worth it for a defect that is now invisible.               */
:lang(zh-Hans) #home-goal-title .line{bottom:.066em;}

/* THE HOME "我们的方案" HEADING (8vw). The one box where moving the ink was
   not enough: the template gives it `padding-top:.5em; line-height:.9`, so the
   reveal travel is .9em while CJK ink is .934em. The ink is TALLER THAN THE
   TRAVEL, which means no offset exists that both keeps the resting text inside
   the window and keeps the pre-reveal text outside it — shifting up by the
   .05em the bottom needs pushes the incoming word .03em into view.

   So raise the line box to 1.1em and pay for it out of the padding, which
   leaves the element exactly 1.4em tall as before — no layout moves — while
   the ink lands at .424em-1.358em with 4.8px under it and the incoming word
   14.3px below the window. Was: 5.7px of 案 sliced off the bottom.          */
:lang(zh-Hans) #home-featured-title{line-height:1.1;padding-top:.3em;}

/* THE DETAIL-PAGE h1 (4.5em, 72px at 1440). Not a clip box — nothing is cut —
   but `line-height:.95` is a LINE PITCH SMALLER THAN ONE EM, and the worst
   case on these pages is a line that mixes scripts: CJK reaches .826em above
   the baseline and a Latin descender .200em below it, so "App 内收" is 1.026em
   of ink in a .95em pitch. On /zh/solutions/in_app_payment/ at 1440x900 that
   drove 款 5.1px into the p of "App" — the same collision the end-section
   headline had, one box further down the site.

   1.15 is the leading the template already uses on its own CJK-width boxes
   (#about-clients-title, .about-award-header-text) and leaves 8.9px between
   the lines at 72px. It costs height, and height is what decides whether
   #project-details-meta clears the fixed header — see "Making the detail page
   readable" below. Re-measured on every page whose Chinese title wraps.     */
:lang(zh-Hans) #project-details-title{line-height:1.15;}


/* --- Template pieces we don't use ----------------------------------------- */

/* placeholder Team subsection (faces/names/roles) on About */
#about-who-subsection-team{display:none !important;}
/* Newsletter: removed on owner instruction 2026-08-02. There is no list to
   subscribe to and the form never worked — its input carries pointer-events:none
   from the template. The container ids stay in the DOM because the engine holds
   them in its header/footer animation arrays and sets properties on them
   (domNewsletterHeader._words = []), so removing them throws during init; the
   text and the email field are gone from the markup instead. */
#footer-middle-newsletter,#header-menu-newsletter,#footer-bottom-labs,#header-menu-labs{display:none !important;}

/* Merchant application is intentionally display-only until onboarding opens. */
.uw-link-disabled {
  cursor: default !important;
  pointer-events: auto !important;
}
.uw-application-disabled,
[aria-disabled="true"].g-cta,
[aria-disabled="true"][id^="project-details-launch-cta"] {
  cursor: default !important;
  pointer-events: none !important;
}
#footer-contact-socials{display:none !important;}
#home-featured-cta{display:none !important;}
/* the reel "watch" button opened the upstream template's hosted film — hide it and the big
   "Our Platform" overlay, and make the video itself non-interactive */
#home-reel-video-watch-btn{display:none !important;}
#home-reel-video-title{display:none !important;}
#home-reel-video-placeholder{pointer-events:none !important;}
/* line-2 UNIWEB was the hidden WebGL-logo slot; show it now the logo mesh is gone */
#about-who-title-left-2{visibility:visible !important;}


/* --- Header: brand mark + wordmark ---------------------------------------- */

#header-logo{display:inline-flex !important;align-items:center !important;gap:10px;}
#header-logo img.uniweb-brand{height:30px;width:30px;display:block;border-radius:9px;flex:none;}
.uniweb-logo-text{font-family:'Poppins',sans-serif !important;font-weight:500 !important;letter-spacing:-.01em !important;line-height:1 !important;}


/* --- Preloader (the Uniweb loading screen, shown on every page load) ------- */

#preloader{background:#f0f1fa !important;}
#preloader-percent-digits{display:none !important;}
.uniweb-load{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:26px;z-index:5;padding:24px;box-sizing:border-box;}
.uniweb-load__mark{position:relative;display:flex;align-items:center;justify-content:center;width:46px;height:46px;animation:uwin .9s cubic-bezier(.25,.6,.2,1) both;}
.uniweb-load__logo{position:relative;width:46px;height:46px;border-radius:12px;box-shadow:0 10px 30px rgba(26,47,251,.16);}
.uniweb-load__name{font-family:UniwebSans,'Poppins',sans-serif;font-weight:400;font-size:clamp(30px,3.4vw,44px);letter-spacing:-.02em;color:#000;margin-top:0;animation:uwin .9s cubic-bezier(.25,.6,.2,1) .08s both;}
.uniweb-load__bar{position:relative;width:min(300px,56vw);height:1px;border-radius:1px;background:rgba(0,0,0,.1);overflow:hidden;margin-top:6px;animation:uwin .9s cubic-bezier(.25,.6,.2,1) .16s both;}
.uniweb-load__bar i{position:absolute;left:0;top:0;height:100%;width:34%;border-radius:1px;background:#000;animation:uwload 1.3s cubic-bezier(.6,.1,.3,.9) infinite;}
.uniweb-load__cap{position:absolute;left:0;right:0;bottom:34px;font-family:'IBMPlexMono',monospace;font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:rgba(0,0,0,.35);text-align:center;animation:uwin .9s cubic-bezier(.25,.6,.2,1) .24s both;}
@keyframes uwin{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:translateY(0)}}
@keyframes uwload{0%{transform:translateX(-140%)}100%{transform:translateX(360%)}}


/* --- The floating header, and what is behind it --------------------------- */

/* THE WORDMARK WAS LANDING ON TOP OF THE PAGE'S OWN TEXT, and often enough
   that it is not an edge case: sampled at 402x874 on /solutions/, DOM copy sat
   inside the wordmark's box at 20 of 40 scroll positions — half the page. It
   hit the SOLUTIONS h1, the card captions (In-App Payment, Dynamic PayNow,
   Terminal Solution), the eyebrow lines, and on /about/ the capability list
   ("007 Cross-border Money Transfer"). Desktop has it too.

   Nothing was defending against it. #header is fixed and transparent —
   #header-background is the full-viewport blue MENU curtain sitting at
   opacity 0, not a header bar — and the wordmark's `mix-blend-mode:exclusion`
   only guarantees it is not the same COLOUR as what is behind it. Two texts
   in the same pixels are both unreadable whatever colour they are.

   So put a backdrop between them. `backdrop-filter` rather than a colour,
   because there is no colour to use: the page behind is near-white on /, black
   on /about/, and a different per-page tint on each of the nine detail pages
   (data-color-bg), and none of that reaches the DOM — the engine keeps it on
   the canvas. A blur is background-agnostic and costs one readback of a
   402x96 band, not the screen.

   It also improves the blend rather than fighting it: exclusion now resolves
   against a smooth average instead of high-contrast glyph edges, so the
   wordmark reads as one solid colour instead of inverting letter by letter.

   Masked to fade out over the lower third so it reads as haze rather than as
   a bar — the template's header is meant to float, and a hard edge would
   change what the page is. The ::before is the header's first child in tree
   order and every control in there is positioned above it (#header-logo
   already carries z-index:1), and #header-background comes later in the DOM,
   so the menu curtain still covers this when the menu opens.

   AND IT ONLY EXISTS ONCE THE PAGE HAS MOVED. The band is the full width of
   the header, so at the top of the page — where nothing has scrolled under it
   yet — the only thing it could reach was the hero tagline, which the page was
   composed to show sharp and which is the first thing anybody sees. Where the
   tagline starts, against where the wordmark ends:

     viewport    402     812     900     994    1440
     tagline x    25      25     252     278     403
     wordmark  65-203  65-203  85-223  90-227 112-250

   Below 812 the page is one column and the tagline runs under the lockup;
   above it the tagline sits in its own grid column and clears it entirely. At
   994 it was three lines of soft focus for nothing.

   Gating on scroll rather than on width is what keeps both halves: the tagline
   is sharp on arrival at every width, and the scrim is still there for what it
   was built for — the SOLUTIONS h1, the card captions and the eyebrow lines
   passing under the wordmark on a phone (20 of 40 scroll positions), and the
   single letters of display type that cross it on a desktop (31% of positions
   on /solutions/ at 1440, 12% on /).

   `html.uw-scrolled` is set by uniweb.js section 6 — read that comment for why
   the signal cannot be window.scrollY. The transition is on opacity so the
   band arrives rather than snaps; with no class and no JS there is simply no
   scrim, which is the state the site shipped in before it existed. */
#header:before{
  content:"";
  position:absolute;
  inset:0;
  -webkit-backdrop-filter:blur(14px);
  backdrop-filter:blur(14px);
  -webkit-mask-image:linear-gradient(to bottom,#000 0,#000 58%,transparent 100%);
  mask-image:linear-gradient(to bottom,#000 0,#000 58%,transparent 100%);
  pointer-events:none;
  opacity:0;
  transition:opacity .28s cubic-bezier(.35,0,0,1);
}
html.uw-scrolled #header:before{opacity:1;}

/* The reveal is a nicety, not the mechanism. Someone who has asked not to be
   animated still gets the scrim, just without the fade. */
@media (prefers-reduced-motion:reduce){
  #header:before{transition:none;}
}


/* --- The award/showcase section headers ----------------------------------- */

/* "TRACK RECORD" printed straight through "2015", and it was not one title.
   The header is a block title with `.about-award-header-number` ABSOLUTELY
   positioned against the right edge, so the title wraps against the full
   column and simply ignores the number. Any title whose last line ends past
   the number's left edge collides with it. Measured at 402x874:

     page        title              last line ends   number starts   overlap
     /about/     TRACK RECORD             342              279         63px
     /solutions/  PAYMENT FLOW             361              349         12px

   The others cleared it only by luck of where they happened to wrap.

   Make the number a real flex item instead, so the title wraps against the
   space that is actually left. TRACK RECORD becomes two lines the way SMART
   TERMINAL and MERCHANT PORTAL already were, which is the composition the
   section was drawn in — nothing new had to be designed for it, and no title
   length can bring the collision back.

   `margin-top:1em` restates the `top:1em` the absolute positioning was giving
   the number (10.85px on /about/), so it keeps sitting just under the title's
   cap line rather than jumping to the top of the row.

   The padding has to move with it. On /about/ the page gutter lives on the
   TITLE (`padding:0 var(--base-padding-x)`) and the number was positioned
   against it separately; once they are siblings in a row, the gutter belongs
   to the row or the number ends up flush against the screen edge. On
   /solutions/ the `.uw-award` section already carries that padding, so the
   second rule takes it back off — otherwise those headers indent twice. */
@media (max-width:1000px){
  .about-award-header{
    display:flex;align-items:flex-start;justify-content:space-between;
    gap:.8em;padding:0 var(--base-padding-x);
  }
  .about-award-header-title{flex:1 1 auto;min-width:0;padding-left:0;padding-right:0;}
  .about-award-header-number{position:static;flex:none;margin-top:1em;}
  .uw-award .about-award-header{padding-left:0;padding-right:0;}
}

/* THE TWO KINDS OF TITLE WRAP DIFFERENTLY, and only one of them may.

   On /solutions/ the `.uw-award` titles are plain text, so the row above lets
   them wrap where they should — SMART / TERMINAL, CHINESE AND / REGIONAL /
   WALLETS — which is how those sections already looked.

   On /about/ they are not text. The engine splits `#about-award`'s titles
   into one `<div class="char" style="display:inline-block">` PER CHARACTER
   for the reveal, and an inline-block is an atomic box: the browser may break
   the line between any two of them, with no idea where the words are. Left to
   wrap, ACCEPTANCE came out "ACCEPTANC / E" and TRACK RECORD "TRACK REC /
   ORD". So hold that title on one line and size it to fit instead.

   3em is measured, not chosen. Worst case is the longest title against the
   widest number — "Track record" beside "2015" — and the ratio it needs is:

     viewport   320    360    390    402    430    560
     scale     .813   .824   .833   .773   .785   1.120

   .75 (3em of the template's 4em) clears every width in the branch with 3-8%
   to spare, and 560 is where the type stops needing it at all — 4em already
   fits there, which is why the shrink stops at the breakpoint the template
   itself uses. The number scales with the title, so the ratios hold. */
@media (max-width:1000px){
  #about-award .about-award-header-title{white-space:nowrap;}
}
@media (max-width:560px){
  #about-award .about-award-header-title{font-size:3em;}
}

/* The `.uw-award` side needs the mirror of that, for the mirror reason: its
   titles wrap at spaces, but a single WORD still has to fit the column, and
   at 320px it did not — "Terminal" wants 240px of a 226px column at 4em, and
   `overflow:hidden` was quietly eating the tail of both SMART TERMINAL and
   MERCHANT PORTAL. 3.4em brings the longest word back inside with 10% to
   spare at the narrowest width anyone browses at.

   `overflow-wrap:break-word` behind it is a net, not a layout: it fires only
   for a word that cannot fit at all, and it turns a title that would have
   been silently truncated into one that is merely broken. Losing text without
   saying so is the worse of the two failures, and it is the one this section
   was already doing. */
@media (max-width:360px){
  .uw-award .about-award-header-title{font-size:3.4em;}
}
@media (max-width:1000px){
  .uw-award .about-award-header-title{overflow-wrap:break-word;}
}


/* --- Home: "Licensed payment channels" strip in the reel section ---------- */

/* `clear:both` is the whole fix for a defect that was visible at EVERY width
   and worst on a phone. `#home-reel-cta` — the "How it works" pill above this
   strip — is `float:left` in the template and is never cleared. This strip is
   a plain block that follows it, so it starts at the float's top edge, and
   `.uniweb-paychips` is a flex container, which means it establishes a block
   formatting context and a BFC MAY NOT OVERLAP A FLOAT: the browser shifts it
   right of the pill and shrinks it to whatever is left.

   Measured before, at 402x874: the pill is 167px of the 352px column, so the
   three logo columns got 185px between them. And because the box was then
   shrink-to-fit, `.uniweb-paylogo{width:100%}` had no definite width to
   resolve against and every logo fell back to its intrinsic SVG size — 51px
   instead of the ~107px its column could give it. The strip came out half
   width, right-aligned under the pill, and 251px tall, which put the last
   logo (WeChat Pay, in the staggered middle column) straight through the
   "PLAY REEL" band — the band is `#home-reel-video-container-decoration`,
   absolutely positioned 28px ABOVE the reel container, so it overhangs into
   whatever ends flush against it.

   Desktop had the same squeeze, just with more room to hide it: at 1440 the
   label's "L" sat behind the pill and the strip used 462px of its 634px
   column.

   margin-bottom pays for the band's 28px overhang plus air, so the strip's
   last row can never touch it again. max-width on the chips holds the desktop
   logos at the size they already were (462px over three columns) now that the
   container is no longer being shrunk for them; below that it is inert and the
   phone gets the full column. */
.uniweb-paychannels{clear:both;margin-top:2.4em;margin-bottom:3.5em;}
.uniweb-paychannels-label{font-family:'IBMPlexMono',monospace;font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;color:#7c8299;margin-bottom:1.5em;}
.uniweb-paychips{display:flex;gap:16px;align-items:flex-start;max-width:470px;}
.uniweb-paycol{display:flex;flex-direction:column;gap:16px;flex:1 1 0;min-width:0;}
.uniweb-paycol-mid{margin-top:52px;}
.uniweb-paylogo{width:100%;height:auto;display:block;}


/* --- Footer: layout, trust column, bottom bar ----------------------------- */

#footer-section{min-height:auto !important;}
#footer-top{flex-grow:0 !important;min-height:14vh !important;}
#footer-bottom-tagline{font-variant:normal !important;}
#footer-bottom-copyright,#footer-bottom-tagline{opacity:.6;}
.footer-address-line:empty{display:none;}
#footer-contact-address > .footer-address-line:first-child,#footer-contact-address > .footer-address-line-wrapper:first-child .footer-address-line,#footer-enquires-header,#footer-business-header,#footer-inquiry-header{font-family:'IBMPlexMono',monospace;font-size:.62em;letter-spacing:.16em;text-transform:uppercase;opacity:.45;margin-bottom:1em;}
#footer-inquiry-link{position:relative;display:block;width:fit-content;padding-bottom:.2em;}
#footer-contact-inquiry{margin-top:2.5em;grid-column:1 / span 3;will-change:transform;}

/* Phone + WhatsApp. Leads the contact column because it is the channel a
   Singapore merchant actually uses to start a conversation — the three email
   addresses below it are for merchants who are already onboarded. */
#uw-footer-contact-phone{grid-column:1 / span 3;will-change:transform;}
#uw-footer-phone-header{font-family:'IBMPlexMono',monospace;font-size:.62em;letter-spacing:.16em;text-transform:uppercase;opacity:.45;margin-bottom:1em;}
#uw-footer-phone-link,#uw-footer-whatsapp-link{position:relative;display:block;width:fit-content;padding-bottom:.2em;}
#uw-footer-whatsapp-link{opacity:.6;}
#uw-footer-whatsapp-link:hover{opacity:1;}
/* the licence line in the footer bottom bar is now a link to the MAS register */
#footer-bottom-tagline .uw-mas-link{color:inherit;text-decoration:none;border-bottom:1px solid currentColor;padding-bottom:.1em;}

#uw-footer-trust{grid-column:8 / span 5;align-self:end;font-size:1.15em;line-height:1.45;}
#uw-footer-trust .uw-ft-h{font-family:'IBMPlexMono',monospace;font-size:.68em;letter-spacing:.16em;text-transform:uppercase;opacity:.45;margin-bottom:1em;}
#uw-footer-trust .uw-ft-logos{display:flex;flex-wrap:wrap;gap:.55em;max-width:23em;}
#uw-footer-trust .uw-ft-logo{height:2em;width:auto;display:block;border-radius:4px;}

/* rotating SG food illustration under the address — desktop only */
#uw-footer-food{display:none;}

@media (max-width:1000px){
  #footer-middle-contact{grid-row:1;}
  #uw-footer-trust{grid-column:1 / span 6;grid-row:2;margin-top:2.75em;}

  /* THE EMAIL ADDRESSES WERE RUNNING OFF THE RIGHT EDGE OF THE PHONE.

     Below 1000px the template moves #footer-contact-enquires to `1 / span 3`
     and leaves #footer-contact-business at `4 / span 3`, so the two support
     addresses sit side by side in a six-column grid — 168px of cell each at
     402px wide. The links inside are `width:fit-content` and do not wrap
     (they are one unbroken token), so they simply overflow their cell:
     measured at 402x874, Support@uniwebpay.com wanted 202px and ran from x=25
     to x=227 while the Reconciliation column started at x=209, and
     Service@uniwebpay.com ran from x=209 to x=406 against a 377px content
     edge. The user sees "Support@uniwebpay." and "Service@uniwebpay.c" —
     the addresses are truncated at the screen edge, which for a contact
     address is not a cosmetic bug.

     A two-up grid cannot hold a 202px address at this width, so give each
     block the full six columns. Business inquiry gets the same treatment: at
     190px it also overflowed its cell, and only looked intact because the
     column beside it happened to be empty.

     row-gap replaces the per-block `margin-top:2.5em`, which several of these
     blocks did not carry — #uw-footer-contact-phone had none, so "PHONE &
     WHATSAPP" was sitting flush against "Singapore 189721" above it with no
     separation at all. One gap on the container spaces every row the same. */
  #footer-middle-contact{row-gap:2.5em;}
  #uw-footer-contact-phone,#footer-contact-enquires,
  #footer-contact-business,#footer-contact-inquiry{grid-column:1 / -1;margin-top:0;}
}
@media (min-width:1001px){
  #footer-top{min-height:4vh !important;margin-top:0 !important;}
  #footer-middle{align-items:start;}
  #footer-middle-contact{grid-column:1 / span 7;grid-template-columns:repeat(7,minmax(0,1fr));row-gap:2em;}
  #footer-contact-address{grid-column:1 / span 4;}
  #uw-footer-contact-phone{grid-column:5 / span 3;margin-top:0;}
  #footer-contact-enquires{grid-column:5 / span 3;margin-top:2.5em !important;}
  #footer-contact-business{grid-column:5 / span 3;margin-top:0 !important;}
  #footer-contact-inquiry{grid-column:5 / span 3;margin-top:0;}
  #uw-footer-trust{grid-column:8 / span 5;align-self:start;margin-top:0;font-size:1.15em;}
  /* logos sit at 85% of their grid cell (15% smaller than the cell-filling
     original) and are centred in it, so the shrink doesn't leave lopsided
     gutters; the columns themselves are unchanged. */
  #uw-footer-trust .uw-ft-logos{display:grid;grid-template-columns:repeat(3,1fr);column-gap:1em;row-gap:1em;width:100%;max-width:none;align-items:start;justify-items:center;padding-bottom:2em;}
  #uw-footer-trust .uw-ft-logo{width:85%;height:auto;aspect-ratio:226/167;}
  #uw-footer-trust .uw-ft-logos .uw-ft-logo:nth-child(3n+2){transform:translateY(35%);}
  #footer-bottom{display:flex !important;justify-content:space-between;align-items:baseline;margin-top:4em !important;padding-top:2em;border-top:1px solid rgba(0,0,0,.08);}
  #footer-bottom-tagline{grid-column:4 / span 8 !important;padding-right:6em;text-align:right;white-space:nowrap;overflow:visible !important;}
  #uw-footer-food{display:flex;flex-direction:column;align-items:flex-start;gap:1em;grid-column:1 / span 4;grid-row:2 / span 2;align-self:start;margin-top:.4em;}
  .uw-food-img{width:8.5em;height:8.5em;object-fit:contain;transition:opacity .45s ease,transform .45s ease;}
  .uw-food-name{font-family:'IBMPlexMono',monospace;font-size:.62em;letter-spacing:.16em;text-transform:uppercase;opacity:.45;transition:opacity .45s ease;}
}


/* --- End section: title underline decorations ----------------------------- */

/* The title carries three decorative bars. Only the top one is container-
   relative (`width:calc(100% - .65em)`); the two bottom ones are hardcoded em
   widths tuned to the upstream template's original second line, which was two words with a gap
   — the left bar underlined the first word, the right bar the second.
   Every Uniweb second line is a single phrase ("settled!", "in your bank.",
   "always paid."), so the .82em left bar has nothing to underline and reads as
   a stray dash floating before the word.

   The other two bars are re-measured per line at runtime (uniweb.js section 4)
   so each line gets underlined end to end, which leaves no role for a third
   partial bar.

   Their vertical position on the Chinese pages is set in the CJK display-type
   block near the top of this file — the template's `top:.85em` is where Latin
   capitals stop, which is inside a CJK glyph. */
#end-section-title-bottom-left-decoration{display:none !important;}

/* The end-section headline is now a real <a> to the merchant application.
   The template already named it "-link" and already gave it `cursor:pointer`,
   but it shipped as a <div> with no href and no handler — so the site asked
   "Ready to start accepting payments?" and offered nothing to click, and more
   importantly `/`, `/about/` and `/solutions/` contained NO apply link in the
   served markup at all: the header pill and the menu "Apply" entry are both
   built by uniweb.js, which crawlers and AI answer engines do not run.

   Retagging div -> a exposes the three user-agent anchor defaults the template
   never had to think about. Neutralise exactly those. `display:block` is not
   cosmetic: the engine replaces the content with block-level
   `.end-section-title-link-line` children during init. */
#end-section-title-link{display:block;color:inherit;text-decoration:none;}


/* --- Home page only ------------------------------------------------------- */

/* at narrow desktop widths (~813-1080px) the long hero tagline (grid col 4/span5)
   runs under the header's round sound button on the right. Shrink its column span
   in that band so the text clears the control; wide + mobile are unaffected. */
@media (min-width:813px) and (max-width:1080px){
  #home-hero-title{grid-column:4 / span 4 !important;}
}


/* --- The solution cards (home + /solutions/) -------------------------------
   The card list on a phone, where a card's caption was being overrun by the
   NEXT card's photo — the reported "solution 的文字与图片重叠".

   The caption sits UNDER the photo it labels (`.project-item-main` then
   `.project-item-footer`), and below 812px the template budgets
   `margin-top:2em` — 32px — between one card and the next. In a static
   layout that is enough. This is not a static layout: the card photo is not
   a DOM image at all, it is a WebGL plane (see the note in uniweb.js above
   SOLUTIONS), and the engine's scroll pass deliberately displaces those
   planes against the page — the depth/pinch distortion that makes the list
   feel physical. Captured mid-scroll at 402x874 the planes stand tens of
   pixels off their DOM slots, uniformly, in whichever direction the scroll is
   going. 32px of budget does not survive that, and on a phone — momentum
   scrolling, so the list is in motion most of the time it is being read — the
   photo lands on the caption's descenders. On the reported screenshot the
   "y" of "WeChat Pay & Alipay" and of "In-Store Payment" are both cut by the
   photo below them, with the plane sitting ~36px above its slot.

   The distortion is the design and is not the thing to change; the spacing
   was never budgeted for it. So separate the two gaps instead of leaving them
   near-equal (15px above the caption, 32px below):

                        at rest    plane 36px high    plane 36px low
     caption to its own photo   8        44                 -
     caption to the next card  88        52                124

   The caption stays closer to the photo it belongs to in every state, and
   there is 52px of clearance where there was -2px. It costs 51px per card,
   ~460px over the nine, which on a page this length is not a scroll anyone
   notices.

   Desktop is untouched: it has `margin-top:5em` between rows already, and at
   that width the same displacement is a fraction of the gap.               */
@media (max-width:812px){
  .project-item:not(:first-child){margin-top:5.5em;}
  .project-item-line-1{margin:.8em 0 .7em;}
}


/* --- Solutions page (/solutions/) only ------------------------------------- */

/* keep the "SOLUTIONS" page heading on one line — it is one letter wider than
   the template's "PROJECTS" and was wrapping (SOLUTION / S) at 17vw. */
#projects-main-title-wrapper{font-size:16vw !important;}
#projects-main-title{white-space:nowrap;}


/* --- Solution detail pages (/solutions/<id>/) only ------------------------- */

/* Every selector below exists only on a detail page, so this block no-ops on
   the other three pages.

   The template ships the whole detail-page stylesheet but no reference page
   survived the portfolio deletion, so there is no "how it looked" to match —
   only the raw numbers, and those numbers are tuned for a *credits* column,
   not a content one. At 1440px they put the two side lists in a 6.8em (109px)
   column at .75em (12px): `#project-details-meta` is 34em, `-left` is 60%,
   `-right` is 40% with `padding-left:20%` (percentage padding resolves against
   the 34em wrapper, and box-sizing is border-box site-wide, so the right
   column's *content* box is only 40%-20% = 20% of 34em). That is what made the
   first attempt unreadable, and it is the one thing here worth spending
   design time on.

   So: widen the block and rebalance the two columns, which takes the side
   list from 109px to 243px, and lift the two body sizes from .75em to .875em.
   The title stays at the template's 4.5em — it is the page's signature and
   the copy is written short enough to live inside it.

   Scoped to >=813px on purpose. Below that the template switches the meta
   block to `width:100%`, stacks the columns and puts the side lists in a
   two-column grid at clamp(1rem,1.5vw,3rem) — that layout is already
   readable, and because uniweb.css is linked last these id-level rules would
   otherwise win the tie and break it.

   Safe against the reel: `#project-details-items-move-container` starts at
   `padding-left:48em` and the meta block ends at `--base-padding-x + 40em`.
   `#project-details`'s font-size is clamped at >=1rem, so at every width where
   this layout applies the gutter stays open — checked at 813, 1280, 1440,
   1920 and at the 21/9 branch (which raises both numbers together). */

@media (min-width:813px){
  #project-details-meta{width:40em;}
  #project-details-left{width:56%;}
  #project-details-right{width:44%;padding-left:6%;}
  #project-details-desc{font-size:.875em;line-height:1.6;}
  #project-details-side-list{font-size:.875em;}
}

/* SHORT DESKTOP VIEWPORTS — this is the header collision, and it is a *height*
   problem, not a `top` one. `#project-details-meta` is `top:50%` centred, so
   whether it clears the fixed header depends entirely on how tall it is:
   clearance = (viewportHeight - metaHeight) / 2 - headerBottom. The header
   bottom is ~96px and does not move, so every viewport shorter than about
   2*(96 + metaHeight/2) collides.

   Measured on the tallest page (a three-line title plus two lead paragraphs,
   520px): 94px of clearance at 1440x900 but only **4px at 1280x720**. Verifying
   one viewport height was not enough.

   So give back the two biggest vertical costs — the display title and the
   desc's 4em margins — but only where they are actually unaffordable. 860px is
   chosen so that 900-tall viewports keep the template's exact 4.5em title;
   below that the title tracks the viewport instead. `--vh` rather than `vh`
   because the engine maintains it. */
@media (min-width:813px) and (max-height:860px){
  #project-details-title{font-size:calc(var(--vh, 1vh) * 8);}
  #project-details-desc{margin:2.4em 0;}
  #project-details-right{margin-top:1em;}
}

/* Below 813px the template stacks the meta block and puts the two side lists in
   `grid-template-columns:1fr 1fr` — with no column gap. Any row wider than half
   the screen then butts straight into its neighbour: at 390px, "SGQR — one
   label, many schemes" ran into "Terminal Solution" with nothing between them,
   which reads as a collision rather than two columns. The template's own demo
   content never hit it because a credits column is all short single words.
   A gutter costs a little wrapping and buys back the separation. */
@media (max-width:812px){
  #project-details-side-list{column-gap:1.5em;}
}

/* The "Related" list is how a merchant gets from one solution to the next, and
   the template styles those anchors exactly like the plain items beside them —
   the underline only exists on hover, so at rest nothing says "link". Reuse
   the template's own :after rule rather than adding a border: show it at rest,
   faint, and let the existing hover transition take it to full. */
#project-details-side-list a:after{transform:scaleX(1);opacity:.3;transition:opacity .3s cubic-bezier(.16,1,.3,1);}
@media (hover:hover){
  #project-details-side-list a:hover:after{opacity:1;}
}

/* the two list headers get the micro-label treatment the rest of the site
   already uses (see #footer-enquires-header above) rather than a new one */
.project-details-side-list-title{font-family:'IBMPlexMono',monospace;font-size:.72em;letter-spacing:.16em;margin-bottom:1.7em;}
.project-details-side-list-item{line-height:1.5;margin:0 0 .6em;}
.project-details-side-list-item:last-child{margin-bottom:0;}

/* Second half of the short-viewport fix above. It has to live here rather than
   in that block because the two rules it overrides are declared *after* it at
   equal specificity, and one of them uses the `margin` shorthand.

   Why it is needed: the pages now carry five "Related" links rather than four,
   and at 720px tall it is the RIGHT column that sets the meta block's height,
   not the left — 13 rows plus two headers came to 452px against the left
   column's ~380, and clearance fell from the shipped 52px to 38px. The extra
   link is worth keeping (internal links between the ten pages are the only hub
   besides the card grid), so pay for it out of the list's own leading, which at
   this viewport height is the least valuable vertical space on the page.
   Measured after, on all ten pages: 1280x720 -> 70px of clearance (the shipped
   four-link build had 52), 1366x768 -> 94px, 1440x900 unchanged at 94-120. */
@media (min-width:813px) and (max-height:860px){
  .project-details-side-list-title{margin-bottom:.9em;}
  .project-details-side-list-item{margin:0 0 .34em;}
  .project-details-side-list-item:last-child{margin-bottom:0;}
}

/* The "next project" strip paints over the *current* page while you scroll
   the reel, but the template hardcodes `var(--color-black)` on its label and
   track — invisible on a dark page, and four of the seven are dark. The
   current page's own text colour is the only one guaranteed to read on it. */
#project-details-preview-footer-text{color:var(--project-details-text);}
#project-details-preview-footer-bar-background{background:var(--project-details-text);}
#project-details-preview-footer-arrow{color:var(--project-details-text);}

/* the scroll hint sits opposite the meta block; keep its arrow on the same
   optical line as its label */
#project-details-header-info svg{flex:none;}


/* --- About page only ------------------------------------------------------ */

/* no italics in the "who" section (italic + all-caps reads badly) */
#about-who-title-left-4,#about-who-title-left-4 span,.about-who-title-right-text,#about-who-desc-top .is-italic,#about-who-desc-top,#about-who-desc-bottom{font-style:normal !important;}
/* who-desc — normal word gaps (template spans carried .5em) */
#about-who-desc-top span span{margin-right:.27em !important;margin-left:0 !important;}
#about-who-desc-bottom span span{margin-left:.27em !important;margin-right:0 !important;}

/* merchant wall — separate the 3 carousel rows (template overlapped them via -2em),
   then give them more air and larger chips on desktop */
.about-clients-carousel-line-wrapper:not(:first-child){margin-top:1.6em !important;}
.about-clients-carousel-line img{height:5.6em !important;width:12.5em !important;object-fit:contain;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.12);border-radius:14px;padding:1.35em 1.7em;margin:0 .9em;}
@media (min-width:813px){
  #about-who-subsection-details{font-size:4.4vw;}
  #about-clients-carousel{margin-top:5.5em !important;}
  .about-clients-carousel-line img{height:6.4em !important;width:14em !important;}
  .about-clients-carousel-line-wrapper:not(:first-child){margin-top:2em !important;}
}

/* cursor light sweep — extends the licence ghost-word glow idea to the data sections */
.uw-glow{position:absolute;inset:0;pointer-events:none;z-index:3;opacity:0;transition:opacity .5s ease;mix-blend-mode:screen;overflow:hidden;}
.uw-glow i{position:absolute;left:0;top:0;width:640px;height:640px;border-radius:50%;transform:translate(-50%,-50%);background:radial-gradient(circle,rgba(125,150,255,.22) 0%,rgba(90,200,255,.10) 42%,rgba(0,0,0,0) 68%);}

/* --- Document outline (SEO) ------------------------------------------------
   Three template elements were retagged as headings so each page has a real
   <h1> and no level is skipped:
     #about-who-title-left   <div> -> <h1>   "WE ARE UNIWEB — A LICENSED …"
     #projects-main-title    <h4>  -> <h1>   "SOLUTIONS"
     #home-featured-title    <h4>  -> <h2>   "Our Solution"
     #home-reel-title        <h4>  -> <h2>   "Every way to get paid."
   The template styles all of them by id and never set the properties a UA
   heading default would now supply, so neutralise exactly those and nothing
   else — the rendered result is unchanged. #home-reel-title needs no rule: it
   already sets its own font-size and margins.

   Both rules are wrapped in :where() on purpose. That gives them zero
   specificity, which is still enough to beat the user-agent stylesheet (author
   origin always wins over UA origin) while losing to every template rule —
   including ones inside media queries. Without it, a flat `margin:0` here also
   wiped the template's `margin-top:1em` on #about-who-title-left below 812px,
   because uniweb.css is linked last and would win the id-vs-id tie.          */

/* inherits 16vw / 8vw from its wrapper; a UA <h1>/<h2> font-size would win */
:where(#projects-main-title,#home-featured-title){font-size:inherit;}

/* positioned off `bottom`, so a UA h1 block margin would shift the hero */
:where(#about-who-title-left){margin:0;}


/* --- Solutions page: the four native showcase sections -------------------- */

/* Smart Terminal / Payment Flow / Merchant Portal / the FAQ. The first three
   were removed once as
   "duplicative of the six cards" and restored on the owner's instruction —
   they are not duplicative, they are the only place the site states what the
   product actually does (Sunmi P3, magnetic stripe, T+1 settlement batches,
   Excel export, void and refund on device). The detail-page copy under
   tools/detail-pages.data.mjs is sourced from these sections.

   They reuse the About page's award-section classes (.about-award-*) on
   purpose, so they inherit the template's own reveal and stagger rather than
   being a static block dropped into it. The WebGL side is driven by two hooks
   the vendor bundle still exports — window.__uwSolBg (per-frame background
   lerp, called guarded) and window.__uwGL.makeTitle (the iridescent outline
   title) — wired up in uniweb.js.

   Self-scoping: every selector below starts from .uw-award or a .uw-* class,
   none of which exist on / or /about/, and the generator strips these sections
   out of the detail-page shell so they are not duplicated onto seven pages. */

.uw-award{position:relative;color:var(--color-off-white);padding:calc(var(--base-padding-y) * 2) var(--base-padding-x);display:flex;flex-direction:column;justify-content:center;min-height:100vh;font-size:clamp(.85rem,2.1vw,2rem);--margin-left:0;--width-size:min(12vw,220px);--uw-k:0;--uw-mx:0;--uw-my:0;overflow:hidden}
.uw-award.uw-light{color:var(--color-black,#000)}.uw-award.uw-light .about-award-line{background:var(--color-black,#000)}.uw-award.uw-light .uw-award-media img{filter:drop-shadow(0 30px 60px rgba(0,0,0,.25))}
.uw-gl-title{position:absolute;left:-4vw;top:calc(50% - 49.5vw);height:auto;visibility:hidden;pointer-events:none;z-index:0}
.uw-award-body{position:relative;z-index:1;display:grid;grid-template-columns:minmax(0,1fr) var(--uw-media,clamp(320px,48%,820px));gap:calc(var(--grid-gap) * 1.5);align-items:center}
.uw-award-list{position:relative;min-width:0}
.uw-award-list .about-award-item,.uw-award-list .about-award-header-title{padding-left:0;padding-right:0}
.uw-award-list .about-award-header-number{right:0;left:auto}
.uw-award-list .about-award-line{width:100%;left:0;background:var(--color-off-white)}
.uw-award-list .award-category-awards .about-award-item-wrapper-text:nth-child(3){flex:0 0 auto;width:auto;max-width:40vw;white-space:nowrap}
.uw-award .about-award-item-wrapper{cursor:default}
.uw-award .about-award-item-wrapper:hover .about-award-item-wrapper-text{transform:translate3d(1.3em,0,0)}
.uw-award .about-award-item-wrapper-svg{left:0;opacity:0;transition:transform .4s cubic-bezier(.4,0,.1,1),opacity .3s ease}.uw-award .about-award-item-wrapper:hover .about-award-item-wrapper-svg{transform:translateZ(0);opacity:1}
.uw-award-media{display:flex;justify-content:center;align-items:center;min-width:0;perspective:1400px}
.uw-award-media img{max-width:100%;max-height:88vh;height:auto;object-fit:contain;filter:drop-shadow(0 40px 80px rgba(0,0,0,.6));transform:rotateY(calc(var(--uw-mx) * 10deg)) rotateX(calc(var(--uw-my) * -7deg)) translate3d(calc(var(--uw-mx) * 26px),calc(var(--uw-my) * 20px + (1 - var(--uw-k)) * 46px),0) scale(calc(.93 + var(--uw-k) * .07));transition:transform .5s cubic-bezier(.16,1,.3,1);will-change:transform}
.uw-award .uw-rv{opacity:0;transform:translateY(1.7em);transition:opacity .9s cubic-bezier(.16,1,.3,1),transform .9s cubic-bezier(.16,1,.3,1);transition-delay:var(--d,0s)}
.uw-award.is-in .uw-rv{opacity:1;transform:none}
@media (max-width:900px){.uw-award{min-height:auto;padding-top:calc(var(--base-padding-y) * 3);padding-bottom:calc(var(--base-padding-y) * 3)}.uw-award-body{grid-template-columns:1fr}.uw-award-media{order:-1;width:88%;margin:0 auto calc(var(--base-padding-y) * 1.5)}.uw-award-media img{max-height:54vh}.uw-award-list .award-category-awards .about-award-item-wrapper-text:nth-child(3){max-width:52vw}}

.uw-award:has(.uw-steps){--uw-media:clamp(280px,38%,560px);font-size:clamp(.72rem,1.6vw,1.5rem)}
.uw-award:has(.uw-shots){--uw-media:clamp(300px,42%,640px);font-size:clamp(.72rem,1.6vw,1.5rem)}
.uw-award:has(.uw-creds){--uw-media:clamp(240px,32%,520px);font-size:clamp(.78rem,1.8vw,1.7rem)}
.uw-steps{display:flex;justify-content:center;align-items:flex-end;gap:clamp(8px,1.4vw,26px);flex-wrap:nowrap}
.uw-step{margin:0;flex:1 1 0;min-width:0;display:flex;flex-direction:column;align-items:center;gap:.9em;transform:translate3d(calc(var(--uw-mx) * var(--sx,10px)),calc((1 - var(--uw-k)) * var(--sy,40px) + var(--uw-my) * 8px),0);transition:transform .5s cubic-bezier(.16,1,.3,1)}
.uw-step:nth-child(1){--sx:22px;--sy:64px}.uw-step:nth-child(2){--sx:10px;--sy:38px}.uw-step:nth-child(3){--sx:-6px;--sy:62px}
.uw-step img{width:100%;height:auto;border-radius:14px;box-shadow:0 26px 56px rgba(0,0,0,.55)}
.uw-step figcaption{display:flex;align-items:center;gap:.6em;font-size:.62em;line-height:1.2;color:var(--color-off-white);opacity:.86;text-align:left}
.uw-step-n{font-family:IBMPlexMono;opacity:.55}
.uw-shots{position:relative;display:block;aspect-ratio:16/11}
.uw-shot{position:absolute;width:80%;border-radius:12px;box-shadow:0 30px 66px rgba(0,0,0,.62);border:1px solid rgba(240,241,250,.08);transition:transform .55s cubic-bezier(.16,1,.3,1)}
.uw-shot-a{left:8%;top:0;z-index:3;transform:translate3d(calc(var(--uw-mx) * 26px),calc((1 - var(--uw-k)) * 44px + var(--uw-my) * 14px),0)}
.uw-shot-b{right:0;top:22%;z-index:2;transform:translate3d(calc(var(--uw-mx) * 14px),calc((1 - var(--uw-k)) * 62px + var(--uw-my) * 9px),0)}
.uw-shot-c{left:14%;bottom:0;z-index:1;transform:translate3d(calc(var(--uw-mx) * 4px),calc((1 - var(--uw-k)) * 78px + var(--uw-my) * 5px),0)}
@media (max-width:900px){.uw-shots{aspect-ratio:16/13}.uw-step figcaption{font-size:.72em}}

/* Narrow screens: the category column is `width: var(--width-size)` with
   `overflow:hidden`, and --width-size is min(12vw,220px) — 47px at 390px wide,
   which clipped our labels to "Overvie", "Transac", "Settlem". The upstream
   About page gets away with it because its categories are single short words;
   ours are not. Widen that column, tighten the gap to the number, and let the
   third column wrap instead of running off the edge (it is nowrap + clipped by
   default, and "Live sales & GMV trend" does not fit on one line at 390px).
   The numbers still line up because the column keeps a fixed width — sizing it
   to content would collapse the empty label cells and stagger every row. */
@media (max-width:900px){
  .uw-award{--width-size:min(30vw,170px);}
  .uw-award-list .award-category-awards .about-award-item-wrapper-text:nth-child(2){margin-left:1em;}
  .uw-award-list .award-category-awards .about-award-item-wrapper-text:nth-child(3){white-space:normal;max-width:none;flex:1 1 auto;}
}


/* --- Solutions page: the FAQ section (.uw-faq) ---------------------------- */

/* The fourth native section. It is the same section type as the three above —
   same reveal, same stagger, same WebGL background lerp and outline title (the
   word is composed by tools/gl-title.mjs out of the letters the other three
   already ship) — with one structural change: the third cell of each row holds
   a question AND its answer instead of a single nowrap label.
   Building it this way rather than as a new component is the whole point: a
   visible FAQ is what makes the FAQPage schema legitimate, and the schema is
   generated back out of this markup by tools/sync-projects-schema.mjs.

   Every selector is prefixed with .uw-faq, which exists on exactly one section
   of one page, so none of this reaches the other three. */

/* no media column, so the body is one column and the type comes down a step —
   at the shared clamp() an answer paragraph would set at ~2rem */
.uw-faq{font-size:clamp(.72rem,1.5vw,1.45rem);min-height:auto;}
.uw-faq .uw-award-body{grid-template-columns:minmax(0,1fr);}
.uw-faq .about-award-item{margin-bottom:2.6em;}

/* The template's third cell is `flex:0 0 auto; white-space:nowrap` (labels like
   "Thermal printing" never wrap). A question does. That rule lives at
   .uw-award-list .award-category-awards …:nth-child(3), specificity (0,4,0),
   so it has to be out-specified rather than merely written later. */
.uw-faq .uw-award-list .award-category-awards .about-award-item-wrapper-text:nth-child(3){
  display:block;flex:1 1 auto;min-width:0;max-width:46em;white-space:normal;
}
/* an <h3> so the outline runs h1 SOLUTIONS -> h2 section -> h3 question; the
   UA heading font-size/weight/margin would otherwise all land */
.uw-faq .uw-faq-q{font-size:inherit;font-weight:inherit;line-height:1.25;margin:0;}
/* 38em at .62em of the section size is ~80 characters a line — the answers ran
   to ~110 across a 1440px viewport before this, which is past readable.

   The dimming is a translucent COLOUR, not `opacity`. Opacity creates a group,
   so a child can never be brighter than its parent — and the "how do I get
   started" answer carries the application link, which has to read as the
   brightest thing in the paragraph rather than the same grey as the prose
   around it. If color-mix is unavailable the declaration drops and the answer
   simply renders at full strength, which is a fine way to fail. */
.uw-faq .uw-faq-a{font-size:.62em;line-height:1.6;margin:.85em 0 0;max-width:38em;
  color:color-mix(in srgb, currentColor 78%, transparent);}

/* the entry point. Underlined at rest — the whole reason this link exists is
   that a merchant should not have to hunt for the way in. */
.uw-faq .uw-faq-a a{color:var(--color-off-white);text-decoration:underline;
  text-decoration-thickness:.06em;text-underline-offset:.28em;
  transition:text-decoration-thickness .3s cubic-bezier(.16,1,.3,1);}
.uw-award.uw-light .uw-faq-a a{color:var(--color-black,#000);}
@media (hover:hover){.uw-faq .uw-faq-a a:hover{text-decoration-thickness:.14em;}}

/* the row hover slides all three cells; on a two-line answer that reads as the
   whole block drifting, so hold the answer still and let the question move */
.uw-faq .about-award-item-wrapper:hover .about-award-item-wrapper-text{transform:none;}
.uw-faq .about-award-item-wrapper:hover .uw-faq-q{transform:translate3d(.9em,0,0);}
.uw-faq .uw-faq-q{transition:transform .4s cubic-bezier(.4,0,.1,1);}

/* The Chinese half of the same section — the same ten questions in Simplified
   Chinese, as a second `.about-award-category` rather than a second section, so
   it sits under one outline word and one background lerp. It is a category, not
   a section, because the template already puts several categories in one
   section on the About page; nothing new had to be laid out for it.

   Two adjustments, both because CJK is denser than Latin at the same size:
   the answers get a shorter measure (a CJK glyph is ~2x a Latin one, so 38em
   would run to ~150 characters a line rather than ~80), and the block needs a
   gap above it so the second 4em header does not read as part of the last
   English answer. */
/* Only when it FOLLOWS the English half. On /zh/solutions/ the Chinese
   questions are the section's only category, and a 2.2x page gutter above the
   first thing in the section reads as a layout bug rather than as separation. */
.uw-faq .about-award-category + .uw-faq-zh{margin-top:calc(var(--base-padding-y) * 2.2);}
.uw-faq .uw-faq-zh .uw-faq-a{max-width:30em;line-height:1.75;}
.uw-faq .uw-faq-zh .uw-faq-q{line-height:1.4;}

@media (max-width:900px){
  /* the topic column and the number are dead weight next to a wrapped question
     on a phone — keep the number as the marker and give the text the width */
  .uw-faq .about-award-item-wrapper-text:nth-child(1){display:none;}
  .uw-faq .uw-award-list .award-category-awards .about-award-item-wrapper-text:nth-child(2){margin-left:0;}
  .uw-faq .uw-award-list .award-category-awards .about-award-item-wrapper-text:nth-child(3){margin-left:1.6em;max-width:none;}
  .uw-faq .uw-faq-a{font-size:.72em;}
  .uw-faq .uw-faq-zh .uw-faq-a{max-width:none;}

  /* .72em of an 11.52px section is 10.4px, which is a readable size for Latin
     and not one for Chinese — a CJK glyph at 10px loses its interior strokes.
     Measured at 390x844 on /zh/solutions/: 10.4px before, 13.0px after, against
     a 14.4px question. Specificity, not order, is what wins this one: the rule
     above is (0,2,0) and this is (0,3,0). */
  :lang(zh-Hans) .uw-faq .uw-faq-a{font-size:.9em;}
}

/* --- Footer: the links into the non-engine pages ------------------------- */
/*
   /guides/ and /privacy/ are not engine routes — they are plain documents
   served outside the WebGL app (see tools/build-guides.mjs). These two anchors
   are the ONLY crawlable path to them from the engine pages, which is why they
   are static HTML in all six shells rather than injected by uniweb.js like the
   header menu's Guides link: no AI crawler in robots.txt executes JavaScript,
   so a runtime-injected link to a page is, for them, no link at all.

   `data-link-type="external"` on both is load-bearing. The router intercepts
   same-origin clicks and matches the path against five registered patterns;
   /guides/ is none of them, so without the attribute the click is swallowed
   and logged as "external/unknown route ignored" — a dead link that looks
   fine in the markup.

   #footer-bottom is a 12-column grid (6 below 900px) with the copyright at
   1/6, the MAS licence at 7/3 and the tagline at 10/2, so this row takes the
   full width underneath rather than competing for a column.
*/
#uw-footer-more{
  grid-column:1 / -1;
  display:flex;
  /* Four links since /sitemap/ joined the row. On a narrow phone four of these
     at 1.5em gaps overflow the column, and an overflowing flex row does not
     wrap by default — it just extends past the viewport and takes the page's
     horizontal scroll with it. */
  flex-wrap:wrap;
  gap:.5em 1.5em;
  margin-top:.9em;
  font-size:.85em;
}
#uw-footer-more a{
  color:inherit;
  text-decoration:none;
  position:relative;
  opacity:.72;
  transition:opacity .3s cubic-bezier(.35,0,0,1);
}
#uw-footer-more a:after{
  content:"";
  position:absolute;
  left:0;
  bottom:-.15em;
  width:100%;
  height:.0625em;
  background:currentColor;
  transform:scaleX(0);
  transform-origin:left;
  transition:transform .5s cubic-bezier(.35,0,0,1);
}
@media (hover:hover){
  #uw-footer-more a:hover{opacity:1;}
  #uw-footer-more a:hover:after{transform:scaleX(1);}
}
