/* ==========================================================================
   InfiWorks Technologies — public site stylesheet
   Serves index, privacy, terms, 404.

   ── DESIGN DIRECTION ──────────────────────────────────────────────────────
   "Quiet editorial, one loud field." Boldness is spent in exactly ONE place
   per page: the full-bleed colour masthead and the display-scale Outfit
   headline in it. Everything below is deliberately quiet — warm paper ground,
   ink text, hairline rules, a two-column label-rail grid. No shadows, no
   gradients, no glassmorphism, no decorative blobs.

   THE SPINE. Wordmark, nav, h1, every section label and the footer all align
   to one left edge. That single alignment is what makes the page read as
   composed rather than stacked.

   ── CLOUDMESS: AN ENDORSED SUB-BRAND, REFERENCED NOT ADOPTED ──────────────
   InfiWorks is the parent; CloudMess is its product. The chrome — wordmark,
   type, footer — is InfiWorks cobalt + Outfit throughout. CloudMess is present
   as exactly one detail: the orange left rule on the card that mentions it
   (--cm-orange on .card--cm). There is no colour handover, because there is no
   longer a separate product page to hand over to.

   ── COLOUR IS COMPUTED, NEVER EYEBALLED ───────────────────────────────────
   Every pair on this site was measured rather than judged by eye. The result
   that still governs a choice here: CloudMess orange #FF6B35 is a FILL colour
   and never a text colour — as text on white it measures 2.84:1 and fails AA.
   It is used only for the 3px rule on .card--cm, never for type.

   ── MOTION: CSS ONLY, ZERO JAVASCRIPT ─────────────────────────────────────
   Scroll-driven reveals use animation-timeline: view(); page-to-page uses
   cross-document view transitions. Both are native CSS, so the site keeps
   script-src 'none' in its CSP — stronger than any JS animation library.
   Everything is wrapped in @supports + prefers-reduced-motion, and nothing
   is hidden by default outside that guard, so a browser without scroll
   timelines shows all content immediately rather than a blank page.
   ========================================================================== */

/* ---------- 1. FONTS — self-hosted, OFL ----------
   One variable file per family covers every weight, so the whole type system
   is two requests and ~80 KB. No Google Fonts hotlink: nothing here makes a
   third-party request, which is what lets font-src stay 'self'. */
@font-face{
  font-family:'Outfit';
  src:url('/fonts/outfit-var.woff2') format('woff2');
  font-weight:100 900; font-style:normal; font-display:swap;
}
@font-face{
  font-family:'Inter';
  src:url('/fonts/inter-var.woff2') format('woff2');
  font-weight:100 900; font-style:normal; font-display:swap;
}

/* ---------- 2. TOKENS ---------- */
:root{
  /* InfiWorks — the company frame */
  --brand:#2952E3;
  --on-brand:#FFFFFF;
  --on-brand-soft:#E4EAFC;     /* 5.08:1 on --brand */
  --accent:#2952E3;            /* links + rules on the page ground */

  /* CloudMess — the endorsed product. Raw value is app_theme.dart's. */
  --cm-orange:#FF6B35;         /* fills and rules only — 2.84:1, fails as text */

  --ink:#121214;
  --muted:#6A6A72;             /* 4.78:1 on --paper */
  --hair:#E7E7EA;
  --paper:#F3F2ED;             /* the logo pack's own warm "Paper" */
  --surface:#FFFFFF;
  --blank-bg:#FFF6E2;
  --blank-line:#D9B978;
  --blank-ink:#5C4412;

  --display:'Outfit',system-ui,-apple-system,'Segoe UI',sans-serif;
  --body:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;

  --measure:68ch;
  --wrap:1060px;
  --gutter:clamp(20px,5vw,48px);
  --rail:clamp(140px,16vw,190px);
  --step:clamp(48px,7vw,84px);
  --ease:cubic-bezier(.22,.7,.22,1);
  color-scheme:light dark;
}

@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --accent:#5C7CF0;          /* 4.99:1 on dark --paper */
    --ink:#F3F2ED;
    --muted:#9A9AA4;           /* 6.71:1 on dark --paper */
    --hair:#2A2A2F;
    --paper:#121214;
    --surface:#1A1A1D;
    --blank-bg:#2A2313;
    --blank-line:#6E5A2A;
    --blank-ink:#EBD9A8;
  }
}
/* Same values under an explicit attribute, so dark can be screenshotted and
   audited without changing the OS setting. */
