/* ═══════════════════════════════════════════════════════════════════════════
   CHATBRIDGE24 — THE PUBLIC SITE DESIGN SYSTEM
   Serves the marketing page at / AND the three legal documents.

   ── WHY ONE FILE FOR TWO RENDERING SYSTEMS ─────────────────────────────────
   The marketing page at / is static HTML returned by
   server/middleware/00.gate.global.ts (it cannot be a Nuxt route, because /
   is already the application dashboard). The legal pages ARE Nuxt routes.
   Without a shared stylesheet those two worlds drift, which is exactly the
   complaint that produced this work: "the terms and privacy and related pages
   are standalone pages without linking to each others".

   `.css` is in the gate's asset-extension allowlist, so /site.css is served to
   anyone, same origin, cached once across all four pages.

   ── THE GOVERNING IDEA: COLOUR ENCODES, IT DOES NOT DECORATE ───────────────
   The logo is not an abstract glyph. It is two rounded panels — cyan at
   (8,14), magenta at (34,28) — overlapping, their intersection filled deep
   navy, with a circular aperture punched clean through the middle. So:

       cyan    = arriving from the customer   (inbound)
       magenta = sent by the business         (outbound)
       navy    = the bridge itself, belonging to neither side

   ⚠️ CYAN AND MAGENTA MAY TOUCH IN EXACTLY TWO PLACES: inside the diagram's
   overlap, and inside the 3px top rule (and there only through navy, because
   the mark's own rule is that the two never meet directly). One card carries
   one accent. A button is filled OR outlined, never both. That single
   restraint is what stops this reading as a candy-coloured startup page.

   ⚠️ NO BORROWED MARKS, EVER. No WhatsApp green, no WhatsApp glyph, no
   Bitrix24 logo, no Bitrix24 blue, at any size, anywhere — including alt text
   and SVG labels. The customer's side is signified by FORM (a phone outline,
   message bubbles), never by borrowing someone else's colour. A Meta reviewer
   reads this site while deciding whether we are a legitimate applicant, and
   borrowed branding is the clearest possible negative signal.

   ⚠️ ZERO EXTERNAL REQUESTS. No font CDN, no analytics, no remote images, no
   script CDN — every byte these four pages need is served from our own origin.
   Cairo was the last exception and is now self-hosted (see the @font-face
   block below). ⚠️ THIS IS AN INVARIANT, NOT AN ACHIEVEMENT: the file is
   served from the same origin as every customer's application, that app sets
   no CSP by design, and a GDPR-forward privacy policy is one click away. If a
   future change appears to need a third-party asset, vendor it into
   app/public/ instead.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── TYPE ───────────────────────────────────────────────────────────────────
   Cairo carries the site (see --font-sans below). IBM Plex Mono carries only
   the small uppercase labels — diagram waypoints, clause numerals, eyebrows —
   where "this word has an exact defined meaning" is the signal being sent.

   ⚠️ THERE IS DELIBERATELY NO @font-face FOR THE MONO, AND THAT IS A FIX, NOT
   AN OMISSION. There were two, pointing at /fonts/IBMPlexMono-*.woff2 — files
   that are not in the repo — so every page load fired two requests that 404ed.
   Measured on the deployed page: `document.fonts` reported
   "IBM Plex Mono 600 error". Harmless in appearance, because font-display:swap
   falls straight through to the system monospace, but it is two wasted round
   trips on every visit and a permanent red line in anyone's network panel.
   A declaration for a file that does not exist is worse than no declaration.

   The stack below already resolves to a good system mono (ui-monospace →
   SF Mono / Cascadia / Roboto Mono / Consolas), which is what actually renders
   the small uppercase labels today.

   TO DELIVER IBM PLEX MONO PROPERLY: drop IBMPlexMono-Regular.woff2 and
   IBMPlexMono-SemiBold.woff2 (SIL OFL, ~30KB) into app/public/fonts/ AND
   restore the two @font-face blocks in the same change. Never one without the
   other.

   ⚠️ THE LOGO'S OWN WORDMARK IS STILL SET IN IBM PLEX SANS — chatbridge24-
   lockup.svg contains live <text> in that family with tracking fitted to Plex's
   widths — so the lockup IMAGE and this site's HTML wordmark are now two
   different faces. That is invisible in practice, because the header and footer
   render the wordmark as HTML text (Cairo) and the lockup <img> is used
   nowhere on this site. It matters if the lockup is ever placed beside site
   type; the fix then is to re-cut the lockup in Cairo, not to change this.
   ───────────────────────────────────────────────────────────────────────── */



/* ── CAIRO, SELF-HOSTED — THIS SITE NOW MAKES NO THIRD-PARTY REQUEST ───────
   Downloaded from Google Fonts and served from our own origin. Cairo is SIL
   OFL, so redistribution is explicitly permitted.

   ⚠️ THIS IS A PRIVACY FIX, NOT AN OPTIMISATION. A fonts.googleapis.com link
   transmits every visitor's IP to a third party, on a site whose GDPR-forward
   privacy policy is one click away — a German court has held that to breach
   the GDPR. It was the last external request on these pages and it is now
   gone — INCLUDING FROM THE APPLICATION ITSELF. app/assets/css/i18n.css and
   app/app.vue (deliberate duplicates) each carried the same import for the RTL
   locales, so every load of the product inside every customer's Bitrix24 iframe
   transmitted that user's IP too. Both now point at these same files.

   ⚠️ THIS FILE IS PUBLICLY SERVED AT /site.css, ONE CLICK FROM THE PRIVACY
   POLICY IT INVOKES, so a stale claim here is read by whoever audits us. It
   said the app "STILL MAKES IT" for 40 minutes after that stopped being true.

   ⚠️ CAIRO IS A VARIABLE FONT — ONE FILE PER SUBSET COVERS EVERY WEIGHT.
   Google's stylesheet declares 400 and 600 separately against the SAME url;
   the weight RANGE below states that properly, so the browser fetches one file
   per subset rather than appearing to need two.

   ⚠️ THE RANGE IS 200-1000 BECAUSE THAT IS WHAT THE FILE ACTUALLY HOLDS, and
   under-declaring it is a silent defect rather than an error. Requesting
   wght@400;600 and wght@200..1000 from Google returns the IDENTICAL url —
   verified — so the downloaded file carries the whole axis either way, and
   Google's own stylesheet declares font-weight 200 1000 for it. A narrower
   declaration does not fail; the browser CLAMPS. The application asks for 800
   (app/assets/css/i18n.css), so a "400 700" declaration would have rendered
   every bold Arabic heading in the product at 700, with nothing reporting it.

   ⚠️ THE unicode-range VALUES ARE LIFTED VERBATIM FROM GOOGLE'S STYLESHEET and
   must stay so. They are what stops a Latin-only page downloading the 31KB
   Arabic subset. Get them wrong and either every visitor pays for Arabic they
   never render, or an Arabic page silently falls back to a system face.

   The Arabic subset is included deliberately: the product ships 33 locales
   with RTL, so the site and the app can agree about type in Arabic too. */
