/* ==========================================================================
   Self-hosted fonts (task 2.7)

   Previously loaded from fonts.googleapis.com. That stylesheet was
   render-blocking - a real Lighthouse run (task 1.9's baseline) measured it
   at ~1.77s of the site's FCP/LCP, the single largest cost on an otherwise
   near-empty page. Self-hosting removes that round trip entirely.

   Only ONE file per family is needed, not one per weight: modern Google
   Fonts serves a variable-font resource even for a request naming discrete
   weights (confirmed by fetching the CSS directly - all four IBM Plex Sans
   weight declarations resolved to the identical file). Browsers render the
   correct weight from a single variable file via ordinary @font-face
   font-weight matching, so self-hosting the two files below covers every
   weight/family combination this site uses.

   Subset: "latin" only (U+0000-00FF, U+2000-206F, and a handful of named
   codepoints) - covers everything in this site's copy, including the
   em-dashes and middot already in use. Confirmed against the fetched
   unicode-range before downloading, not assumed.

   font-display: swap + <link rel="preload"> in PublicApp.razor's <head>
   (not defer/async - these are needed for the very first paint) minimizes
   the fallback-to-final-font swap window that was the other half of the
   Google Fonts cost (font-swap layout shift, noted in the 1.9/2.4 baselines).
   ========================================================================== */

@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 400 700;
    /* one variable file serves the whole range - see the note above */
    font-display: swap;
    src: url("/Fonts/ibm-plex-sans-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: "Instrument Serif";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/Fonts/instrument-serif-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;
}

/* ---- Metrics-matched fallback fonts: TRIED, REVERTED (task 2.7) ----
   Attempted a metrics-matched fallback (size-adjust/ascent-override/
   descent-override on local Arial/Times New Roman, standing in for IBM Plex
   Sans/Instrument Serif while the webfont loads) to close the font-swap
   layout shift that preloading alone didn't touch (see the log entry for
   this date in AppContext.md - preloading left Home's CLS bit-for-bit
   unchanged at 0.0754564..., proving the cause was the metrics mismatch
   itself, not the swap's timing).

   The override values were computed from real font tables with fontTools
   (not guessed), and cross-checked against a real constraint before ever
   testing them - ascent-override + descent-override must sum to the actual
   font's own (ascent + |descent|)/unitsPerEm, since that sum is what
   line-height:normal resolves to on each face. A first attempt failed that
   check (107.66% against IBM Plex Sans's actual 130%) and was caught before
   testing; the corrected values summed to exactly 130% for both fonts.

   Even so, measured Lighthouse CLS got WORSE on every single page after
   applying it - Home 0.075->0.183, Services 0.001->0.016, About 0.035->0.043,
   Contact 0.001->0.019 - despite the override math checking out. Likely
   cause: xAvgCharWidth is one averaged number, and real paragraph text
   reflows based on the actual width of the specific characters in it, which
   Arial/Times New Roman and IBM Plex Sans/Instrument Serif can space
   differently enough per-word that an average-matched fallback still
   re-wraps lines when the swap happens - possibly to a *worse* line-wrap
   difference than accepting the plain system-font-stack swap the site had
   before. This was verified against the live pages, not assumed from the
   math being "correct" - the math checking out did not mean the outcome
   improved, and it didn't. **Do not reintroduce this technique without
   measuring again per-page; the sound-looking formula was not sufficient.**
   Self-hosting + <link rel="preload"> (still in place) was kept - it's what
   delivered this task's actual win, moving Home from 94 to 95 and every
   interior page to 97-98. ---- */

/* ==========================================================================
   Public site design tokens (proton-software.com)

   Dark-only — no light mode, no html.light-gradient branching. That machinery
   is an admin-side pattern (see AppContext.md / DesignContext.md) and does
   not apply here.

   The five raw brand hexes below are the ONLY values that must stay in sync
   with Theming/ProtonTheme.cs and the --brand-* block in app.css — see
   DesignContext.md's "Raw brand tokens" table. Everything else here (surface
   shades, spacing, radii, shadows) is public-site-only and free to evolve
   independently of the admin palette.
   ========================================================================== */