:root[data-theme="dark"]{
  --accent:#5C7CF0;
  --ink:#F3F2ED; --muted:#9A9AA4; --hair:#2A2A2F;
  --paper:#121214; --surface:#1A1A1D;
  --blank-bg:#2A2313; --blank-line:#6E5A2A; --blank-ink:#EBD9A8;
}

/* ---------- 3. RESET ---------- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--body);
  font-size:clamp(1rem,0.97rem + 0.18vw,1.0625rem);
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img,svg{max-width:100%;height:auto;display:block}
h1,h2,h3,h4{font-family:var(--display);font-weight:600;letter-spacing:-0.02em;text-wrap:balance;margin:0}
p,ul,ol,dl{margin:0}

/* ---------- 4. LINKS + FOCUS ----------
   Underline is always present on body links — colour alone never carries the
   affordance. The focus ring is a 3px outline with offset, recoloured on the
   coloured fields so it stays visible there too. */
a{color:var(--accent);text-decoration:underline;text-underline-offset:0.18em;text-decoration-thickness:0.08em}
a:hover{text-decoration-thickness:0.14em}
:focus-visible{outline:3px solid var(--accent);outline-offset:3px;border-radius:2px}
.masthead :focus-visible{outline-color:var(--on-brand)}

.skip{
  position:absolute;left:-9999px;top:0;
  background:var(--surface);color:var(--ink);
  padding:12px 18px;z-index:20;text-decoration:none;
  border:2px solid var(--accent);border-radius:0 0 6px 0;
}
.skip:focus{left:0}

/* ---------- 5. LAYOUT ---------- */
.wrap{max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gutter)}

/* The editorial grid: label rail left, content right. Below 800px it becomes
   one column and the label sits above its section — same reading order. */
.row{display:grid;grid-template-columns:var(--rail) minmax(0,1fr);gap:clamp(20px,4vw,56px)}
@media (max-width:800px){.row{grid-template-columns:1fr;gap:14px}}

.label{
  font-family:var(--display);
  font-size:0.75rem;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;
  color:var(--muted);margin:0;padding-top:0.55rem;
}
.label::before{
  content:"";display:block;width:26px;height:2px;
  background:var(--accent);margin-bottom:0.8rem;transform-origin:left center;
}
@media (max-width:800px){.label{padding-top:0}}

.prose{max-width:var(--measure)}
.prose p + p{margin-top:1rem}

/* ---------- 6. SITE BAR (inside the masthead) ---------- */
.sitebar{display:flex;align-items:center;justify-content:space-between;gap:18px 28px;flex-wrap:wrap}
.sitebar a{display:block;line-height:0}
.sitebar .mark img{width:clamp(164px,19vw,206px)}

.nav{display:flex;gap:clamp(14px,2.4vw,26px);flex-wrap:wrap;align-items:center;line-height:1.4}
.nav a{
  font-family:var(--display);font-size:0.92rem;font-weight:500;letter-spacing:0.01em;
  text-decoration:none;position:relative;padding-block:3px;line-height:1.4;
}
/* Underline grows from the left rather than fading in — the motion matches the
   reading direction, which reads as intentional instead of generic. */
.nav a::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:2px;
  background:currentColor;transform:scaleX(0);transform-origin:left center;
  transition:transform 220ms var(--ease);
}
.nav a:hover::after,.nav a:focus-visible::after{transform:scaleX(1)}
.nav a[aria-current="page"]::after{transform:scaleX(1);opacity:0.55}

/* ---------- 7. MASTHEAD — the one loud surface ---------- */
.masthead{
  background:var(--brand);color:var(--on-brand);
  padding-block:clamp(26px,4vw,38px) clamp(48px,8vw,88px);
}
.masthead a{color:var(--on-brand)}
.masthead hr{border:0;height:1px;background:rgba(255,255,255,0.28);margin:clamp(24px,3.6vw,34px) 0 clamp(28px,4.6vw,42px)}
.masthead h1{font-size:clamp(2.3rem,6.4vw,4.1rem);line-height:1.04;letter-spacing:-0.028em;max-width:19ch}
.masthead .lede{
  margin-top:clamp(16px,2.4vw,24px);
  max-width:56ch;color:var(--on-brand-soft);
  font-size:clamp(1.05rem,0.98rem + 0.42vw,1.26rem);line-height:1.58;
}

/* Compact variant — inner pages get the same anchor at proportionate weight. */
.masthead.compact{padding-block:clamp(26px,4vw,34px) clamp(32px,4.6vw,48px)}
.masthead.compact h1{font-size:clamp(1.9rem,4.4vw,2.9rem);max-width:24ch}
.masthead.compact hr{margin:clamp(20px,2.8vw,28px) 0 clamp(22px,3vw,30px)}