@font-face{
  font-family:"Cairo"; font-style:normal; font-weight:200 1000; font-display:swap;
  src:url("/fonts/cairo-latin.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;
}
@font-face{
  font-family:"Cairo"; font-style:normal; font-weight:200 1000; font-display:swap;
  src:url("/fonts/cairo-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:"Cairo"; font-style:normal; font-weight:200 1000; font-display:swap;
  src:url("/fonts/cairo-arabic.woff2") format("woff2");
  unicode-range:U+0600-06FF,U+0750-077F,U+0870-088E,U+0890-0891,U+0897-08E1,U+08E3-08FF,U+200C-200E,U+2010-2011,U+204F,U+2E41,U+FB50-FDFF,U+FE70-FE74,U+FE76-FEFC,U+102E0-102FB,U+10E60-10E7E,U+10EC2-10EC4,U+10EFC-10EFF,U+1EE00-1EE03,U+1EE05-1EE1F,U+1EE21-1EE22,U+1EE24,U+1EE27,U+1EE29-1EE32,U+1EE34-1EE37,U+1EE39,U+1EE3B,U+1EE42,U+1EE47,U+1EE49,U+1EE4B,U+1EE4D-1EE4F,U+1EE51-1EE52,U+1EE54,U+1EE57,U+1EE59,U+1EE5B,U+1EE5D,U+1EE5F,U+1EE61-1EE62,U+1EE64,U+1EE67-1EE6A,U+1EE6C-1EE72,U+1EE74-1EE77,U+1EE79-1EE7C,U+1EE7E,U+1EE80-1EE89,U+1EE8B-1EE9B,U+1EEA1-1EEA3,U+1EEA5-1EEA9,U+1EEAB-1EEBB,U+1EEF0-1EEF1;
}
/* ── TOKENS ─────────────────────────────────────────────────────────────────
   Seven semantic tokens plus six derived. ⚠️ NO RULE OUTSIDE :root MAY NAME A
   HEX. If you find yourself writing a colour in a component, you are missing a
   token. Contrast ratios below are computed, not estimated — they are in the
   source so nobody "tidies" a failing colour back in.
   ───────────────────────────────────────────────────────────────────────── */
:root{
  color-scheme: light;   /* one committed look — see the note below */

  --ground:      #FFFFFF;   /* page canvas                                    */
  --surface:     #F3F5F8;   /* cards, tables, ToC rail, footer                */
  --display:     #000047;   /* headings, diagram chassis    19.3:1 on ground  */
  --ink:         #201E1D;   /* body prose                   16.6:1            */
  --ink-quiet:   #4A4847;   /* captions, meta, marginalia — 7.12:1 worst case */

  /* ⚠️ #0088B0 MEASURES 4.08:1 ON WHITE — IT FAILS AA FOR TEXT, AND IT FAILS
     INVISIBLY. It is a fill-and-artwork colour only. --inbound-text below is
     the brand's own darker cyan (from the lockup) and is the ONLY cyan that
     may carry text on a light ground. */
  --inbound:     #0088B0;   /* customer side, arriving — GRAPHICS ONLY        */

  /* ⚠️ #D6006C IS NOT THE TEXT MAGENTA, AND THE REASON IS MEASURED. It is
     5.15:1 on plain white — fine — but 4.04:1 on the magenta-tinted card
     ground, which is exactly where a role label sits. That is the same defect
     shape as the 3.77:1-on-navy incident: a colour that passes on the ground
     you tested and fails on the ground it actually lands on. --outbound is the
     same hue line (329.6° vs 329.7°) at a darker value: 5.88:1 worst case.
     Raw #D6006C survives at full strength ONLY as a solid button fill under
     white, where it measures 5.15:1. */
  --outbound:    #A80055;   /* business side, replying — text-safe, 5.88:1    */
  --outbound-fill:#D6006C;  /* the brand magenta, solid fills only            */

  --inbound-text:#006786;   /* 6.4:1 — the text-safe cyan                     */

  /* ⚠️ WAS #605D5D, WHICH MEASURED 5.11:1 ON THE CARD TINTS. AA, but under the
     7:1 this site holds itself to for body copy. */
  --edge:        #E3E6EE;
  --accent-ink:  #FFFFFF;   /* ink on a solid accent fill                     */
  --focus:       #0088B0;
  --shadow-1: 0 1px 2px rgba(0,0,71,.06);
  --shadow-2: 0 10px 30px -14px rgba(0,0,71,.18);

  /* ── THE SAPPHIRE RAMP — DERIVED, NOT CHOSEN ─────────────────────────────
     Every opaque stop on the dark band is a stated mix of two brand colours,
     so the lightest opaque stop is knowable and every contrast figure below is
     a FLOOR rather than a sample. A stop that cannot be written as a brand mix
     is a stop nobody thought about. */
  --sap-0: #0B0B38;         /* #000047 65% + #201E1D 35% — band floor         */
  --sap-1: #030343;         /* #000047 90% + #201E1D 10% — band base          */
  --sap-2: #00084C;         /* #000047 92% + #006786  8% — swell mid          */
  --sap-3: #001352;         /* #000047 82% + #006786 18% — LIGHTEST OPAQUE    */

  /* Card blooms. The neutral one is the default and most cards keep it. */
  --bloom-in:     rgba(0,103,134,.07);
  --bloom-bridge: rgba(0,0,71,.05);
  --bloom-out:    rgba(214,0,108,.07);
  --bloom-neutral:rgba(0,0,71,.05);

  /* ── CAIRO, AND WHY IT IS THE RIGHT CHOICE RATHER THAN A NEW DEPENDENCY ──
     The owner asked for Cairo. It is ALREADY the product's font: the app loads
     it in app/assets/css/i18n.css for every RTL locale (Arabic, Hebrew,
     Persian, Urdu), so the site and the application now agree about type
     instead of disagreeing. It also has a large x-height and open apertures,
     which is what "clear and readable" actually depends on, and it covers
     Arabic — which matters for a Gulf customer base.

     ⚠️ IT IS SELF-HOSTED — see the @font-face blocks above. The stack still
     names it first, so a browser that fails to load our woff2 falls through to
     the platform UI face rather than to nothing. The system fallbacks are
     ordered so the site degrades to something metrically close rather than to
     Times. */
  --font-sans:"Cairo", ui-sans-serif, system-ui, -apple-system,
    BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI", Roboto,
    "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --font-mono:"IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono",
    "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Consolas, monospace;
}

/* ── ⚠️ THIS SITE COMMITS TO ONE LOOK. THERE IS NO OS-FOLLOWING DARK MODE. ──
   It had one, and that was the wrong call for a marketing site. The owner's
   report was "I don't like the background colors", and the reason they saw a
   near-black page at all is that their OS is set to dark and the stylesheet
   followed it — so the impression a visitor forms was decided by a setting we
   do not control and cannot preview. A brochure is a designed object; it
   should look the same to everyone.

   Light is also the readable choice for the half of this site that is long-form
   legal prose: dark-on-light holds up over 2,000 words in a way that
   light-on-dark does not.

   The brand navy has not been lost — it is concentrated into `.plate`, the one
   full-bleed dark band, which re-binds these tokens ON ITSELF (see below). One
   dark band on a light page is a stronger use of #000047 than a whole page of
   it, and it makes that band the anchor by construction.

   ⚠️ DO NOT REINTRODUCE A `prefers-color-scheme` BLOCK WITHOUT ALSO SOLVING
   THE ABOVE. If a dark theme is wanted later it should be an explicit control
   the visitor can see, not an inherited OS setting — and every token below
   would need its contrast recomputed against the dark ground, which is how
   #D6006C (3.77:1 on navy) previously shipped failing AA. */


/* ── RESET + BASE ─────────────────────────────────────────────────────────── */
*,*::before,*::after{ box-sizing:border-box }

/* ⚠️ `overflow-x:clip` IS A NET, NOT THE FIX — AND SAYING SO IS THE POINT.
   The real defect was the .page grid's track arithmetic (see the banner there);
   this line only guarantees that the NEXT one cannot make the page pan
   sideways under a thumb. Horizontal overflow on a phone is uniquely nasty
   because it is invisible on a desktop at every width, so it ships, and the
   only symptom is the page sliding while someone scrolls vertically.

   ⚠️ `clip`, NEVER `hidden`. `overflow:hidden` on html/body makes the element a
   scroll container, which silently kills `position:sticky` for EVERY
   descendant — and this site's header is sticky. `clip` clips without creating
   a scroll container, so sticky survives. Verified after adding it: with the
   document scrolled to 600px the header still sits pinned at the top, under
   the 3px brand rule.

   ⚠️ IT MUST NOT BECOME A PLACE TO HIDE REAL BUGS — AND THE CHECK THAT USED TO
   BE PRESCRIBED HERE WAS ONE OF ITS VICTIMS. This comment said to test
   `document.documentElement.scrollWidth > clientWidth`. With the clip on BOTH
   html and body that property tracks the VIEWPORT, not the content, so it reads
   clean while content is genuinely cut off. Measured on the deployed page at
   375px with the pre-fix track restored:

       documentElement.scrollWidth  375   <-- says fine, is wrong
       document.body.scrollWidth    415   <-- the truth
       worst section right edge     415   (40px clipped, unreachable)

   Both clips are load-bearing for the masking: remove either one and
   documentElement reports 415 again.

   ⚠️ SO THE CHECK IS `document.body.scrollWidth > document.documentElement.clientWidth`
   AT A 375px VIEWPORT. Use body, never documentElement.

   ⚠️ AND BISECTING BY HIDING SUBTREES NO LONGER WORKS ON documentElement
   EITHER — the technique that actually located the original bug. Watch
   `document.body.scrollWidth` while hiding, or lift the two clips for the
   duration of the hunt.

   The clip has a real cost and it is worth stating plainly: before it, a
   horizontal overflow made the page PAN under a thumb, which is a symptom a
   customer can report — and did. Now the same overflow truncates silently.
   That trade is deliberate, but it means this check is the only detector left,
   so it has to be the working one. */
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; overflow-x:clip }
body{ overflow-x:clip }
@media (prefers-reduced-motion: reduce){ html{ scroll-behavior:auto } }

body{
  margin:0;
  background:var(--ground);
  color:var(--ink);
  font-family:var(--font-sans);
  font-size:1.09375rem;          /* 17.5px — Cairo runs a touch small at 17 */
  line-height:1.7;
  font-synthesis:none;           /* never let a fallback fake a 600          */
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  hanging-punctuation:first;
  overflow-wrap:break-word;
}

/* ⚠️ DISPLAY SIZES ARE FLUID, PROSE SIZES ARE FIXED. A 4,000-word policy that
   resizes as the window moves is disorienting to read; a headline that does
   not is a headline that is wrong at one end of the range. */

img,svg{ max-width:100%; height:auto; display:block }
a{ color:var(--inbound-text); text-underline-offset:.18em }
a:hover{ color:var(--outbound) }
:focus-visible{ outline:2px solid var(--focus); outline-offset:2px; border-radius:2px }
::selection{ background:var(--outbound); color:var(--accent-ink) }

/* ⚠️ WEIGHTS ARE 400 AND 600 ONLY. Plex's 700 muddies the g and a at display
   size, and 600 tightly tracked is where the face is best. */
h1,h2,h3,h4{
  color:var(--display); font-weight:600; margin:0;
  text-wrap:balance;
  margin-inline-start:-0.03em;   /* correct the capital's left side-bearing so
                                    the left edge of the page is unbroken     */
}
p{ margin:0; text-wrap:pretty }

/* ⚠️ ~40 <strong>s PER SCREEN IN THE PRIVACY POLICY. At 700 with a colour
   change that is a zebra. A weight-and-colour step, never a background,
   never magenta. */
strong,b{ font-weight:600; color:var(--display) }

/* ⚠️ THE .doc-SCOPED DUPLICATES BELOW ARE NOT REDUNDANT — THEY OUT-SPECIFY
   TAILWIND'S PREFLIGHT. The three legal pages are Nuxt routes, so the
   application's global CSS (preflight included) loads on them alongside this
   file, and the order of two <link>s in <head> is not something we control.
   Preflight sets `b,strong{font-weight:bolder}` and
   `h1,h2,h3{font-size:inherit;font-weight:inherit}` at element specificity —
   identical to the rules above, so whichever loads last would win. A class
   selector settles it deterministically instead of by luck. The marketing page
   at / never loads preflight and is unaffected either way. */
.doc strong,.doc b{ font-weight:600; color:var(--display) }
.doc h1,.doc h2,.doc h3,.doc h4{ font-weight:600; color:var(--display) }
.doc article ul{ list-style:none }
.doc article ol{ list-style:decimal }

/* ⚠️ THE TYPEFACE HAS TO BE RE-ASSERTED HERE TOO, AND I MISSED IT FIRST TIME.
   Measured on the deployed page: the marketing page rendered Cairo correctly,
   but /privacy-policy computed `font-family: Inter` — the application's global
   CSS sets Inter on `body` at element specificity, and every child inherits it,
   so `body{font-family:var(--font-sans)}` above lost on load order exactly the
   way `b,strong{font-weight:bolder}` does. Two pages, one stylesheet, two
   different faces is precisely the drift this file exists to prevent.
   Class-scoped so it is settled deterministically rather than by luck. */
.site-head,.site-foot,.doc,.doc article,.related{ font-family:var(--font-sans) }
.doc .eyebrow,.doc .doc__meta,.doc article h2::before,.site-foot h4{
  font-family:var(--font-mono);
}

time,.tnum{ font-variant-numeric:tabular-nums }
code,kbd,samp{ font-family:var(--font-mono); font-size:.9375em }


/* ── THE GRID — DEFINED ONCE, USED BY ALL FOUR PAGES ───────────────────────
   Everything is prose by default and opts OUT to a wider track. That is what
   lets a legal page inherit the design by DELETING classes rather than adding
   them, and it is why no max-width is ever sprayed through the markup.
   ───────────────────────────────────────────────────────────────────────── */
/* ⚠️ THE TEXT COLUMN MUST SUBTRACT THE TWO GUTTERS, OR THE WHOLE SITE SCROLLS
   SIDEWAYS ON EVERY PHONE. This read `min(68ch,100%)` and was reported from a
   real handset as "when i scroll down or up the page moves left and right".

   It is arithmetic, not a rendering quirk. Grid sizes a track row by the SUM of
   its track minimums, and `100%` in a track resolves against the grid
   container's FULL inline size — it does not know it is sitting between two
   gutters. So at a 375px viewport the minimums were:

       20px  +  0  +  min(68ch, 375px) = 375px  +  0  +  20px   =  415px

   415 against a 375 container is 40px of overflow, on every page, at every
   width below about 60em — measured live at exactly 415/375. Because the two
   `minmax(20px,1fr)` gutters are HARD minimums they cannot absorb it, and
   because every section shares the one column set, EVERY section rendered
   40px wide and the document became horizontally scrollable. That is what
   makes a touch-scroll drift sideways: the page really can pan.

   `100% - 40px` subtracts the gutters the column sits between, so the row's
   minimum equals the container exactly. Verified after the change: scrollWidth
   == clientWidth at 320, 360, 375 and 768.

   ⚠️ IF YOU EVER CHANGE A GUTTER, CHANGE THE SUBTRAHEND IN THE SAME EDIT —
   they are one number written twice, and getting it wrong does not error, it
   silently returns the sideways drift. */
.page{
  display:grid;
  grid-template-columns:
    [full-start] minmax(20px,1fr)
    [wide-start] minmax(0,190px)
    [text-start] min(68ch,100% - 40px) [text-end]
                 minmax(0,190px) [wide-end]
    minmax(20px,1fr) [full-end];
}
.page > *{ grid-column:text }
.wide{ grid-column:wide }
.full{ grid-column:full }
.scroll-x{ overflow-x:auto; -webkit-overflow-scrolling:touch }


/* ── THE 3px RULE — the cheapest thing that makes four pages one publication
   ⚠️ NAVY MIDPOINT IS NOT DECORATION: the mark's own rule is that magenta and
   cyan never touch directly. It is part of the sticky header so it never
   scrolls away.                                                              */
.brandrule{
  height:3px; width:100%;
  background:linear-gradient(90deg,var(--outbound) 0%,#000047 50%,var(--inbound) 100%);
}


/* ── HEADER ──────────────────────────────────────────────────────────────── */
.site-head{
  position:sticky; top:0; z-index:50;
  background:color-mix(in srgb, var(--ground) 82%, transparent);
  backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  border-bottom:1px solid var(--edge);
}
@supports not (backdrop-filter: blur(1px)){ .site-head{ background:var(--ground) } }
.site-head__bar{
  display:flex; align-items:center; justify-content:space-between; gap:20px;
  max-width:1240px; margin:0 auto; padding:0 24px; height:76px;
}

/* ⚠️ THE WORDMARK IS HTML TEXT BESIDE AN INLINE MARK, NOT AN <img> LOCKUP.
   There is no reversed LOCKUP asset — only a reversed mark — so putting the
   light-ground lockup on a dark page is how the first attempt at this site
   ended up washed out. As text it is selectable, screen-readable, and correct
   in both themes because it is painted from tokens. */
/* ── THE BRAND IS THE SUPPLIED LOCKUP, UNMODIFIED ───────────────────────────
   ⚠️ `/chatbridge24-lockup.svg` AS AN <img>, SCALED ONLY — the same asset the
   module renders in app/layouts/default.vue, at the same fidelity. Height is
   set and width is auto, so the 590x148 artwork keeps its ratio at every size.

   ⚠️ DO NOT REBUILD IT OUT OF AN INLINE MARK + HTML TEXT. That is what this
   site did until 2026-08-30 and it was wrong twice over. The module's own
   comment already said so — "it is the brand, and re-setting the wordmark in a
   different typeface makes it a lookalike rather than the logo" — and the
   rebuild here set the wordmark in Cairo while the artwork uses IBM Plex Sans.
   It also DRIFTED IN COLOUR, which is how it was caught: the reconstruction
   painted its magenta panel from `var(--outbound)` (#A80055, the darkened
   text-safe magenta) where the logo is #D6006C. Reported as "our logo colors
   are little darker than the actual one", and measured at exactly that.

   An <img> cannot drift. The colours, the geometry, the wordmark and its
   tracking all live in one file that both the module and this site load.

   ⚠️ THE LOCKUP IS FOR LIGHT GROUNDS ONLY. There is no reversed LOCKUP asset —
   only a reversed MARK (chatbridge24-mark-reversed.svg) — so this must never be
   placed on the sapphire .plate band or any dark surface. The header is
   --ground and the footer is --surface, both light, which is what makes this
   safe here. If a dark placement is ever needed, the reversed mark plus set
   type is the answer, and it needs a designed asset, not a filter. */
.brand{ display:flex; align-items:center; text-decoration:none }
.brand img{
  height:40px; width:auto; display:block; flex:none;
  user-select:none; -webkit-user-drag:none;
}
@media (max-width:559px){ .brand img{ height:34px } }

.site-nav{ display:flex; align-items:center; gap:30px }

/* ⚠️ `:not(.btn)` IS LOAD-BEARING — WITHOUT IT THE HEADER BUTTON IS
   ILLEGIBLE. `.site-nav a` is specificity (0,1,1); `.btn--primary` is (0,1,0).
   So the nav rule won on any button inside the header and painted
   --ink-quiet on the --outbound fill: measured 1.6:1 on the deployed page,
   grey on pink. The hero's button was fine because it is not inside .site-nav,
   which is exactly what made it easy to miss. Verified in a real browser. */
.site-nav a:not(.btn){
  color:var(--ink); text-decoration:none; font-size:1.0625rem; font-weight:500;
  position:relative; padding-block:6px;
}
.site-nav a:not(.btn):hover{ color:var(--display) }
/* The underline grows from the centre — a small, cheap piece of craft that
   reads as considered rather than as a hover colour change. */
.site-nav a:not(.btn)::after{
  content:""; position:absolute; left:50%; right:50%; bottom:0; height:2px;
  background:var(--outbound); border-radius:2px;
  transition:left .18s ease, right .18s ease;
}
.site-nav a:not(.btn):hover::after{ left:0; right:0 }
@media (prefers-reduced-motion: reduce){ .site-nav a:not(.btn)::after{ transition:none } }

/* ── THE SELECTED LINK ──────────────────────────────────────────────────────
   Reported 2026-08-30: "when i open the page, i don't know which one i
   selected". Two mechanisms, because this nav mixes two kinds of destination
   and only one of them is a page.

   1 · A PAGE (Privacy, Terms) is marked SERVER-SIDE with aria-current="page"
       by site/Shell.vue. The styling hangs off the same attribute a screen
       reader announces, rather than off a second `active` class that could
       drift from it — one fact, one place.

   2 · A SECTION of the home page (#how, #about) has no server-side "current":
       it changes as the reader clicks. `:target` matches the section the URL
       fragment names, and `:has()` lets the NAV LINK be styled from it.

   ⚠️ `:has()` IS WHY THIS NEEDS NO JAVASCRIPT, AND THAT IS NOT A PREFERENCE.
   The marketing page is static HTML returned by the gate and is REQUIRED to
   carry no script of its own (test/gate/access-gate.mjs asserts it), so a
   scroll-spy was never available. Where `:has()` is unsupported the nav simply
   does not highlight — exactly today's behaviour. It is feedback, never
   navigation, so degrading costs nothing.

   ⚠️ `[href$="#how"]`, NOT `[href="/#how"]`. The two navs disagree: the
   marketing page writes `#contact` while Shell.vue writes `/#contact`, and the
   section links are `/#how` in both. An ends-with match covers every form, so
   this cannot silently stop matching when one of the two navs is edited.

   ⚠️ `:not(.btn)` THROUGHOUT, FOR THE REASON DOCUMENTED ABOVE. "Contact us" is
   a filled --outbound button; painting --display on it reproduces the 1.6:1
   grey-on-pink defect this file already records. A filled button is prominent
   already and needs no selected state. */
.site-nav a:not(.btn)[aria-current="page"],
.nav-toggle__panel a:not(.btn)[aria-current="page"],
body:has(#how:target)   .site-nav a:not(.btn)[href$="#how"],
body:has(#how:target)   .nav-toggle__panel a:not(.btn)[href$="#how"],
body:has(#about:target) .site-nav a:not(.btn)[href$="#about"],
body:has(#about:target) .nav-toggle__panel a:not(.btn)[href$="#about"]{
  color:var(--display); font-weight:600;
}
/* the underline the hover state grows — held open, so "selected" and "hovered"
   are the same piece of craft rather than two competing signals */
.site-nav a:not(.btn)[aria-current="page"]::after,
body:has(#how:target)   .site-nav a:not(.btn)[href$="#how"]::after,
body:has(#about:target) .site-nav a:not(.btn)[href$="#about"]::after{
  left:0; right:0;
}
/* In the mobile panel the links are stacked rows with a rule between them, so a
   grown underline reads as a border. An inline-start accent bar is the same
   idea in the shape that column affords. */
.nav-toggle__panel a:not(.btn)[aria-current="page"],
body:has(#how:target)   .nav-toggle__panel a:not(.btn)[href$="#how"],
body:has(#about:target) .nav-toggle__panel a:not(.btn)[href$="#about"]{
  box-shadow:inset 3px 0 0 0 var(--outbound); padding-inline-start:12px;
}

/* ── MOBILE NAVIGATION ──────────────────────────────────────────────────────
   ⚠️ A <details> DISCLOSURE, NOT JAVASCRIPT. This page ships no application JS
   by design, and the legal pages must stay usable if the app bundle fails. The
   native element gives keyboard support, a correct expanded/collapsed state for
   assistive tech, and works with scripting off. */
.nav-toggle{ display:none }
.nav-toggle > summary{
  list-style:none; cursor:pointer; display:flex; align-items:center; gap:8px;
  padding:10px 14px; border:1px solid var(--edge); border-radius:8px;
  font-weight:600; font-size:1rem; color:var(--display);
}
.nav-toggle > summary::-webkit-details-marker{ display:none }
.nav-toggle__panel{
  position:absolute; left:0; right:0; top:100%;
  background:var(--ground); border-bottom:1px solid var(--edge);
  box-shadow:var(--shadow-2);
  padding:16px 24px 22px; display:grid; gap:4px;
}
.nav-toggle__panel a{
  color:var(--ink); text-decoration:none; font-size:1.0625rem; font-weight:500;
  padding:12px 4px; border-bottom:1px solid var(--edge);
}
.nav-toggle__panel a:last-child{ border-bottom:0 }
.nav-toggle__panel .btn{ margin-top:12px; justify-content:center; padding:14px 18px }

.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  font:600 .9375rem/1 var(--font-sans); letter-spacing:-.006em;
  padding:11px 18px; border-radius:6px;   /* the mark's rx=3 on a 58 square   */
  text-decoration:none; border:1px solid transparent; cursor:pointer;
  transition:transform .12s ease, box-shadow .12s ease, background-color .12s ease;
}
/* ⚠️ THE BUTTON USES --outbound-fill (#D6006C), NOT --outbound (#A80055).
   This is the one place the brand magenta survives at full strength: as a
   SOLID FILL under white it measures 5.15:1 and passes. It is --outbound that
   is the text-safe darker variant, for magenta used as ink. */
.btn--primary{ background:var(--outbound-fill); color:var(--accent-ink); box-shadow:var(--shadow-1) }
.btn--primary:hover{ color:var(--accent-ink); transform:translateY(-1px); box-shadow:var(--shadow-2) }
.btn--ghost{ border-color:var(--edge); color:var(--display); background:transparent }
.btn--ghost:hover{ border-color:var(--display); color:var(--display) }
@media (prefers-reduced-motion: reduce){ .btn:hover{ transform:none } }


/* ── SECTIONS + RHYTHM ─────────────────────────────────────────────────────
   ⚠️ SPACE ABOVE A HEADING IS AT LEAST 3× THE SPACE BELOW IT. A heading
   belongs to what follows it; equal space makes it float between two blocks.  */
.section{ padding-block:clamp(64px,8vw,112px) }
.section + .section{ border-top:1px solid var(--edge) }

/* The small mono kicker. ⚠️ KEPT ONLY WHERE IT IS NOT THE SECTION'S HEADING —
   the hero's "Bitrix24 marketplace app" and the legal pages' "Legal". Anywhere
   it NAMED a section it has been replaced by .section-title below. */
.eyebrow{
  font-family:var(--font-mono); font-size:.75rem; font-weight:600;
  letter-spacing:.12em; text-transform:uppercase; color:var(--inbound-text);
  margin:0 0 18px;
}

/* ── THE SECTION HEADING ────────────────────────────────────────────────────
   ⚠️ THIS IS AN <h2>, NOT A STYLED PARAGRAPH, AND THAT IS THE POINT OF THE
   CHANGE. "How it works", "What it does", "Why it is different" are the words
   a search engine should see as this page's structure, and they were previously
   an 11px mono <p> — visually a caption, structurally invisible. The line
   beneath ("One message, end to end.") is the visual statement and is now a
   paragraph, not a heading: it is memorable but it is not what anyone searches
   for.

   So each section has exactly ONE h2, it carries the keyword, and it is large
   enough to read as a heading rather than a label. */
.section-title{
  font-size:clamp(1.5rem,1.15rem + 1.1vw,1.9375rem);
  line-height:1.2; letter-spacing:-.012em; font-weight:600;
  color:var(--inbound-text); margin:0 0 14px;
}
.section-statement{
  font-size:clamp(1.875rem,1.35rem + 2.1vw,2.75rem); line-height:1.1;
  letter-spacing:-.014em; font-weight:600; color:var(--display); margin:0;
  text-wrap:balance;
}
.plate .section-title{ color:var(--inbound) }
.plate .section-statement{ color:var(--display) }
.h-display{
  font-size:clamp(2.5rem,1.5rem + 4.4vw,4.25rem); line-height:1.02;
  letter-spacing:-.018em;
}
.h-section{
  font-size:clamp(1.875rem,1.35rem + 2.1vw,2.75rem); line-height:1.08;
  letter-spacing:-.008em;
}
.lead{
  font-size:clamp(1.0625rem,1rem + .4vw,1.25rem); line-height:1.55;
  color:var(--ink); margin-top:20px;
}
.muted{ color:var(--ink-quiet) }
.small{ font-size:.875rem; line-height:1.5; color:var(--ink-quiet) }


/* ── CARDS ───────────────────────────────────────────────────────────────── */
/* ⚠️ FIXED THREE COLUMNS WITH EXPLICIT BREAKPOINTS, NOT auto-fit. The old
   `repeat(auto-fit,minmax(260px,1fr))` is what produced a two-and-a-widow row
   at common widths — six cards landing as 3+3, 2+2+2 or 3+2+1 depending on the
   window. A row of three is a composition; a widow is an accident. */
.deck{
  display:grid; gap:clamp(18px,2.1vw,30px);
  grid-template-columns:repeat(3,minmax(0,1fr));
  align-items:stretch;
}
@media (max-width:1000px){ .deck{ grid-template-columns:repeat(2,minmax(0,1fr)) } }
@media (max-width:660px){ .deck{ grid-template-columns:1fr } }

.panel{
  --bloom:var(--bloom-neutral);
  --rule:var(--ink-quiet);
  position:relative; isolation:isolate; overflow:hidden;
  display:flex; flex-direction:column; gap:.7rem;
  padding:clamp(28px,2.7vw,44px) clamp(26px,2.4vw,40px);
  border:1px solid var(--edge); border-radius:20px;
  background-image:linear-gradient(180deg,#FFFFFF 0%,#EDF1F7 100%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9), 0 18px 34px -26px rgba(0,0,71,.30);
  transition:transform .3s cubic-bezier(.2,.7,.2,1), box-shadow .3s, border-color .3s;
}
.plate .panel{
  background-image:var(--surface);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07), 0 26px 52px -30px rgba(0,0,0,.95);
}
/* The bloom: the section's own aurora geometry, one card wide — which is what
   makes a card read as a window onto the section's light rather than a
   decorated box. */
.panel::before{
  content:""; position:absolute; z-index:-1; inset:-45% 35% 35% -45%;
  background:radial-gradient(58% 58% at 32% 32%, var(--bloom) 0%, transparent 72%);
}
/* The 2px rule is what NAMES the card, and it is the non-text boundary
   indicator that satisfies WCAG 1.4.11 — not the 1px border. */
.panel::after{
  content:""; position:absolute; inset:0 0 auto 0; height:2px;
  background:linear-gradient(90deg, var(--rule) 0%, transparent 78%);
}

/* ⚠️ :hover MAY CHANGE transform, box-shadow AND border-color. IT MAY NEVER
   TOUCH --surface OR --bloom. Measured: lifting the card's glass stop from .06
   to .08 drops quiet ink to 6.65 and the pink to 4.77; at .10 it is 6.25/4.48,
   below AA — on hover, where nobody screenshots and no linter looks. */
.panel:hover{
  transform:translateY(-3px);
  border-color:color-mix(in srgb, var(--edge) 100%, #000047 14%);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9), 0 30px 56px -28px rgba(0,0,71,.42);
}
.plate .panel:hover{ box-shadow:inset 0 1px 0 rgba(255,255,255,.10), 0 32px 62px -30px rgba(0,0,0,1) }
@media (prefers-reduced-motion:reduce){ .panel{ transition:none } .panel:hover{ transform:none } }

/* ⚠️ THE ONLY THREE ROLE MODIFIERS, AND THEY MAY ONLY GO ON A BLOCK WHOSE
   CONTENT IS THAT DIRECTION. The encoding survives because the DEFAULT card is
   neutral. The moment a card takes --bloom-in because cyan looked nicer there,
   the row is a rainbow and the diagram's colours stop meaning anything — which
   is exactly the distance between "expensive" and "gaudy". */
.panel--in    { --bloom:var(--bloom-in);     --rule:var(--inbound-text) }
.panel--bridge{ --bloom:var(--bloom-bridge); --rule:var(--display) }
.panel--out   { --bloom:var(--bloom-out);    --rule:var(--outbound) }

.panel__n{
  font-family:var(--font-mono); font-size:.8125rem; font-weight:600;
  letter-spacing:.16em; text-transform:uppercase; color:var(--rule);
  font-variant-numeric:tabular-nums;
}
.panel h3{
  margin:.1rem 0 0; color:var(--display);
  font-size:clamp(1.25rem,1.6vw,1.5rem); line-height:1.3; letter-spacing:-.01em;
}
.panel p{ margin:0; color:var(--ink-quiet); font-size:1.0625rem; line-height:1.7 }


/* ── THE PLATE — the diagram band, and the ONLY full-bleed dark region ─────
   ⚠️ IT RE-BINDS THE TOKENS ON ITSELF, NEVER IN A MEDIA QUERY. The consequence
   is that the diagram is byte-identical in light and dark and only ever has to
   be proven legible against ONE ground. Being the only inverted region also
   makes it the page's anchor by construction — which is why there is no zebra
   striping anywhere else.                                                     */
.plate{
  --ground:var(--sap-1);
  --surface:linear-gradient(180deg,rgba(255,255,255,.060) 0%,rgba(255,255,255,.018) 100%);
  --display:#FFFFFF;
  --ink:#F3F2F2;
  --ink-quiet:#C7D3EE;          /* 7.07:1 on the worst card ground            */
  --inbound:#62C5EE;            /* 5.43:1 worst case                          */
  --inbound-text:#62C5EE;
  --outbound:#FF8FC8;           /* #D6006C lifted on its own hue line, 5.07:1 */
  --edge:rgba(255,255,255,.12);
  --accent-ink:#FFFFFF;
  --rail:#8C9AC4;               /* structural lines only — 3.81:1, non-text   */

  --bloom-in:rgba(0,103,134,.12);
  --bloom-bridge:rgba(255,255,255,.02);
  --bloom-out:rgba(214,0,108,.18);
  --bloom-neutral:rgba(255,255,255,.02);

  --aurora-cyan:14% -12%;
  --aurora-magenta:108% 116%;

  position:relative; isolation:isolate;
  color:var(--ink-quiet);
  padding-block:clamp(80px,9vw,140px);   /* the air IS the luxury */
  background-color:var(--ground);

  /* ⚠️ THE ALPHAS BELOW ARE A LOCKED TRIPLE, NOT TASTE VALUES. The sheen at
     .10, the magenta aurora at .16 and the dither at .014 are the ceiling every
     contrast figure on this band is computed from. Measured: lifting the sheen
     to .14 drops quiet ink to 6.50 and at .18 to 5.97 with the pink at 4.28 —
     below AA. Nothing on screen breaks; it just quietly stops being legible.
     Change one and re-composite against --sap-3; the acceptance test is quiet
     ink >= 7.00 on the both-auroras-overlap ground.

     Both auroras are anchored OUTSIDE the box, so their peak stop is never
     actually painted — which is why these are floors rather than samples. */
  background-image:
    /* 5 · micro-dither, kills banding across 100vw */
    repeating-linear-gradient(112deg,
      rgba(255,255,255,.014) 0 1px, rgba(255,255,255,0) 1px 3px),
    /* 4 · vignette — only ever DARKENS, so it can never cost contrast */
    radial-gradient(130% 120% at 50% 46%,
      rgba(3,3,20,0) 30%, rgba(3,3,20,.34) 74%, rgba(3,3,20,.52) 100%),
    /* 3 · specular sheen — ONE light source */
    radial-gradient(120% 92% at var(--aurora-cyan),
      rgba(98,197,238,.10) 0%, rgba(98,197,238,.04) 38%, rgba(98,197,238,0) 68%),
    /* 2 · magenta aurora — opposite corner, extinct at 66%, never meets the cyan */
    radial-gradient(74% 60% at var(--aurora-magenta),
      rgba(214,0,108,.16) 0%, rgba(214,0,108,.07) 34%, rgba(214,0,108,0) 66%),
    /* 1 · the sapphire swell — the band opens through its middle */
    radial-gradient(120% 140% at 50% 42%,
      var(--sap-3) 0%, var(--sap-2) 44%, rgba(0,8,76,0) 82%),
    /* 0 · base */
    linear-gradient(168deg, var(--sap-1) 0%, var(--sap-0) 100%);
}

/* The closing band mirrors the light, so the two dark sections are not twins. */
.plate--mirror{ --aurora-cyan:104% 120%; --aurora-magenta:-8% -12% }

/* Machined edges: a lit top, a shadowed bottom. */
.plate::before,.plate::after{
  content:""; position:absolute; left:0; right:0; height:1px; z-index:2; pointer-events:none;
}
.plate::before{ top:0;
  background:linear-gradient(90deg,
    rgba(98,197,238,.50) 0%, rgba(243,242,242,.30) 46%, rgba(255,143,200,.42) 92%, transparent 100%);
}
.plate::after{ bottom:0; background:rgba(0,0,0,.55) }

.plate h2,.plate h3{ color:var(--display) }
.plate p,.plate li{ color:var(--ink-quiet) }
.plate .eyebrow{ color:var(--inbound) }


/* ── FOOTER — on all four pages. This is the direct fix for "standalone pages
   without linking to each others".                                            */
.site-foot{
  background:var(--surface); border-top:1px solid var(--edge);
  padding-block:72px 40px; position:relative;
}
/* The brand rule again at the foot, so the page is bracketed by it top and
   bottom. Same gradient, same navy midpoint — one device, used twice. */
.site-foot::before{
  content:""; position:absolute; top:0; left:0; right:0; height:3px;
  background:linear-gradient(90deg,var(--outbound) 0%,#000047 50%,var(--inbound) 100%);
}
.site-foot__cols{
  max-width:1240px; margin:0 auto; padding:0 24px;
  display:grid; gap:44px;
  grid-template-columns:1.6fr 1fr 1fr 1fr;
}
@media (max-width:959px){ .site-foot__cols{ grid-template-columns:1fr 1fr; gap:36px } }
@media (max-width:559px){ .site-foot__cols{ grid-template-columns:1fr } }

.site-foot h4{
  font-family:var(--font-mono); font-size:.75rem; font-weight:600;
  letter-spacing:.12em; text-transform:uppercase; color:var(--display);
  margin:0 0 18px;
}
.site-foot ul{ list-style:none; margin:0; padding:0; display:grid; gap:13px }
.site-foot a{
  color:var(--ink-quiet); text-decoration:none; font-size:1rem;
  transition:color .14s ease;
}
.site-foot a:hover{ color:var(--outbound) }

.site-foot__base{
  max-width:1240px; margin:52px auto 0; padding:26px 24px 0;
  border-top:1px solid var(--edge);
  display:flex; flex-wrap:wrap; gap:14px; justify-content:space-between;
  align-items:center; font-size:.9375rem; color:var(--ink-quiet);
}
/* "Powered by Saabsoft" — the parent company, linked out. Given its own
   treatment rather than buried in a sentence, because it is a credibility
   signal: a Meta reviewer checking whether a real company stands behind this
   should find the answer without hunting. */
.poweredby{
  display:inline-flex; align-items:center; gap:8px;
  color:var(--ink-quiet); text-decoration:none; font-size:.9375rem;
}
.poweredby strong{
  color:var(--display); font-weight:600;
  border-bottom:1px solid transparent; transition:border-color .14s ease;
}
.poweredby:hover strong{ border-bottom-color:var(--outbound); color:var(--outbound) }


/* ═══ LEGAL DOCUMENTS ══════════════════════════════════════════════════════
   Same header, same footer, same grid, same top rule. There is deliberately
   no separate "legal shell" — the documents inherit the design's LOGIC (the
   grid, the type scale, the mono-means-defined-term rule) rather than its
   decoration. The test: put the homepage and the privacy policy side by side.
   Obviously the same product; obviously different KINDS of thing. If the
   policy looks like a marketing page, it has failed.
   ═══════════════════════════════════════════════════════════════════════════ */
.doc{ padding-block:clamp(48px,6vw,80px) }

.doc__head{ margin-bottom:44px }
/* ⚠️ display-2, NOT display-1. A policy that shouts reads as marketing. */
.doc__head h1{ font-size:clamp(1.875rem,1.35rem + 2.1vw,2.75rem); line-height:1.08; letter-spacing:-.024em }
.doc__meta{
  margin-top:14px; font-family:var(--font-mono); font-size:.8125rem;
  color:var(--ink-quiet); font-variant-numeric:tabular-nums;
}

.doc article{ font-size:1.09375rem; line-height:1.78 }
.doc article > * + *{ margin-top:1.1em }

/* Numbered clauses. ⚠️ THE SINGLE LARGEST USABILITY GAIN AVAILABLE HERE: it
   makes a clause citable in an email, which is worth more to a reviewer or a
   customer's lawyer than any amount of styling. The numeral hangs into the
   margin so the numbers form their own column. */
.doc article{ counter-reset:sec }
.doc article h2{
  counter-increment:sec; position:relative;
  font-size:1.375rem; line-height:1.28; letter-spacing:-.008em;
  margin-top:2.4em; margin-bottom:.6em;      /* 4:1, per the rhythm rule      */
  padding-inline-start:12px;
  border-inline-start:3px solid var(--inbound);
}
.doc article h2::before{
  content:counter(sec);
  position:absolute; inset-inline-start:-3.5rem; top:.1em;
  font-family:var(--font-mono); font-size:.9375rem; font-weight:600;
  color:var(--ink-quiet); font-variant-numeric:tabular-nums;
}
@media (max-width:1023px){
  .doc article h2::before{ position:static; margin-inline-end:.75em }
}
.doc article h3{
  font-size:1.0625rem; letter-spacing:-.006em;
  margin-top:1.8em; margin-bottom:.5em;
}

/* Hanging bullets, so wrapped list text returns to the paragraph's left edge
   and the page keeps one unbroken left margin. */
.doc article ul{ list-style:none; margin:1.1em 0 0; padding:0 }
.doc article ul li{ position:relative; padding-inline-start:1.25em; margin-top:.6em }
.doc article ul li::before{
  content:""; position:absolute; inset-inline-start:0; top:.62em;
  width:5px; height:5px; background:var(--outbound); border-radius:1px;
}
.doc article ol{ margin:1.1em 0 0; padding-inline-start:1.35em }
.doc article ol li{ margin-top:.6em }

/* ⚠️ MONO MEANS "THIS WORD HAS AN EXACT DEFINED MEANING." Sans means a human
   wrote it. That one idea unifies the diagram's API vocabulary with the
   policy's defined terms. */
.doc dfn{ font-family:var(--font-mono); font-style:normal; font-weight:600; color:var(--outbound) }

.doc article a{ color:var(--inbound-text); text-decoration:underline }
.doc article a:hover{ color:var(--outbound) }
.doc table{ border-collapse:collapse; width:100%; font-size:.9375rem }
.doc th,.doc td{ text-align:start; padding:10px 12px; border-bottom:1px solid var(--edge) }
.doc th{ font-weight:600; color:var(--display) }

/* Related documents. TWO independent routes between the four pages — this and
   the footer — because one of them will eventually be forgotten. */
.related{ margin-top:56px; padding-top:32px; border-top:1px solid var(--edge) }
.related h2{ font-size:1.0625rem; margin-bottom:16px; border:0; padding:0 }
.related h2::before{ content:none }
.related__grid{ display:grid; gap:14px; grid-template-columns:repeat(auto-fit,minmax(200px,1fr)) }
.related__card{
  display:block; padding:16px 18px; background:var(--surface);
  border:1px solid var(--edge); border-radius:10px; text-decoration:none;
}
.related__card:hover{ border-color:var(--inbound) }
.related__card strong{ display:block; font-size:.9375rem; margin-bottom:4px }
.related__card span{ display:block; font-size:.8125rem; color:var(--ink-quiet); line-height:1.5 }


/* ── MOTION ────────────────────────────────────────────────────────────────
   ⚠️ THE REDUCED-MOTION STATE IS A COMPOSED STILL, NOT "THE ANIMATION
   REMOVED". Parking the travelling dots at fixed offsets keeps the diagram
   legible; simply setting animation:none can leave them stacked at the origin. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important; animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
}


/* ── PRINT — NOT OPTIONAL ──────────────────────────────────────────────────
   These documents get printed to PDF and forwarded to counsel and to Meta. */
@media print{
  :root{
    --ground:#fff; --surface:#fff; --display:#000047; --ink:#201E1D;
    --ink-quiet:#605D5D; --edge:#ccc; --inbound-text:#006786;
  }
  .site-head,.site-foot,.doc__toc,.related,.btn{ display:none !important }
  .brandrule{ background:#000047 !important }
  body{ font-size:11pt; line-height:1.5 }
  .doc article h2{ break-after:avoid }
  .doc article a[href^="http"]::after{ content:" (" attr(href) ")"; font-size:9pt; color:#605D5D }
}


/* ── RESPONSIVE ──────────────────────────────────────────────────────────────
   ⚠️ THE BREAKPOINT IS 960px, NOT 720px, AND THAT IS DELIBERATE. The header now
   carries FIVE links plus a button at 17px. That needs roughly 720px of its own
   beside a 38px mark and a 21px wordmark, so it collides on a tablet long
   before a phone — and the previous rule dealt with the collision by HIDING
   links (`.is-secondary{display:none}`), which silently removed Privacy and
   Terms from the navigation on small screens. Those two are pages a Meta
   reviewer and the Bitrix24 marketplace both require to be reachable. Nothing
   is hidden now; it moves into the disclosure. */
@media (max-width:959px){
  .site-head{ position:sticky }
  .site-head__bar{ height:64px; position:relative }
  .site-nav{ display:none }
  .nav-toggle{ display:block }
  /* the lockup shrinks as one piece — it is an <img>, so mark and wordmark
     cannot get out of step the way two separate elements could */
  .brand img{ height:32px }
}
@media (min-width:960px){
  .nav-toggle{ display:none }
}
