/* ============================================================================
   MagnetERP — magneterp.com marketing site
   ----------------------------------------------------------------------------
   DIRECTION CONTRACT (read before editing; the render is audited against it)

   THESIS      The page IS the document the system produces. Magnet's only honest
               proof is structural — PRODUCT.md forbids fabricated testimonials,
               client logos, benchmarks and usage claims — so this site
               DEMONSTRATES posting rather than describing it. It refuses the SaaS
               default it cannot fill truthfully: tilted dashboard screenshot,
               logo wall, three icon-heading-text cards, pricing table.
   OWN-WORLD   Published-accounts typography drenched over Ledger Blue. TWO grounds
               only — paper white and Ledger Blue Deep — and no cards anywhere.
               Rules do the work borders normally do, including a real accounting
               DOUBLE RULE under every total. Figures are tabular-lining, right
               aligned, and they foot. Red is ledger rule / credit ink only.
   STORY       A Nigerian SME finance manager sees this is the whole back office in
               one book, believes it by watching one invoice post to stock, the
               customer account, the project and the general ledger with the
               figures agreeing, and acts by writing to info@magneterp.com.
   1ST VIEWPORT Full-bleed Ledger Blue, TWO columns from 1100px. Left: wordmark
               small, tagline, the statement heading and its lead, primary action
               under it. Right, and inside the first screen: the posting trace —
               the sales invoice as a white PAPER SHEET on the field, four
               destination records beside it, joined by leader rules that draw
               themselves once. Below 1100px the statement stacks over the trace.
   DOCUMENTS   The site's proof device throughout (2026-09-11): typeset paper
               documents the system produces — invoice, customer account, stock
               card, pay run, trial balance — each labelled an illustration and
               each footing. Never a screenshot of the software.
   FORM        Multi-part posting document + published-accounts typography.

   ----------------------------------------------------------------------------
   THIS IS THE ONLY STYLESHEET. Bootstrap was removed deliberately (8.4 MB of
   unused assets against a product principle that names low bandwidth a
   REQUIREMENT, not polish). Section components use the primitives below; they do
   not invent new colour or type values. To re-theme, edit the token block only.
   ============================================================================ */

/* ---------------------------------------------------------------------------
   1. TOKENS
   The Ledger Blue family is copied from the product's single source of truth,
   Magnet/wwwroot/css/theme.css. The One Accent Rule holds here: no accent
   outside this family. Two stops are ADDED for this surface only (--field-deep
   and the on-field inks) because a marketing page drenches colour at page scale
   where the app only ever accents it. Both are stops in the same family.
   --------------------------------------------------------------------------- */
/* LIGHT AND DARK.
   Both modes define the SAME token names, so no component knows which mode it is
   in — a rule that reads var(--paper) is correct in both, and there is no dark
   variant of any component anywhere in this file.

   THE HARD PART, and the thing to preserve if you edit this: the entire design
   rests on TWO ALTERNATING GROUNDS — paper and the Ledger Blue field. Dark mode
   has to keep them reading as two. So --paper goes NEUTRAL dark and --field goes
   SATURATED dark: the blue stays visibly blue against it, the rhythm of the
   scroll survives, and the page does not collapse into one flat slab.

   Mode is chosen by data-theme on <html>, set before first paint by the inline
   script in App.razor and stored in localStorage under `magnet.theme`. With no
   stored choice the OS preference decides, via the block at the end of §9. */
