/* ============================================================
   operioninc.com — shared stylesheet
   Tokens, type and chrome common to every page. Page-specific
   rules stay inline in the page that needs them.

   Colours are copied from the Operion application's GENERATED
   ODS sheet so the site and the product share one palette.
   Copied, not imported: ods.css is generated in the other repo
   and must never be hand-edited there.
   ============================================================ */

/* ============================================================
   Fonts — self-hosted. The site contacts NO third-party host.
   Google Fonts was removed 2026-08-23 on counsel's advice: it
   disclosed every visitor's IP and user-agent to Google, and it
   was the page's only external request. See fonts/README.md.
   All three families are SIL OFL 1.1; the licences ship beside
   the files, which the licence requires.
   ============================================================ */

@font-face {
  font-family: "Exo";
  src: url("fonts/exo-variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Sans";
  src: url("fonts/ibm-plex-sans-variable.woff2") format("woff2-variations");
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/ibm-plex-mono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/ibm-plex-mono-500.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("fonts/ibm-plex-mono-600.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* ============================================================
   Tokens — derived from the shipped ODS sheet so the site and
   the product speak one palette. Light is the base; dark
   redefines tokens only.
   ============================================================ */

:root {
  --brand:       #2d5477;
  --brand-deep:  #203c56;
  --stat:        #dc2626;

  --ink:         #0d141b;
  --ink-2:       #3d4b59;
  --ink-3:       #5c6b7a;
  --ground:      #f4f6f9;
  --panel:       #ffffff;
  --line:        #d5dde5;
  --line-soft:   #e4eaf0;
  --line-strong: #808d9b;
  --focus:       var(--brand);
  --on-brand:    #ffffff;

  --logo-ink:    #275f8e;

  --f-display: "Exo", "Helvetica Neue", Arial, sans-serif;
  --f-body:    "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --f-mono:    "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --wrap: 1080px;
  --rail: 128px;

  /* One 4px grid. Before this there were sixteen block-axis values, four
     pairs of them 2px apart — 86 vs 88 for the same structural gap. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 20px; --s6: 24px;
  --s7: 32px; --s8: 40px; --s9: 56px; --s10: 80px; --s11: 112px;

  /* em, not ch. `1ch` is the advance of `0` — 0.600em in Plex Sans against a
     0.4493em average — so every `ch` measure ran ~33% wide, and .copy-em
     rendered 23px WIDER than .copy despite being declared narrower. */
  --measure: 34em;
  --measure-tight: 28em;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --brand:      #518abd;
    --brand-deep: #7da8ce;
    --stat:       #f87171;

    --ink:        #eaf0f6;
    --ink-2:      #b3c0cd;
    --ink-3:      #7d8c9b;
    --ground:     #0b1117;
    --panel:      #121a22;
    --line:       #253340;
    --line-soft:  #1c2833;
    --line-strong: #5a6c7c;
    --focus:       var(--brand);
    --on-brand:    var(--ground);
  
    --logo-ink:   #7da8ce;
  }
}

:root[data-theme="dark"] {
  --brand:      #518abd;
  --brand-deep: #7da8ce;
  --stat:       #f87171;

  --ink:        #eaf0f6;
  --ink-2:      #b3c0cd;
  --ink-3:      #7d8c9b;
  --ground:     #0b1117;
  --panel:      #121a22;
  --line:       #253340;
  --line-soft:  #1c2833;
  --line-strong: #5a6c7c;
  --focus:       var(--brand);
  --on-brand:    var(--ground);

  --logo-ink:   #7da8ce;
}

/* The browser keeps light UA chrome without this: light scrollbars against
   #0b1117, and mobile Safari's overscroll flashes white. Measured `normal`. */
:root                     { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { color-scheme: dark; }
}

/* ============================================================
   Reset — the two declarations this site cannot do without.
   Without border-box, .wrap (width:100% + 28px padding) computes
   to 100%+56px and the page scrolls sideways at every viewport
   under ~1152px. Without body margin:0, the UA's 8px does it too.
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; }

/* ============================================================
   Base
   ============================================================ */

body {
  background: var(--ground);
  color: var(--ink);
  font-family: var(--f-body);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--f-display);
  text-wrap: balance;
  letter-spacing: -.012em;
  line-height: 1.09;
  margin: 0;
}

p { margin: 0; }

[id] { scroll-margin-top: 88px; }
a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: 28px;
}

.railed {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  gap: 40px;
  align-items: start;
}

