/* ---------- tokens ---------- */
:root {
  --blue: #1E82C6;
  --green: #8AC640;
  --paper: #F7F9FB;
  --white: #FDFEFF;
  --tint: #E4F0F8;
  --ink: #14232F;
  --ink-soft: #46596A;
  --rule: #B6C2CC;        /* decorative hairlines and card borders: 1.72:1 on paper */
  --rule-strong: #788B9B; /* form control borders: 3.34:1 on paper, WCAG 1.4.11 */
  /* One typeface for the whole page, headings and body alike (client review,
     2026-09-17: "make sure all the fonts are the same"). The serif that first
     carried this was swapped back out for the sans on 2026-09-17: the serif
     had been the heading face all along, and setting the body in it too made
     the whole page read as the display voice. No web fonts, so this resolves
     to San Francisco on Apple, Segoe UI on Windows, Roboto on Android.
     Measurements in this file were taken under system-ui and under Arial,
     the far end of the stack, at 768/1024/1440. */
  --face: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Three sizes, and only three. The client asked for two — one for headings
     and one for everything else — then chose to keep section and subsection
     headings distinct, which costs the third. Anything tempted to invent a
     fourth (footer small print, field errors, the tenet markers) uses
     --fs-body instead. */
  --fs-body: .96875rem;   /* 15.5px */
  --fs-h: 1.75rem;        /* 28px — h1, h2, and the closing statement */
  --fs-h-sub: 1.1875rem;  /* 19px — h3 */
  /* One shared text edge. Must be a length, not `ch`: ch resolves against
     each element's own font, so a single value produced eleven different
     right edges down the page — body prose, hero lede, subheads and the
     eyebrow all landed somewhere different. 38rem preserves the prose line
     length that was already approved (it measured 606px). */
  --measure: 38rem;
  --logo-w: 10rem;
  /* The green divider's centre sits at 64.05% of logo.png's width (sampled
     2026-09-07). Derive the axis from --logo-w so the two cannot drift:
     changing the logo width alone used to move the divider off the axis. */
  --axis-x: calc(1.5rem + var(--logo-w) * 0.6405);
  --header-h: 4.25rem;
  /* Text colours are stated relative to the band's ground rather than
     absolutely, so a section can be inverted by flipping two values.
     Light bands: */
  --on-ground: var(--ink);
  --soft: var(--ink-soft);
  --card: var(--white);
  --card-rule: var(--rule);
}

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* +1px for the header's bottom border, which sits outside the row's
   min-height: without it an anchored heading lands 1px under the header. */
html { scroll-behavior: smooth; scroll-padding-top: calc(var(--header-h) + 1px); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--face);
  font-size: var(--fs-body);
  line-height: 1.5;
  /* Client review: everything aligns on both edges. Justification without
     hyphenation opens rivers in a 608px column, so hyphens carry the cost;
     <html lang="en"> is already set, which is what the hyphenator needs.
     Chrome (nav, labels, buttons, errors, field text) opts back out below. */
  text-align: justify;
  -webkit-hyphens: auto;
  hyphens: auto;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }
:focus-visible { outline: 3px solid var(--blue); outline-offset: 3px; }

.skip {
  position: absolute; left: 0; top: 0; padding: .5rem 1rem;
  background: var(--ink); color: var(--white); transform: translateY(-120%);
}
.skip:focus { transform: none; z-index: 10; }

.container { max-width: 76rem; margin: 0 auto; padding: 0 1.5rem; }

/* ---------- header ---------- */
.site-header {
  position: sticky; top: 0; z-index: 5;
  background: var(--white);
  border-bottom: 1px solid var(--rule);
}
.header-row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: .625rem 1.75rem; min-height: var(--header-h);
}
.brand img { width: var(--logo-w); height: auto; }
.site-nav { display: flex; flex-wrap: wrap; gap: 1.25rem; font-weight: 500; }
.site-nav a { text-decoration: none; padding: .25rem 0; border-bottom: 2px solid transparent; }
.site-nav a:hover { border-bottom-color: var(--green); }

/* ---------- axis: the logo's green divider continues down the page ----------
   The vertical rhythm lives on .axis rather than on the sections, so that each
   band's spine segment spans the full band and the segments butt against one
   another. Put the padding back on the sections and the spine breaks into
   dashes with a gap at every boundary — which is what it used to do.
   Longhand only: .container already sets `padding: 0 1.5rem` at the same
   specificity, so a `padding` shorthand here would silently drop the gutters. */
.axis { position: relative; padding-left: 2rem; padding-top: 3rem; padding-bottom: 3rem; }
.axis::before {
  content: ""; position: absolute; top: 0; bottom: 0; left: 0;
  width: 2px; background: var(--green); transform-origin: top;
}
@media (min-width: 768px) {
  .axis { padding-left: calc(var(--axis-x) + 2rem); }
  .axis::before { left: var(--axis-x); }
}
/* Below 768px the nav wraps under the logo, so the sticky header is taller. */
@media (max-width: 767px) { :root { --header-h: 6.5rem; } }