:root,
[data-theme="light"] {
    color-scheme: light;

    /* Ledger Blue — the product accent */
    --accent: #2563EB;
    --accent-bright: #60A5FA;
    --accent-600: #1D4ED8;
    --accent-press: #1E40AF;
    --accent-deep: #1E3A8A;
    --accent-soft: #EEF2FE;
    --accent-line: #C7D6F5;

    /* The two grounds. Everything on this site sits on one or the other.
       --paper is NOT pure white: people read this page for a while, and #ffffff
       at full display brightness is the thing they turn dark mode on to escape. */
    --paper: #FCFDFF;
    --field: #1E3A8A;      /* drenched ground — white on this is 10.3:1        */
    --field-deep: #112455; /* the closing stop: the foot of every field sheet,
                              and the footer's flat ground. White on it is 14.9:1. */
    /* The wash that lights the field. One more stop in the same family:
       --accent-bright at low alpha, laid over the ground from above the
       top-left corner. At its strongest the lit ground still holds white at
       7.7:1 and --on-field-soft at 5.3:1. */
    --field-glow: rgba(96, 165, 250, 0.26);

    /* Ink on paper */
    --ink: #0f172a;        /* headings                        — 17.7:1 on paper */
    --text: #334155;       /* body                            — 10.7:1 on paper */
    --strong: #475569;     /* secondary                       —  7.3:1 on paper */
    --muted: #5F6E82;      /* tertiary, the floor             —  5.2:1 on paper */
    --line: #E7ECF3;       /* hairline rules                                    */
    --line-firm: #cbd5e1;  /* column-head and total rules                       */

    /* The mark's three rings and its letter. Named separately rather than reusing --ink and
       --line-firm because the RUN has to invert: on paper the dots darken outward, on the dark
       ground they brighten outward. Borrowing the neutrals would put a dark inner ring on a dark
       page, where it simply disappears. (light) */
    --mark-r1: #CBD5E1;
    --mark-r2: #1E3A8A;
    --mark-r3: #0F172A;
    --mark-m:  #0F172A;

    /* Ink on field — tinted from the hue, never grey (craft floor) */
    --on-field: #ffffff;      /* 10.3:1 on --field */
    --on-field-soft: #C7D6F5; /*  7.1:1 on --field */
    --on-field-rule: rgba(199, 214, 245, 0.28);
    /* The eyebrow blue on dark grounds — the same sky-blue step the dark-theme paper eyebrow
       uses (--accent-600 there). One value in BOTH themes, because its ground (the field) stays
       dark in both: 4.8:1 on this --field, 6.8:1 on the dark one — passes the small-text floor. */
    --on-field-accent: #7DB4FB;

    /* The light control that sits ON the field (.btn--paper). It needs its own
       tokens rather than reusing --paper: in dark mode a --paper button on a
       --field sheet would be dark-on-dark and vanish, so this pair stays light
       in BOTH modes and keeps the call to action popping off the blue. */
    --cta-bg: #FFFFFF;
    --cta-bg-hover: #EEF2FE;
    --cta-ink: #1E40AF;

    /* Text on a filled accent button. White in BOTH modes — a filled button stays
       saturated in dark, so its label must not follow --paper down into the dark.
       Deliberately NOT overridden in the dark block; it inherits from here. */
    --on-accent: #FFFFFF;

    /* The second ink. Ledger convention: red rules the totals and marks the
       credit side. It decorates nothing. Both stops are the product's own
       documented reds (theme.css --danger-strong / --danger-border). */
    --rule-red: #b91c1c;         /* 6.4:1 on paper */
    --credit-on-field: #fecaca;  /* ~8:1 on --field  */

    /* Type */
    --face: Heebo, "Segoe UI", system-ui, -apple-system, sans-serif;
    --face-mark: CACNormHeavy, Heebo, "Segoe UI", system-ui, sans-serif;

    --t-display: clamp(2.5rem, 6.4vw, 4.5rem);
    --t-display-beside: clamp(2.6rem, 4.4vw, 3.9rem); /* the statement when it shares the screen with the trace */
    --t-h2: clamp(1.75rem, 3.4vw, 2.5rem);
    --t-h2-quiet: clamp(1.5rem, 2.6vw, 1.95rem);   /* the short sections' step */
    --t-h3: clamp(1.15rem, 1.8vw, 1.4rem);
    --t-lead: clamp(1.08rem, 1.6vw, 1.3rem);
    --t-body: 1.0625rem;
    --t-small: 0.9375rem;
    --t-label: 0.72rem;
    --t-mark: 1.35rem;   /* the wordmark step — this surface only */

    /* Rhythm. More space above a heading than below it, everywhere. */
    --s-1: 8px;
    --s-2: 12px;
    --s-3: 16px;
    --s-4: 24px;
    --s-5: 40px;
    --s-6: 64px;
    --s-7: 96px;
    --wrap: 1180px;

    /* The DESIGN.md radius scale, declared rather than assumed. */
    --r-sm: 9px;
    --r-md: 11px;
    --r-lg: 16px;
    --r-pill: 999px;

    /* Motion — one strong curve, per the design-engineering floor. The built-in
       CSS easings are too weak; ease-in is never used. */
    --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
    --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

    /* How long a mode change takes. Opt-in — see §9. */
    --theme-swap: 220ms;

    /* Print ink. The print block forces every sheet to dark-on-white whatever theme the
       visitor was in; this is the ONE ink that must not follow the theme, so it is its
       own token (the product's slate ink) rather than --ink, which goes light in dark mode. */
    --print-ink: #0f172a;

    /* The document sheet's lift off its ground (§6c). The product's Feather shadow
       on paper; a deeper, still soft, fall on the blue field where a sheet has to
       read as a physical object lying on a dark desk. */
    --shadow-sheet: 0 10px 30px rgba(16, 24, 40, 0.06);
    --shadow-sheet-on-field: 0 28px 56px -28px rgba(6, 12, 40, 0.65), 0 2px 6px rgba(6, 12, 40, 0.18);
}

/* ---------------------------------------------------------------------------
   1b. DARK
   Not an inversion. Inverting this page would give a black ground with glaring
   white type, which is worse to read for a while than the light theme it
   replaced — and it would flatten the two grounds into one.

   --paper goes NEUTRAL dark, --field stays visibly BLUE. That is the whole
   trick: the alternating rhythm of paper sheet / field sheet is the page's
   structure, and it has to survive the switch.

   The accent lifts a step, because #2563EB on a near-black ground is muddy and
   fails on small text. Ink stops short of #ffffff for the same reason the light
   ground is not pure white.
   --------------------------------------------------------------------------- */