.chan {
  font-family: var(--f-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding-top: 9px;
  border-top: 2px solid var(--ink);
  width: max-content;
  max-width: 100%;
  line-height: 1;
  margin-top: -8px;   /* optical: the label's cap sat 11.06px below the heading's */
}

section { padding-block: var(--s10); }

/* A rule, not a texture. This was a full-bleed band of 1px ticks at a 40px
   pitch that aligned to nothing and read as a stray image. It now sits in the
   content column and looks like what it is: the boundary between two sections
   that share a background. */
.ticks {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  height: 1px;
  background: var(--line);
}

/* ============================================================
   Header
   ============================================================ */

.top {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--ground) 88%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line-soft);
}

.top-in {
  display: flex;
  align-items: center;
  gap: 20px;
  height: 66px;
}

.brandmark {
  margin-right: auto;
  display: flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
}

.brandmark svg { display: block; }
.brandmark .wordmark { fill: var(--logo-ink); }

/* ============================================================
   Buttons
   ============================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--f-body);
  font-size: 15px;
  font-weight: 600;
  text-decoration: none;
  padding: 11px 20px;
  border: 1.5px solid transparent;
  border-radius: 3px;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}

.btn-solid {
  background: var(--brand);
  color: var(--on-brand);
  border-color: var(--brand);
}

.btn-solid:hover { background: var(--brand-deep); border-color: var(--brand-deep); }

/* ============================================================
   Footer
   ============================================================ */
footer { padding-block: 44px; font-size: 14.5px; color: var(--ink-3); }

.foot-in {
  display: flex;
  flex-wrap: wrap;
  gap: 18px 30px;
  align-items: center;
}

.foot-meta {
  font-family: var(--f-mono);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding-left: 16px;
  border-left: 1px solid var(--line);
}

footer nav { display: flex; flex-wrap: wrap; gap: 22px; margin-left: auto; margin-block: -11px; }
footer a { text-decoration: none; }
footer nav a { color: var(--ink-2); padding-block: 11px; }  /* 23.5px tap target -> 45.5px */
footer a:hover { color: var(--brand); text-decoration: underline; }

/* ============================================================
   Responsive + motion — these lived in index.html, so neither
   legal page had a single @media rule of its own.
   ============================================================ */
@media (max-width: 860px) {
  [id]         { scroll-margin-top: 78px; }
  section      { padding-block: var(--s9); }
  .foot-meta   { padding-left: 0; border-left: none; }
  .railed      { grid-template-columns: minmax(0, 1fr); gap: var(--s3); }
}

/* ============================================================
   Sections arrive as you reach them.

   CSS scroll-driven animation, not JavaScript — a <script> tag
   would fail tools/check-legal.sh and make the privacy policy's
   "the page contains no scripts at all" a false statement.

   Guarded three ways, because the failure mode of getting this
   wrong is content stuck at opacity 0 forever:
     · @supports — unsupported browsers never see the keyframes
     · prefers-reduced-motion — no motion for anyone who asked
     · `both` fill with a `to` state of full opacity
   ============================================================ */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    /* Longhands on purpose. `animation: arrive linear both` sets
       animation-duration to 0s, which finishes a scroll-driven animation
       instantly — it needs `auto`. The shorthand also resets
       animation-timeline, so it is avoided entirely here. */
    main > section > .wrap {
      animation-name: arrive;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      /* cover 50% is EXACTLY the scroll position where the section's centre
         meets the viewport's centre — and it is exactly that for any section
         height and any viewport, because both terms scale together:
             at cover 0%   the section's centre sits at  V + H/2
             we want it at                               V/2
             distance                                  = V/2 + H/2
             cover range length                        = V + H
             ratio                                     = 0.5
         So the reveal finishes with the content middle-aligned, on every
         screen, without a single magic number. */
      animation-range: cover 15% cover 50%;
    }

    /* The last section can never reach `cover 50%` — that needs the page to
       scroll far enough to put its centre at the viewport centre, and there
       is nothing below it to scroll. It would strand mid-fade. `entry 100%`
       is the moment its bottom edge clears the viewport bottom, which is
       reachable for anything that fits on screen. */
    main > section:last-of-type > .wrap {
      animation-range: entry 5% entry 95%;
    }

    @keyframes arrive {
      from { opacity: 0; transform: translateY(26px); }
      to   { opacity: 1; transform: translateY(0); }
    }
  }
}