/* ---------- bands ----------
   Sections are separated by their ground, not by a drawn boundary. Three sit
   on ink — About Us, the closing statement, the footer — and they alternate
   with the light ones, so no two neighbours share a ground and nothing needs a
   rule between them. The hero stays light: it opens on the same paper as the
   header, and the tint notice directly under it is the page's first change of
   ground.

   Alternating two near-whites was tried first and cut: #F7F9FB against #FDFEFF
   measures 1.05:1, which separates nothing.

   Measured on ink #14232F: white 15.86:1, #9FB0BD 7.18:1 (the mirror of
   --ink-soft's 6.87:1 on paper), green axis 7.80:1, blue focus ring 3.86:1. */
.about, .statement, .site-footer {
  background: var(--ink);
  position: relative;
  color: var(--on-ground);
  --on-ground: var(--white);
  --soft: #9FB0BD;
  /* Cards need their own fill on ink or they read as white holes. #1F3444 sits
     1.25:1 off the ground and still carries white text at 12.74:1; its border
     at 1.64:1 against the fill matches the 1.72:1 the light bands use. */
  --card: #1F3444;
  --card-rule: #3E5468;
}

/* Every section boundary is now a change of ground, so none of them is drawn.
   The earlier hairline-and-green-hang device is gone with the last light-to-
   light join; green now marks structure at one rank only, the sub-headings. */

/* ---------- type ---------- */
/* Headings share the one text edge with prose. This deliberately gives up the
   full-column headline: a single alignment down the whole page was the client's
   request, and two edges still read as ragged to them. */
h1, h2, h3 { font-family: var(--face); font-weight: 400; line-height: 1.12; margin: 0 0 .625rem; letter-spacing: -0.01em; max-width: var(--measure); }
h1, h2 { font-size: var(--fs-h); line-height: 1.08; letter-spacing: -0.015em; }
h3 { font-size: var(--fs-h-sub); margin-top: 1.75rem; }
/* Subsections get the same green mark as the section nodes, at a smaller
   scale, so structure is signalled by one device at two ranks. */
h3::before { content: ""; display: block; width: 1.75rem; height: 2px; background: var(--green); margin-bottom: .75rem; }
/* A 28px face cannot justify in a phone-width column: measured at 375px the
   h1's widest word gap was 58.4px against a 7.4px natural space (7.9x), and
   the closing statement's 36.1px (4.9x). Headings therefore justify only from
   768px up. That threshold is not the worst desktop case, which is why it can
   be the threshold: measured 2026-09-17 across 768/1024/1440 and both faces,
   the h1 runs 1.39x-2.10x (widest gap 14.8px, at 768px in Iowan) and the
   closing statement 1.19x-1.95x. Body prose stays justified at every width
   (2.6x at worst, at 375px; 1.26x-1.91x on desktop), which hyphenation keeps
   readable. */
h1, h2, h3 { text-align: left; }
@media (min-width: 768px) { h1, h2, h3 { text-align: justify; } }
p { max-width: var(--measure); margin: 0 0 .8125rem; }
.subhead { color: var(--soft); margin-bottom: .875rem; max-width: var(--measure); }
.lede { color: var(--soft); max-width: var(--measure); }

/* ---------- section rhythm ---------- */
.hero .axis { padding-top: 3.75rem; padding-bottom: 2.5rem; }
.hero .lede { margin-top: 1.375rem; }

.notice { background: var(--tint); }
.notice .axis { padding-top: 1.375rem; padding-bottom: 1.375rem; }
/* Variant B's green left rule is dropped: with the spine now continuous it
   ran as a second green vertical 1rem away, and its indent pushed the notice
   19px right of every other paragraph. The tint band and the bold lead-in
   already mark the callout, so the text hangs on the spine like the rest. */
.notice p { margin: 0; max-width: var(--measure); }
.notice strong { font-weight: 600; }

.intro .axis { padding-top: 2rem; padding-bottom: 2rem; }

.statement .axis { padding-top: 3.75rem; padding-bottom: 3.75rem; }
.site-footer .axis { padding-top: 2.25rem; padding-bottom: 2.25rem; }

.tenets { list-style: none; padding: 0; margin: 0; max-width: var(--measure); }
.tenets li {
  display: grid; grid-template-columns: 1.5rem 1fr; gap: 0 .75rem; align-items: center;
  padding: 1rem 0; border-top: 1px solid var(--card-rule);
}
.tenets li:last-child { border-bottom: 1px solid var(--card-rule); }
/* Client review: the markers were 36px display figures hanging off a 4rem
   column. They now sit at body size, centred against the whole item rather
   than the first line, in a column measured to the widest marker. That is
   "(d)": 21.7px in Iowan Old Style, 20.5px in Georgia, both clear of the
   24px column at 768/1024/1440. */
.tenets .mark { color: var(--soft); text-align: left; }


/* ---------- form ---------- */
/* Justification is for prose. Stretching a two-line field label or the text
   someone is typing into a textarea reads as broken, and single-line chrome
   gains nothing from it either. */
.site-nav, .skip, label, button, input, textarea,
.required-note, .error, .status, .footer-copy { text-align: left; }