[data-theme="dark"] {
    color-scheme: dark;

    --accent: #60A5FA;
    --accent-bright: #93C5FD;
    --accent-600: #7DB4FB;   /* hover LIGHTENS on dark — the mirror of the light rule */
    --accent-press: #2563EB; /* filled buttons — white text ≈ 4.9:1                   */
    --accent-deep: #1D4ED8;
    --accent-soft: #16233A;  /* a blue-tinted raise, not a pale wash */
    --accent-line: #2C4370;

    --paper: #10161F;        /* neutral dark — the "paper" sheets  */
    --field: #16274F;        /* still unmistakably blue against it */
    --field-deep: #0D1730;
    --field-glow: rgba(96, 165, 250, 0.18); /* lighter hand on a dark ground */

    --ink: #E9EEF7;          /* 15.3:1 on paper */
    --text: #C3CDDC;         /* 11.3:1 on paper */
    --strong: #A8B4C6;       /*  8.4:1 on paper */
    --muted: #8C99AD;        /*  6.1:1 on paper */
    --line: #212A36;
    --line-firm: #2F3A4A;

    /* The mark's three rings and its letter. Named separately rather than reusing --ink and
       --line-firm because the RUN has to invert: on paper the dots darken outward, on the dark
       ground they brighten outward. Borrowing the neutrals would put a dark inner ring on a dark
       page, where it simply disappears. (dark) */
    --mark-r1: #3E5C93;
    --mark-r2: #60A5FA;
    --mark-r3: #E9EEF7;
    --mark-m:  #E9EEF7;

    --on-field: #FFFFFF;        /* 14.5:1 on --field */
    --on-field-soft: #BACBEC;   /*  8.6:1 on --field */
    --on-field-rule: rgba(186, 203, 236, 0.24);
    --on-field-accent: #7DB4FB; /*  6.8:1 on --field; = this theme's --accent-600 */

    /* Stays light in dark mode, on purpose: this is the button on the blue
       field, and a dark button there would disappear into its own ground. */
    --cta-bg: #DCE7FB;
    --cta-bg-hover: #C4D6F6;
    --cta-ink: #0E1A34;

    --rule-red: #F87171;         /* 6.4:1 on dark paper */
    --credit-on-field: #FCA5A5;
}

/* ---------------------------------------------------------------------------
   1c. THE OPERATING SYSTEM'S PREFERENCE
   Applies only while the visitor has made no explicit choice. `:not([data-theme])`
   is what makes the toggle authoritative — someone who picks light on a machine
   set to dark keeps light, on this site and on every return visit.
   The values are the dark block above, repeated because CSS custom properties
   cannot be aliased across selectors without an indirection that would show up
   in every DevTools inspection of every token.
   --------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) {
        color-scheme: dark;

        --accent: #60A5FA;
        --accent-bright: #93C5FD;
        --accent-600: #7DB4FB;
        --accent-press: #2563EB;
        --accent-deep: #1D4ED8;
        --accent-soft: #16233A;
        --accent-line: #2C4370;

        --paper: #10161F;
        --field: #16274F;
        --field-deep: #0D1730;
        --field-glow: rgba(96, 165, 250, 0.18); /* lighter hand on a dark ground */

        --ink: #E9EEF7;
        --text: #C3CDDC;
        --strong: #A8B4C6;
        --muted: #8C99AD;
        --line: #212A36;
        --line-firm: #2F3A4A;

        /* The mark's three rings and its letter. Named separately rather than reusing --ink and
           --line-firm because the RUN has to invert: on paper the dots darken outward, on the dark
           ground they brighten outward. Borrowing the neutrals would put a dark inner ring on a dark
           page, where it simply disappears. (system dark) */
        --mark-r1: #3E5C93;
        --mark-r2: #60A5FA;
        --mark-r3: #E9EEF7;
        --mark-m:  #E9EEF7;

        --on-field: #FFFFFF;
        --on-field-soft: #BACBEC;
        --on-field-rule: rgba(186, 203, 236, 0.24);
        --on-field-accent: #7DB4FB;

        --cta-bg: #DCE7FB;
        --cta-bg-hover: #C4D6F6;
        --cta-ink: #0E1A34;

        --rule-red: #F87171;
        --credit-on-field: #FCA5A5;
    }
}

/* The wordmark face carries the product name and nothing else (One Family Rule).
   Everything else on this site is Heebo. */
@font-face {
    font-family: CACNormHeavy;
    src: url("fonts/CACNormHeavy.ttf") format("truetype");
    font-weight: 400 900;
    font-display: swap;
}

/* ---------------------------------------------------------------------------
   2. RESET
   --------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

/* The masthead is sticky, so an anchor jump would otherwise land its target
   underneath it. Every element that can be linked to keeps the bar's 60px plus a
   breath clear above it. */
[id] { scroll-margin-top: 76px; }

/* The page ground is the FIELD, not paper.
   The masthead detaches at 8px of scroll, but the hero sheet does not reach the
   top of the viewport until you have scrolled its own height — so for that first
   moment the strip above and beside the floating bar showed the page ground, and
   a paper-coloured ground put a mismatched band across the top of every page.
   Every page on this site opens on a .sheet--field (checked: Home, About,
   ModulesIndex, ModuleDetail, NotFound, Error), so the field colour is the right
   ground everywhere and the gap now reads as part of the hero.
   Nothing else is affected: the sheets are opaque and full-bleed, so the body
   background is only ever visible in that gap and in overscroll. */
body {
    margin: 0;
    background: var(--field);
    color: var(--text);
    font-family: var(--face);
    font-size: var(--t-body);
    line-height: 1.6;
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; color: inherit; }

/* The focus ring is the same promise the product makes: 2px accent, 2px offset,
   on :focus-visible, everywhere, no exceptions. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.on-field :focus-visible,
.sheet--field :focus-visible,
.sheet--deep :focus-visible { outline-color: var(--accent-bright); }

/* Skip link — the first tab stop on every page. It borrows the light CTA pair
   (--cta-bg / --cta-ink) rather than --accent-press on --paper: in dark mode
   that pairing fell to 3.5:1, and a control the keyboard meets first must not be
   the one place contrast slips. The CTA pair stays high-contrast in both modes. */
.skip {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    background: var(--cta-bg);
    color: var(--cta-ink);
    padding: var(--s-2) var(--s-3);
    font-weight: 700;
}
.skip:focus { left: var(--s-3); top: var(--s-3); }