:root {
    /* ---- Brand (keep in sync with ProtonTheme.cs + app.css --brand-*) ---- */
    --brand-lime-logo: #E2FD3C;
    --brand-lime-band: #D7FD3C;
    --brand-lime-deep: #A9C41F;
    --brand-olive: #3B4532;
    --brand-ink: #0C0E0A;

    /* ---- Surfaces (public-only; not part of the brand-sync set above) ---- */
    --pub-bg: var(--brand-ink);
    --pub-surface-1: #12150E;
    /* subtle lift off the page background */
    --pub-surface-2: #181C12;
    /* card / elevated panel */
    --pub-border: #2A3222;
    /* For boundaries that must be findable on their own (form fields): 3.8:1
       against --pub-surface-2, clearing WCAG 1.4.11's 3:1 for UI components.
       --pub-border above is only 1.3:1 - fine as a decorative hairline, not
       as the sole edge of an input. Same value as the admin dark GrayDefault. */
    --pub-border-strong: #6E7A61;

    /* ---- Text ---- */
    --pub-text: #E9EEDD;
    --pub-text-muted: #9AA68B;

    /* ---- Accent ---- */
    --pub-accent: var(--brand-lime-logo);
    --pub-accent-contrast: var(--brand-ink);
    --pub-secondary: var(--brand-olive);

    /* ---- Status ---- (same value as the admin dark palette's Error) */
    --pub-danger: #FF5A6E;

    /* ---- Type ramp (marketing scale — larger than the admin app's;
       see ProtonTheme.BuildTypography for the admin-side scale this
       intentionally exceeds).

       Display/heading sizes are fluid: clamp(min, preferred, max), where the
       preferred value is a rem+vw mix so they scale smoothly with viewport
       width instead of jumping at breakpoints. The vw coefficient is chosen
       so each size reaches its max around ~1280-1330px viewport width - past
       that, headings stop growing rather than ballooning on ultrawide
       monitors. Body-scale text stays static: fluid type earns its keep on
       a handful of big display sizes, not on the text people actually read
       paragraph-length copy in. ---- */
    --pub-font-body: "IBM Plex Sans", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
    --pub-font-display: "Instrument Serif", "Times New Roman", serif;

    --pub-display-1: clamp(2.75rem, 2rem + 3vw, 4.5rem);
    --pub-display-2: clamp(2rem, 1.5rem + 2.2vw, 3.25rem);
    --pub-heading-1: clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem);
    --pub-heading-2: clamp(1.5rem, 1.25rem + 1vw, 2rem);
    --pub-heading-3: clamp(1.25rem, 1.1rem + 0.6vw, 1.5rem);
    --pub-body-lg: 1.25rem;
    --pub-body: 1rem;
    --pub-body-sm: 0.875rem;

    /* ---- Spacing scale ---- */
    --pub-space-2xs: 0.375rem;
    --pub-space-xs: 0.75rem;
    --pub-space-sm: 1.25rem;
    --pub-space-md: 2rem;
    --pub-space-lg: 3rem;
    --pub-space-xl: 5rem;
    --pub-space-2xl: 8rem;

    /* ---- Radii ---- */
    --pub-radius-sm: 6px;
    /* matches ProtonTheme.LayoutProperties.DefaultBorderRadius */
    --pub-radius-md: 12px;
    --pub-radius-lg: 24px;
    --pub-radius-full: 999px;

    /* ---- Shadows (subtle — dark-on-dark) ---- */
    --pub-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --pub-shadow-md: 0 8px 24px rgba(0, 0, 0, 0.45);
    --pub-shadow-lg: 0 24px 64px rgba(0, 0, 0, 0.5);
    --pub-shadow-glow: 0 0 32px rgba(226, 253, 60, 0.25);

    /* ---- Motion ---- */
    --pub-ease: cubic-bezier(0.16, 1, 0.3, 1);
    --pub-duration-fast: 150ms;
    --pub-duration-base: 300ms;
    --pub-duration-slow: 600ms;

    /* ---- Layout ---- */
    --pub-header-height: 76px;
    --pub-content-max-width: 1200px;
}

/* ==========================================================================
   Base reset + element typography (tasks 1.4 + 1.6)
   ========================================================================== */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
}