form { max-width: var(--measure); margin-top: 1rem; }
.required-note { color: var(--soft); max-width: var(--measure); }
.req { color: var(--soft); }
.field { margin-bottom: .875rem; }
label { display: block; font-weight: 500; margin-bottom: .3125rem; }
input, textarea {
  width: 100%; font: inherit; color: var(--ink); background: var(--white);
  border: 1px solid var(--rule-strong); padding: .5rem .6875rem; border-radius: 0;
}
input:focus, textarea:focus { border-color: var(--blue); outline: 3px solid var(--blue); outline-offset: 0; }
.field.invalid input, .field.invalid textarea { border-color: #B42318; }
.error { color: #B42318; margin: .375rem 0 0; }
.error::before { content: "⚠ "; }
.hp { position: absolute; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }
button {
  font: inherit; font-weight: 600; color: var(--white); background: var(--ink);
  border: 0; padding: .75rem 1.5rem; cursor: pointer;
}
button:hover { background: #0e1a24; }
button[disabled] { opacity: .6; cursor: default; }
.status { margin-top: 1rem; font-weight: 500; }
.status.ok::before { content: "✓ "; }
.status.fail::before { content: "⚠ "; }
.status.fail { color: #B42318; }

.form-privacy { color: var(--soft); margin: .25rem 0 1rem; text-align: left; }

/* ---------- legal pages ----------
   /privacy/ and /pdpa/ are generated from IBC's docx files by
   tools/build_legal.py. Same three sizes as the rest of the site: the document
   titles are h1/h2 at --fs-h, the numbered sections h3 at --fs-h-sub. The green
   tick that marks subsections on the home page is dropped here; across 28
   numbered questions it stops marking anything. */
.legal h3 { margin-top: 1.5rem; }
.legal h3::before { content: none; }
.legal ol, .legal ul { max-width: var(--measure); margin: 0 0 .8125rem; padding-left: 1.5rem; }
.legal li { margin-bottom: .375rem; }
.legal ol.lettered { list-style-type: lower-alpha; }
.legal ul.dashed { list-style-type: "– "; }
.legal .lang-jump { text-align: left; margin-bottom: 1.5rem; }
.legal .translation { border-top: 1px solid var(--rule); margin-top: 2.5rem; padding-top: 2rem; max-width: var(--measure); }
.legal .translation-note { color: var(--soft); }

/* ---------- footer ---------- */
.site-footer { color: var(--soft); }
.footer-brand { color: var(--on-ground); margin-bottom: .625rem; }
.footer-copy { margin: 0; }
.footer-links { text-align: left; }

/* ---------- motion ----------
   One orchestrated moment rather than scattered effects: the axis draws
   downward and the band it belongs to rises into place behind it. The hero
   runs from CSS on load so it needs no JavaScript and cannot flash; the bands
   below the fold are driven by an IntersectionObserver in main.js, which only
   ever hides what is already off screen. With JavaScript off, or with reduced
   motion asked for, every rule here is inert and the page is simply static. */
@media (prefers-reduced-motion: no-preference) {
  .site-nav a, input, textarea, button { transition: border-color 200ms, background-color 200ms; }

  @keyframes ibc-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
  @keyframes ibc-draw { from { transform: scaleY(0); } to { transform: scaleY(1); } }

  .hero h1, .hero .lede { animation: ibc-rise 700ms cubic-bezier(.2,.7,.3,1) both; }
  .hero h1    { animation-delay: 80ms; }
  .hero .lede { animation-delay: 210ms; }
  .hero .axis::before { animation: ibc-draw 1100ms cubic-bezier(.3,.8,.4,1) 40ms both; }

  /* .reveal / .draw are added by main.js only to bands below the fold */
  .axis.reveal { opacity: 0; transform: translateY(16px); transition: opacity 620ms ease, transform 620ms cubic-bezier(.2,.7,.3,1); }
  .axis.reveal.in { opacity: 1; transform: none; }
  .axis.draw::before { transform: scaleY(0); transition: transform 950ms cubic-bezier(.3,.8,.4,1) 90ms; }
  .axis.draw.in::before { transform: scaleY(1); }
}

/* ---------- geometric ground ----------
   An eight-fold girih tiling, the star-and-octagon figure of Islamic
   architectural screens, laid faintly across the dark bands. It is the
   subject's own ornament rather than generic finance decoration, and being
   non-representational it claims nothing about a firm that is not licensed
   yet — no offices, no assets, no operations implied.

   A real .svg file rather than a data: URI, because the CSP is
   `img-src 'self'` and a data: URI would be blocked with no visible error.
   Kept to the dark bands so the reading sections stay plain paper.

   Contrast cost, measured from rendered pixels on 2026-09-07: the lightest
   patterned pixel is #203331, so white text reads 13.17:1 over it rather
   than 15.86:1 on flat ink, and --soft reads 5.96:1 rather than 7.18:1.
   Both still clear 4.5:1. Raising the opacity past .10 would eat that
   margin, so re-measure before changing it. */
.about::before, .statement::before, .site-footer::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("pattern.svg") repeat 0 0;
  background-size: 260px 260px;
  opacity: .10;
  pointer-events: none;
}