/* ---------------------------------------------------------------------------
   3. STRUCTURE
   A sheet is a full-bleed horizontal band of the document. There are no cards
   on this site: a sheet either sits on paper or on the Ledger Blue field, and
   rules separate what borders would otherwise box.
   --------------------------------------------------------------------------- */
/* A paper sheet STATES its own ground. It must never inherit one from <body>:
   body is the field colour (so the gap around the floating masthead reads as
   part of the hero), and a sheet without this line renders blue with dark ink
   on it — which is exactly what happened when body changed. */
.sheet {
    padding: var(--s-7) var(--s-4);
    position: relative;
    background-color: var(--paper);
    color: var(--text);
}
.sheet--tight { padding-block: var(--s-6); }

/* On a phone the section rhythm has to come down. At 96px top and bottom, two
   stacked sections put 192px of empty ground between the last line of one and the
   first of the next — on a ~800px viewport that is a quarter of the screen showing
   nothing. The desktop rhythm is right for a wide page and too loose for a narrow
   one, so it steps down rather than being reduced everywhere. */
@media (max-width: 760px) {
    .sheet { padding-block: var(--s-6); }
    .sheet--tight { padding-block: var(--s-5); }
}

/* The field is LIT, not flat. A soft wash falls from beyond the top-left corner
   and the ground settles toward --field-deep at the foot, so a band drenched at
   page scale gains depth without leaving the Ledger Blue family or picking up a
   second hue. Every stop is a token.
   Two things this arrangement protects, and the reasons not to reverse it:
   - The TOP stop is --field, which is also <body>, so the strip beside the
     floating masthead still matches the hero exactly (see the body rule in 00a72).
   - The wash PEAKS OFF-CANVAS, above the top edge, so no line of text ever sits in it at full
     strength. That is the safety margin that lets the glow be seen at all: at full strength on
     paper-weight text --on-field-soft would land near the 4.5:1 floor, and off-canvas it does not.
   - Contrast only ever IMPROVES downward, and the closing sheet hands off to the
     footer's --field-deep with no seam. Lifting the bottom instead would break
     both. */
.sheet--field {
    background-color: var(--field);
    background-image:
        radial-gradient(135% 100% at 8% -14%, var(--field-glow), transparent 66%),
        linear-gradient(180deg, var(--field) 0%, var(--field-deep) 100%);
    color: var(--on-field);
}
.sheet--deep {
    background: var(--field-deep);
    color: var(--on-field);
}
.sheet--field a:not(.btn),
.sheet--deep a:not(.btn) { color: var(--on-field); }

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

/* ---------------------------------------------------------------------------
   4. TYPE
   --------------------------------------------------------------------------- */

/* The eyebrow is the product's page-header signature, inherited here so the
   site and the app open the same way: uppercase label, accent dot, then title. */
.eyebrow {
    display: flex;
    align-items: center;
    gap: var(--s-1);
    font-size: var(--t-label);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent-600);
    margin: 0 0 var(--s-3);
}
.eyebrow::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.14);
    flex: none;
}
/* On the dark grounds the eyebrow TEXT takes --on-field-accent (owner decision, 2026-08-09):
   the same sky-blue the dark-theme paper eyebrow shows, so the signature stays blue on every
   ground. (History: --accent-line read dim on the light-theme field, and in dark theme it
   doubles as a hairline-border tone that made the label near-invisible; a white pass was tried
   and the owner chose the blue.) The DOT keeps --accent-bright — it is a graphic, where 3:1
   applies. */
.sheet--field .eyebrow,
.sheet--deep .eyebrow { color: var(--on-field-accent); }
.sheet--field .eyebrow::before,
.sheet--deep .eyebrow::before {
    background: var(--accent-bright);
    box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.2);
}

/* The statement heading takes Heebo's heaviest cut. One weight step above the h2
   is what separates the page's thesis from its section titles — the sizes alone
   were doing all the work before. */
.display {
    font-size: var(--t-display);
    font-weight: 900;
    line-height: 1.03;
    letter-spacing: -0.03em;
    text-wrap: balance;
    color: var(--ink);
    margin: 0;
}
.sheet--field .display,
.sheet--deep .display { color: var(--on-field); }

.h2 {
    font-size: var(--t-h2);
    font-weight: 800;
    line-height: 1.12;
    letter-spacing: -0.02em;
    text-wrap: balance;
    color: var(--ink);
}
.sheet--field .h2,
.sheet--deep .h2 { color: var(--on-field); }

/* The quieter section title — one step under .h2 — for the short sections
   (multi-currency, the people band, find us), so the scroll has peaks and rests
   instead of every section arriving at the same volume. */
.h2--quiet { font-size: var(--t-h2-quiet); }

.h3 {
    font-size: var(--t-h3);
    font-weight: 700;
    line-height: 1.25;
    letter-spacing: -0.01em;
    color: var(--ink);
}
.sheet--field .h3,
.sheet--deep .h3 { color: var(--on-field); }

.lead {
    font-size: var(--t-lead);
    line-height: 1.5;
    color: var(--strong);
    max-width: 62ch;
}
.sheet--field .lead,
.sheet--deep .lead { color: var(--on-field-soft); }

/* Body copy holds a 65–75ch measure. */
.prose { max-width: 68ch; }
.prose p + p { margin-top: var(--s-3); }
.sheet--field .prose,
.sheet--deep .prose { color: var(--on-field-soft); }

.small {
    font-size: var(--t-small);
    color: var(--strong);
}
.sheet--field .small,
.sheet--deep .small { color: var(--on-field-soft); }