body {
    background: var(--pub-bg);
    color: var(--pub-text);
    font-family: var(--pub-font-body);
    font-size: var(--pub-body);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;

    /* Column so PublicLayout's <main> can flex:1 and keep the footer at the
       bottom of short pages instead of floating mid-viewport. */
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

a {
    color: inherit;
}

img {
    max-width: 100%;
    display: block;
}

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

/* ---- Headings ----
   H1/H2 in the display serif (an accent, not the workhorse font - same rule
   as the admin app's ProtonTheme.BuildTypography); H3 and below stay in the
   body sans. Margins are top-heavy (more space above than below) so a
   heading reads as attached to the content that follows it, not centered
   between two paragraphs. */
h1,
h2 {
    font-family: var(--pub-font-display);
    font-weight: 400;
    line-height: 1.1;
    letter-spacing: -0.01em;
    margin: 0 0 var(--pub-space-sm);
}

h3,
h4,
h5,
h6 {
    font-family: var(--pub-font-body);
    font-weight: 600;
    line-height: 1.25;
    margin: 0 0 var(--pub-space-xs);
}

h1 {
    font-size: var(--pub-display-1);
}

h2 {
    font-size: var(--pub-display-2);
}

h3 {
    font-size: var(--pub-heading-1);
}

h4 {
    font-size: var(--pub-heading-2);
}

h5,
h6 {
    font-size: var(--pub-heading-3);
}

/* ---- Body copy ---- */
p {
    margin: 0 0 var(--pub-space-sm);
    /* Readable line length. Anything laid out in its own narrower column
       (a card, a multi-column grid cell) is already width-constrained by
       its container, so this rarely needs an override in practice. */
    max-width: 65ch;
}

p:last-child {
    margin-bottom: 0;
}

/* ==========================================================================
   Shared primitives (task 2.1, reused by 2.2's interior pages)

   Site-wide building blocks only — a page's own composition belongs in that
   page's scoped .razor.css, not here.
   ========================================================================== */

.container {
    max-width: var(--pub-content-max-width);
    margin: 0 auto;
    padding-left: var(--pub-space-sm);
    padding-right: var(--pub-space-sm);
}

.section {
    padding: var(--pub-space-xl) 0;
}

/* Alternate band — sits a hair above the page ground so consecutive
   sections read as distinct blocks without needing dividers. */
.section--alt {
    background: var(--pub-surface-1);
    border-top: 1px solid var(--pub-border);
    border-bottom: 1px solid var(--pub-border);
}

/* Small tracked label above a section heading. Same Overline treatment as
   the admin theme's Overline typography and the footer column headings. */
.eyebrow {
    margin: 0 0 var(--pub-space-xs);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--pub-accent);
    max-width: none;
}

.section__intro {
    color: var(--pub-text-muted);
    font-size: var(--pub-body-lg);
    max-width: 52ch;
}

/* ---- Buttons ---- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--pub-space-2xs);
    padding: 0.875rem 1.5rem;
    border-radius: var(--pub-radius-full);
    font-weight: 600;
    font-size: var(--pub-body);
    line-height: 1;
    text-decoration: none;
    white-space: nowrap;
    border: 1px solid transparent;
    transition: box-shadow var(--pub-duration-base) var(--pub-ease),
        border-color var(--pub-duration-fast) var(--pub-ease),
        color var(--pub-duration-fast) var(--pub-ease),
        transform var(--pub-duration-fast) var(--pub-ease);
}

.btn:active {
    transform: translateY(1px);
}

.btn--primary {
    background: var(--pub-accent);
    color: var(--pub-accent-contrast);
}

.btn--primary:hover {
    box-shadow: var(--pub-shadow-glow);
}

.btn--ghost {
    background: transparent;
    color: var(--pub-text);
    border-color: var(--pub-border);
}

.btn--ghost:hover {
    border-color: var(--pub-accent);
    color: var(--pub-accent);
}

/* Readable by screen readers, invisible on screen - for headings that give a
   section a proper landmark name without needing visible text. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---- Card ---- */
.card {
    background: var(--pub-surface-2);
    border: 1px solid var(--pub-border);
    border-radius: var(--pub-radius-md);
    padding: var(--pub-space-md);
}

.card h3 {
    font-size: var(--pub-heading-3);
}

.card p {
    color: var(--pub-text-muted);
}

/* ---- Scroll reveal (task 2.4) ----
   The site's one scroll motif. JS/public.js adds .reveal only to [data-reveal]
   blocks that start below the fold, then .is-visible as each scrolls in - so
   without JS nothing here ever applies. Headings are never marked; only
   content blocks rise in, and siblings stagger via --reveal-i. */
.reveal {
    opacity: 0;
    transform: translateY(16px);
    transition: opacity var(--pub-duration-slow) var(--pub-ease),
        transform var(--pub-duration-slow) var(--pub-ease);
    transition-delay: calc(var(--reveal-i, 0) * 90ms);
}

.reveal.is-visible {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .reveal {
        opacity: 1;
        transform: none;
    }
}

/* ---- Reduced motion ----
   Sitewide default: cuts every animation/transition to effectively nothing
   for anyone who has asked their OS for less motion. Individual components
   (task 2.4's scroll-reveal, PublicLayout's own transitions) can still add
   their own prefers-reduced-motion rules for a *different* reduced state
   (e.g. skip straight to the revealed state instead of merely a fast
   transition to it) — this is the floor everything else stands on. */
@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;
    }
}