/* ---------- 8. SECTIONS ---------- */
.section{padding-block:var(--step);border-bottom:1px solid var(--hair)}
.section:last-of-type{border-bottom:0}
/* :not(.label) matters. Rail labels are h2 elements too (they are the real
   section headings), and a bare `.section h2` at 0,1,1 outranks `.label` at
   0,1,0 — which silently rendered every label at 32px instead of 12px. */
.section h2:not(.label){font-size:clamp(1.45rem,3vw,2rem);line-height:1.2}
.section h2:not(.label) + .prose,.section h2:not(.label) + p{margin-top:1rem}
.section h3{font-size:1.05rem;letter-spacing:-0.01em;margin-bottom:0.4rem}
/* Subheads inside running prose need air above them or they read as part of
   the paragraph they follow. Zeroed on the first one so the block still aligns
   to the label rail beside it. */
.prose h3{margin-top:2rem}
.prose > h3:first-child{margin-top:0}

/* ---------- 10. CARD + PRODUCT BLOCK ---------- */
.card{
  border:1px solid var(--hair);background:var(--surface);
  border-left:3px solid var(--accent);
  padding:clamp(20px,3vw,30px);max-width:var(--measure);
}
.card .more{margin-top:1.1rem}
.card--cm{border-left-color:var(--cm-orange)}

/* ---------- 16. CONTACT ---------- */
.contact{display:grid;gap:1.5rem;margin:0;max-width:var(--measure)}
.contact dt{
  font-family:var(--display);font-size:0.72rem;font-weight:600;
  letter-spacing:0.1em;text-transform:uppercase;color:var(--muted);margin-bottom:0.3rem;
}
.contact dd{margin:0}
.contact dd a{font-family:var(--display);font-weight:500;font-size:1.15rem}
.contact .addr{font-style:normal}

/* ---------- 17. LEGAL PAGES ---------- */
.notice{
  border:1px solid var(--blank-line);background:var(--blank-bg);color:var(--blank-ink);
  border-left:3px solid var(--blank-line);
  padding:clamp(16px,2.6vw,22px);max-width:var(--measure);margin-bottom:var(--step);
}
.notice strong{font-family:var(--display);font-weight:600}
.notice p + p{margin-top:0.6rem}
.blank{
  background:var(--blank-bg);color:var(--blank-ink);
  border:1px dashed var(--blank-line);border-radius:3px;
  padding:0.05em 0.42em;font-size:0.94em;
}
.legal > ol{padding-left:0;list-style:none;counter-reset:sec;max-width:var(--measure)}
.legal > ol > li{counter-increment:sec;padding-top:var(--step);border-top:1px solid var(--hair)}
.legal > ol > li:first-child{padding-top:0;border-top:0}
.legal > ol > li > h2::before{
  content:counter(sec) ".";color:var(--accent);margin-right:0.5em;font-variant-numeric:tabular-nums;
}
.legal h2 + p,.legal h2 + ul{margin-top:1rem}
/* Legal pages are stacks of sibling <p>; without this they run together. */
.legal p + p{margin-top:1rem}
.legal ul{padding-left:1.15rem;margin-top:0.8rem;display:grid;gap:0.45rem}
.legal p + ul,.legal ul + p{margin-top:0.8rem}
.legal .updated{margin-top:var(--step);color:var(--muted);font-size:0.92rem}

/* Reading progress for the long policy documents — scroll-driven, no JS. */
.progress{position:fixed;top:0;left:0;height:3px;width:100%;transform-origin:left center;transform:scaleX(0);background:var(--accent);z-index:30}

/* ---------- 18. STATUTORY FOOTER ----------
   Required on EVERY page — Companies Act s.12(3)(c). */
.statutory{border-top:1px solid var(--hair);background:var(--surface);padding-block:clamp(28px,4vw,42px)}
.statutory .inner{display:grid;grid-template-columns:auto minmax(0,1fr);gap:18px;align-items:start}
.statutory .mark{width:22px;padding-top:3px}
.statutory p{margin:0;font-size:0.82rem;line-height:1.6;color:var(--muted)}
.statutory .name{font-family:var(--display);font-weight:600;color:var(--ink);font-size:0.86rem;letter-spacing:0.01em}
.statutory .meta{margin-top:0.7rem}
.statutory .meta a{color:var(--muted)}
.statutory .meta a:hover{color:var(--accent)}
.statutory nav{margin-top:0.5rem;display:flex;gap:14px;flex-wrap:wrap;font-size:0.82rem}