/* A caption that names a demonstration as a demonstration. Every illustrative
   figure on this site carries one — PRODUCT.md's no-fabricated-proof rule means
   example data must never be mistakable for a customer's. */
.synthetic {
    font-size: 0.8125rem;
    letter-spacing: 0.02em;
    color: var(--muted);
    max-width: 64ch;   /* a caption is read, not scanned — it keeps a reading measure */
}
.sheet--field .synthetic,
.sheet--deep .synthetic { color: var(--on-field-soft); opacity: 0.85; }

/* ---------------------------------------------------------------------------
   5. LEDGER MARKS
   The vocabulary an accountant reads without being taught: column heads ruled
   underneath, figures in tabular lining numerals, a single rule above a total
   and a DOUBLE rule beneath it, red for the credit side.
   --------------------------------------------------------------------------- */
.fig {
    font-variant-numeric: tabular-nums lining-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    text-align: right;
    white-space: nowrap;
}
.credit { color: var(--rule-red); }
/* The credit ink lifted for the blue field. --danger-border is the product's own
   light-red step (theme.css); on --field it reads at ~8:1. */
.sheet--field .credit,
.sheet--deep .credit { color: var(--credit-on-field); }

.colhead {
    font-size: var(--t-label);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    padding-bottom: var(--s-1);
    border-bottom: 1px solid var(--line-firm);
}
.sheet--field .colhead,
.sheet--deep .colhead {
    color: var(--on-field-soft);
    border-bottom-color: var(--on-field-rule);
}

.rule {
    border: 0;
    border-top: 1px solid var(--line);
    margin: 0;
}
.rule--firm { border-top-color: var(--line-firm); }
.sheet--field .rule,
.sheet--deep .rule { border-top-color: var(--on-field-rule); }

/* The double rule. A total is closed with it in every set of published accounts,
   and nothing else on this site uses it. */
.rule--double {
    border: 0;
    border-top: 1px solid var(--rule-red);
    border-bottom: 1px solid var(--rule-red);
    height: 3px;
    margin: 0;
}
.sheet--field .rule--double,
.sheet--deep .rule--double {
    border-top-color: var(--accent-bright);
    border-bottom-color: var(--accent-bright);
}

/* ---------------------------------------------------------------------------
   6. THE SCHEDULE
   The site's list form, used wherever a lesser page would reach for a grid of
   identical cards. It is a ruled schedule: label left, definition centre,
   reference right — the shape a statement note actually takes.
   --------------------------------------------------------------------------- */
.schedule { border-top: 1px solid var(--line-firm); }
.sheet--field .schedule,
.sheet--deep .schedule { border-top-color: var(--on-field-rule); }

.schedule__row {
    display: grid;
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr) auto;
    gap: var(--s-3) var(--s-5);
    align-items: baseline;
    padding: var(--s-4) 0;
    border-bottom: 1px solid var(--line);
}
.sheet--field .schedule__row,
.sheet--deep .schedule__row { border-bottom-color: var(--on-field-rule); }

.schedule__name {
    font-weight: 700;
    color: var(--ink);
    letter-spacing: -0.01em;
    font-size: var(--t-body);
}
.sheet--field .schedule__name,
.sheet--deep .schedule__name { color: var(--on-field); }

/* The note holds a reading measure even when the row is the full wrap wide — on a
   1180px row the centre column would otherwise run past 100 characters a line. */
.schedule__note { color: var(--text); max-width: 66ch; }
.sheet--field .schedule__note,
.sheet--deep .schedule__note { color: var(--on-field-soft); }

.schedule__ref {
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--muted);
    font-variant-numeric: tabular-nums lining-nums;
}

/* A live row tints rather than growing a coloured bar down its edge — a thick
   accent border on a list item is the decoration this direction refuses.

   ONLY ROWS THAT ACTUALLY GO SOMEWHERE TINT. `:has(a)` is doing real work here:
   a tint on a row whose only link is a few words inside it promises a click
   target across the whole line that is not there, and the pointer lands on dead
   space. Rows with no link stay inert, which is honest.

   `:focus-within` gives the keyboard the same signal the mouse gets — without
   it, tabbing through a schedule highlights nothing but the small link itself.
   It sits outside the hover media query on purpose: keyboard users exist on
   touch devices too. */
/* Square corners on purpose: this is a band across a ruled schedule, and a
   rounded pill sitting on hairline rules reads as a chip that wandered in from
   another design. */
.schedule__row {
    margin-inline: calc(var(--s-3) * -1);
    padding-inline: var(--s-3);
    transition: background-color 180ms var(--ease-out);
}

.schedule__row:has(a):focus-within { background: var(--accent-soft); }

.sheet--field .schedule__row:has(a):focus-within,
.sheet--deep .schedule__row:has(a):focus-within { background: rgba(255, 255, 255, 0.055); }

@media (hover: hover) and (pointer: fine) {
    .schedule__row:has(a):hover { background: var(--accent-soft); }

    .sheet--field .schedule__row:has(a):hover,
    .sheet--deep .schedule__row:has(a):hover { background: rgba(255, 255, 255, 0.055); }
}
/* Same reason as the eyebrow: 0.8125rem is small text, so it takes the 7.1:1 step. */
.sheet--field .schedule__ref,
.sheet--deep .schedule__ref { color: var(--accent-line); }

/* A two-column schedule — name and note, no reference (the About commitments). */
.schedule--two .schedule__row { grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); }

/* Narrow: the name and the reference share the first line, the note runs the full
   width beneath. The areas are ASSIGNED — auto-placement dropped the reference to
   a third line, under the note, where it read as a stray figure. */
@media (max-width: 760px) {
    .schedule__row {
        grid-template-columns: minmax(0, 1fr) auto;
        gap: var(--s-1) var(--s-3);
    }
    .schedule__name { grid-area: 1 / 1; }
    .schedule__ref  { grid-area: 1 / 2; justify-self: end; }
    .schedule__note { grid-area: 2 / 1 / 3 / -1; }
    .schedule--two .schedule__row { grid-template-columns: minmax(0, 1fr); }
}

/* ---------------------------------------------------------------------------
   6b. PLATES — a framed photograph with its caption
   The site's only framed object. Everything else sits directly on the page; a
   photograph earns a frame because it brings its own edges and needs separating
   from the paper it lands on.

   HISTORY, so nobody rebuilds it: this began as a reserved-space device holding
   room for product screenshots, with crop marks and an "awaiting artwork" state.
   The owner dropped that idea — the site shows no screenshots — so everything
   except the parts a real photograph uses has been removed.
   --------------------------------------------------------------------------- */
.plate { margin: 0; }

.plate__area {
    position: relative;
    aspect-ratio: 16 / 10;
    background: var(--accent-soft);
    border-radius: var(--r-lg);
    overflow: hidden;
}
.sheet--field .plate__area,
.sheet--deep .plate__area { background: rgba(255, 255, 255, 0.06); }

.plate__area > img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

.plate figcaption {
    margin-top: var(--s-2);
    font-size: 0.8125rem;
    color: var(--muted);
}
.sheet--field .plate figcaption,
.sheet--deep .plate figcaption { color: var(--on-field-soft); }

/* ---------------------------------------------------------------------------
   6c. THE DOCUMENT SHEET
   The second framed object on the site (the photograph plate is the first), and
   the proof device the whole page rests on: a typeset paper document the system
   produces — the invoice in the hero, the customer account, stock card, pay run
   and trial balance in "A closer look". It is deliberately a SHEET and not a
   card: it holds one document, it is built from the ledger marks above (column
   heads, tabular figures, a single rule above a total and a double rule beneath
   it), and its ground is always --paper so it reads as paper whether it lies on
   the blue field or on the paper sheet — in dark mode the neutral-dark paper
   still separates from the saturated field, which is the whole point of the two
   grounds (§1b). Every sheet carries a .synthetic caption beside it; the figures
   are examples and they foot.
   --------------------------------------------------------------------------- */
.doc {
    background: var(--paper);
    color: var(--text);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    padding: var(--s-4);
    box-shadow: var(--shadow-sheet);
    min-width: 0;
}
.sheet--field .doc,
.sheet--deep .doc {
    border-color: transparent;
    box-shadow: var(--shadow-sheet-on-field);
}

/* Head: the document's title and its reference — ruled underneath the way every
   printed form rules its heading off from its body. Title left, reference right
   while the sheet is wide enough; on a narrow sheet the reference drops to its
   own line under the title rather than squeezing it (a grid with an auto column
   did exactly that in the hero's 300px invoice). */
.doc__head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    gap: 2px var(--s-3);
    padding-bottom: var(--s-2);
    border-bottom: 1px solid var(--line-firm);
}
.doc__title {
    font-size: var(--t-h3);
    font-weight: 800;
    letter-spacing: -0.015em;
    line-height: 1.15;
    color: var(--ink);
    white-space: nowrap;
}
.doc__ref {
    font-size: 0.8125rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums lining-nums;
}

/* Two-column lines: description left, figure right. A <dl> in the invoice, so
   the pairs stay pairs for a screen reader. */
.doc__lines {
    margin: var(--s-3) 0 0;
    display: grid;
    gap: 6px;
}
.doc__line {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--s-3);
    align-items: baseline;
    font-size: var(--t-small);
}
.doc__line dt { color: var(--text); min-width: 0; }
.doc__line dd { margin: 0; color: var(--ink); }

.doc__rule {
    border: 0;
    border-top: 1px solid var(--line-firm);
    margin: var(--s-3) 0;
}
.doc__lines--sum { margin-top: 0; }
.doc__lines--total { margin-top: var(--s-2); }
.doc__lines--total .doc__line {
    font-size: var(--t-body);
    font-weight: 700;
}
.doc__lines--total dt { color: var(--ink); }
.doc .rule--double { margin-top: var(--s-2); }

/* Multi-column documents — a stock card, a pay run, a trial balance — are real
   tables. Column heads are ruled; the total row takes the single rule above and
   the accounting double rule (a genuine CSS `double` border) beneath. A six-column
   register is wider than a phone, so the table scrolls inside its own sheet — the
   page body never scrolls sideways. */
.doc__scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    margin-inline: calc(var(--s-4) * -1);
    padding-inline: var(--s-4);
}
.doc__table {
    width: 100%;
    margin-top: var(--s-3);
    border-collapse: collapse;
    font-size: 0.875rem;
    line-height: 1.35;
}
.doc__table th {
    text-align: left;
    font-size: var(--t-label);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
    padding: 0 0 var(--s-1);
    border-bottom: 1px solid var(--line-firm);
    vertical-align: bottom;
}
.doc__table td {
    padding: 7px 0;
    vertical-align: baseline;
    color: var(--text);
    border-bottom: 1px solid var(--line);
}
.doc__table th + th,
.doc__table td + td { padding-left: var(--s-3); }
.doc__table th.fig,
.doc__table td.fig { text-align: right; }
.doc__table td.fig { color: var(--ink); }
.doc__table tbody tr:last-child td { border-bottom: 0; }
.doc__table tfoot td {
    font-weight: 700;
    color: var(--ink);
    border-top: 1px solid var(--line-firm);
    border-bottom: 3px double var(--rule-red);
    padding: var(--s-2) 0;
}
.doc__table tfoot td + td { padding-left: var(--s-3); }
.doc__table .doc__sub { color: var(--muted); font-weight: 400; }