/* Two supplied mark variants swapped by theme — neither file is altered. */
.mark-dark{display:none}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .mark-light{display:none}
  :root:not([data-theme="light"]) .mark-dark{display:block}
}
:root[data-theme="dark"] .mark-light{display:none}
:root[data-theme="dark"] .mark-dark{display:block}

/* ==========================================================================
   19. MOTION — all CSS, no JavaScript anywhere.
   Rules: nothing moves more than ~14px, nothing lasts longer than 620ms,
   nothing animates that the reader is already reading, and every effect is
   inside BOTH @supports and prefers-reduced-motion so an unsupporting
   browser renders the finished state immediately.
   ========================================================================== */

/* 19a. Cross-document view transitions. Moving between the home page and the
   legal pages morphs the shared masthead and footer instead of hard-cutting,
   so the chrome stays put and only the content changes. One at-rule. */
@view-transition{navigation:auto}
@media (prefers-reduced-motion:no-preference){
  .masthead{view-transition-name:masthead}
  .statutory{view-transition-name:statutory}
  ::view-transition-old(root),::view-transition-new(root){animation-duration:280ms}
  ::view-transition-old(masthead),::view-transition-new(masthead){animation-duration:340ms}
}

/* 19b. Masthead entry — a short, weighted settle. Staggered by 70ms so the
   eye lands on the headline first and the lede arrives just behind it. */
@media (prefers-reduced-motion:no-preference){
  @keyframes rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}
  @keyframes fade{from{opacity:0}to{opacity:1}}
  .masthead h1{animation:rise 560ms var(--ease) both}
  .masthead .lede{animation:rise 560ms var(--ease) 70ms both}
  .masthead .sitebar{animation:fade 420ms var(--ease) both}
  .masthead hr{animation:wipe 520ms var(--ease) 40ms both;transform-origin:left center}
  @keyframes wipe{from{transform:scaleX(0)}to{transform:scaleX(1)}}
}

/* 19c. Scroll-driven reveals.

   THE RULE THIS OBEYS: no scroll-driven animation may start from a state that
   hides or removes anything. Content animates TRANSFORM ONLY — never opacity.

   Why, measured rather than assumed: a view() timeline positions an element
   inside its `entry` phase, and an element near the bottom of a page that
   CANNOT SCROLL is stuck part-way through that phase forever. Measured at
   1440x6000, three blocks sat at opacity 0.19, 0.40 and 0.69 permanently.
   Any reader whose viewport is taller than the page — a large monitor, a
   zoomed-out window, a short page — would get permanently greyed-out body
   text with no way to fix it.

   Transform-only makes that failure mode harmless: the worst case is a block
   resting 14px lower than intended, which nobody can detect. The fade that
   does run — on the masthead — is TIME-based, so it always completes. */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    @keyframes riseY{from{transform:translateY(14px)}to{transform:none}}
    /* Ranges end early (entry 45%) so the movement resolves well before the
       element reaches reading position, rather than drifting as you read. */
    .reveal{animation:riseY linear both;animation-timeline:view();animation-range:entry 5% entry 45%}
    /* The tick above each section label draws out from the spine. It scales
       from 0.3, never 0, so a stuck state is a short rule and not a missing
       one — the same no-hiding rule applied to decoration. */
    @keyframes tick{from{transform:scaleX(0.3)}to{transform:scaleX(1)}}
    .label::before{animation:tick linear both;animation-timeline:view();animation-range:entry 10% entry 55%}
  }
}

/* 19d. Reading progress — driven by document scroll, again with no script. */
@supports (animation-timeline:scroll()){
  .progress{animation:wipe linear both;animation-timeline:scroll(root block)}
}
@supports not (animation-timeline:scroll()){
  .progress{display:none}
}

/* 19e. Micro-interaction on the card blocks: the accent rule thickens and the
   block shifts a hair toward it. No shadow, no scale — consistent with a
   design language that has none. */
@media (prefers-reduced-motion:no-preference){
  .card{transition:border-color 220ms var(--ease),transform 220ms var(--ease)}
  .card:hover{transform:translateX(2px)}
}

/* 19f. The global off switch. Anything that slipped through still stops. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:0.01ms!important;animation-iteration-count:1!important;
    transition-duration:0.01ms!important;scroll-behavior:auto!important;
  }
  .progress{display:none}
}