/* A one-line remark beneath a document body — the balance carried, the
   employer's own contribution — set small and quiet. */
.doc__note {
    margin-top: var(--s-2);
    font-size: 0.8125rem;
    color: var(--muted);
    max-width: 64ch;   /* a footnote keeps a reading measure on a wide sheet */
}

/* ---------------------------------------------------------------------------
   7. CONTROLS
   Filled controls deepen on hover, never brighten — the product's rule.
   --------------------------------------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-1);
    padding: 0.85rem 1.4rem;
    border-radius: 9px;
    border: 1px solid transparent;
    font-size: var(--t-small);
    font-weight: 700;
    letter-spacing: 0.01em;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 160ms var(--ease-out),
                border-color 160ms var(--ease-out),
                color 160ms var(--ease-out),
                transform 160ms var(--ease-out);
}
.btn:active { transform: scale(0.97); }

.btn--primary {
    background: var(--accent-press);
    color: var(--on-accent);
}
.btn--ghost {
    background: var(--paper);
    border-color: var(--line-firm);
    color: var(--strong);
}
/* The light control on the blue field. Uses its own tokens, not --paper: see the
   note beside --cta-bg. It stays light in dark mode so the call to action keeps
   popping off the field instead of sinking into it. */
.btn--paper {
    background: var(--cta-bg);
    color: var(--cta-ink);
}
.btn--outline {
    background: transparent;
    border-color: var(--on-field-rule);
    color: var(--on-field);
}

@media (hover: hover) and (pointer: fine) {
    .btn--primary:hover { background: var(--accent-deep); }
    /* --accent-soft, not a literal near-white: in dark mode a #f8fafc hover would
       flash a near-white slab on a dark button. */
    .btn--ghost:hover { background: var(--accent-soft); color: var(--ink); }
    .btn--paper:hover { background: var(--cta-bg-hover); }
    .btn--outline:hover { border-color: var(--on-field-soft); background: rgba(255, 255, 255, 0.08); }
}

/* A text link that reads as a reference in the document, not a button. */
.link {
    color: var(--accent-press);
    font-weight: 700;
    text-decoration: none;
    border-bottom: 1px solid var(--accent-line);
    padding-bottom: 1px;
    transition: border-color 160ms var(--ease-out), color 160ms var(--ease-out);
}
.sheet--field .link,
.sheet--deep .link {
    color: var(--on-field);
    border-bottom-color: var(--accent-bright);
}
@media (hover: hover) and (pointer: fine) {
    .link:hover { border-bottom-color: currentColor; }
}

/* ---------------------------------------------------------------------------
   8. THE WORDMARK
   --------------------------------------------------------------------------- */
.mark {
    font-family: var(--face-mark);
    font-size: var(--t-mark);
    letter-spacing: 0.01em;
    line-height: 1;
    color: var(--ink);
    text-decoration: none;
    display: inline-flex;
    align-items: baseline;
    gap: 0.28em;
    white-space: nowrap;
}
.mark em {
    font-style: normal;
    color: var(--accent);
}
.on-field .mark,
.sheet--field .mark,
.sheet--deep .mark { color: var(--on-field); }
.on-field .mark em,
.sheet--field .mark em,
.sheet--deep .mark em { color: var(--accent-bright); }

/* ---------------------------------------------------------------------------
   9. MOTION
   The site has ONE authored moment — the posting trace drawing itself — and a
   quiet reveal grammar around it. Everything here is PROGRESSIVE ENHANCEMENT:
   the hidden start states are scoped to `html.js`, a class set by an inline
   script before first paint. If the script never runs, or fails, or the visitor
   blocks it, every word on this site is visible at full opacity. That is not a
   nicety — low bandwidth is a product REQUIREMENT (PRODUCT.md), and content
   that depends on JavaScript to become visible is content that can vanish.
   --------------------------------------------------------------------------- */

/* Cross-document view transitions. Fifteen static pages that share a masthead
   and a footer; without this every navigation is a white flash, with it the
   chrome holds still while the document changes underneath. Browsers that do
   not support it simply navigate as before. */
@view-transition { navigation: auto; }

::view-transition-old(root) { animation-duration: 140ms; }
::view-transition-new(root) { animation-duration: 220ms; }

/* The masthead and footer persist across the transition instead of cross-fading
   with the page body. */
.site-header { view-transition-name: site-header; }
.site-footer { view-transition-name: site-footer; }

@media (prefers-reduced-motion: no-preference) {
    /* Scroll reveal. Enters from an already-settled default — a short rise and
       a fade, never a scale from nothing. */
    html.js .reveal {
        opacity: 0;
        transform: translateY(16px);
    }

    html.js .reveal.is-in {
        opacity: 1;
        transform: none;
        transition: opacity 620ms var(--ease-out), transform 620ms var(--ease-out);
        transition-delay: var(--reveal-delay, 0ms);
    }
}

/* Reduced motion means fewer and gentler animations, not none: the opacity
   change still aids comprehension, the movement goes. */
@media (prefers-reduced-motion: reduce) {
    html.js .reveal { opacity: 0; }
    html.js .reveal.is-in {
        opacity: 1;
        transform: none;
        transition: opacity 240ms ease;
    }

    @view-transition { navigation: none; }
}

/* ---------------------------------------------------------------------------
   9b. THE MODE TRANSITION
   Opt-in, and live only for the ~420ms site.js keeps `.theme-anim` on <html>.

   Permanent colour transitions would be wrong twice over: every page load would
   fade its own colours in as the stylesheet lands, which reads as a rendering
   fault rather than a design; and a standing transition on `color` makes every
   hover feel a beat late.

   The broad selector and !important are the point — for the length of the swap
   this must beat each component's own transition, then get out of the way
   completely. site.js does not add the class at all under reduced motion: the
   mode still changes, it simply arrives rather than travels. A theme switch is
   exactly the kind of full-screen colour movement that provokes symptoms in
   someone with a vestibular disorder.
   --------------------------------------------------------------------------- */
html.theme-anim,
html.theme-anim *,
html.theme-anim *::before,
html.theme-anim *::after {
    transition:
        background-color var(--theme-swap) ease,
        border-color var(--theme-swap) ease,
        color var(--theme-swap) ease,
        fill var(--theme-swap) ease,
        stroke var(--theme-swap) ease,
        box-shadow var(--theme-swap) ease !important;
    transition-delay: 0s !important;
}

/* Photographs are exempt: fading a picture's colours during a theme change
   looks like a decoding error, not a transition. */
html.theme-anim img,
html.theme-anim iframe {
    transition: none !important;
}

/* ---------------------------------------------------------------------------
   9c. THE MODE TOGGLE
   Hidden until `html.js` is present, because a control that cannot work without
   JavaScript should not be offered. Two glyphs live inside it and CSS picks
   which to show, so changing mode costs no re-render on a statically rendered
   page: the moon means "go dark", the sun means "go light".
   --------------------------------------------------------------------------- */
.theme-toggle { display: none; }

html.js .theme-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    flex: none;
    padding: 0;
    border: 1px solid var(--line-firm);
    border-radius: var(--r-sm);
    background: var(--paper);
    color: var(--strong);
    cursor: pointer;
    transition: background-color 160ms var(--ease-out),
                border-color 160ms var(--ease-out),
                color 160ms var(--ease-out),
                transform 160ms var(--ease-out);
}

.theme-toggle:active { transform: scale(0.94); }

@media (hover: hover) and (pointer: fine) {
    .theme-toggle:hover {
        background: var(--accent-soft);
        border-color: var(--accent-line);
        color: var(--accent-press);
    }
}

.theme-toggle svg { width: 17px; height: 17px; display: block; }

/* Light mode offers the moon; dark mode offers the sun. */
.theme-toggle .tt-sun { display: none; }
.theme-toggle .tt-moon { display: block; }

[data-theme="dark"] .theme-toggle .tt-sun { display: block; }
[data-theme="dark"] .theme-toggle .tt-moon { display: none; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) .theme-toggle .tt-sun { display: block; }
    :root:not([data-theme]) .theme-toggle .tt-moon { display: none; }
}

/* ---------------------------------------------------------------------------
   10. PRINT
   The jotter is a printed piece and this site is its companion; a visitor who
   prints a page should get the document, not a screenshot of a website.
   --------------------------------------------------------------------------- */
@media print {
    .sheet--field,
    .sheet--deep {
        background: #fff !important;
        color: var(--print-ink) !important;
    }
    .sheet--field *,
    .sheet--deep * { color: var(--print-ink) !important; }
    .site-header,
    .site-footer nav,
    .btn { display: none !important; }
    .sheet { padding: var(--s-4) 0; break-inside: avoid; }
    a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 0.75em; }
}

/* ---------------------------------------------------------------------------
   9d. THE MARK
   The dot figure beside the wordmark, drawn inline so its fills are tokens.

   THE TRANSITION IS THE POINT. Because the rings are painted from custom
   properties, flipping the theme animates the fill from one value to the next
   instead of swapping one image for another — the mark changes colour with the
   page rather than blinking. `fill` is an animatable property; two swapped image
   elements cross-fading could never do this without doubling the request count.
   --------------------------------------------------------------------------- */
.mark {
    align-items: center;   /* was baseline: the mark is a shape, not a letter */
}

.mark__mk {
    width: 1.85em;         /* relative to the wordmark, so they scale together */
    height: 1.85em;
    flex: none;
    display: block;
    overflow: visible;
}

.mark__word {
    display: inline-flex;
    align-items: baseline;
}

.mk-r1 circle { fill: var(--mark-r1); }
.mk-r2 circle { fill: var(--mark-r2); }
.mk-r3 circle { fill: var(--mark-r3); }

.mk-m {
    fill: var(--mark-m);
    font-family: var(--face-mark);
}

/* Every painted part of the mark eases between themes on the same curve. */
.mk-r1 circle,
.mk-r2 circle,
.mk-r3 circle,
.mk-m {
    transition: fill 260ms var(--ease-out);
}

/* A theme change is information, not decoration — but the movement still goes
   when the visitor has asked for less of it. */
@media (prefers-reduced-motion: reduce) {
    .mk-r1 circle,
    .mk-r2 circle,
    .mk-r3 circle,
    .mk-m { transition: none; }
}

@media (max-width: 620px) {
    /* Tighter beside a smaller wordmark, and the inner ring is a speck at this
       size anyway — the shape still reads from the outer two. */
    .mark__mk { width: 1.6em; height: 1.6em; }
}
