/*
 * FUTURE PROOF MY BIZ — BRAND TOKENS
 * Night Shift system. Spec §2.1.
 *
 * Canonical reference: public/nightshift/FPMB_NightShift_Full.html
 * Full rules:          public/nightshift/FPMB_Brand_Guide_NightShift.html
 *
 * Every surface in the platform consumes these tokens. A future re-skin has to be a
 * change to this file, not a rewrite of the pages — which is the whole reason the
 * previous Gunmetal system came out in one commit instead of thirty.
 *
 * The one belief: the business never closes. Everything here serves that. The dark
 * room is the night shift your AI works; the light room is the morning you wake up to.
 */

:root {
    /* ---------------------------------------------------------- dark room
       70% of the platform. Near-black with a slight blue-violet bias, so the
       violet gradients sit on it rather than on top of it. */
    --ink:        #0B0C10;   /* page ground */
    --ink-2:      #12141A;   /* raised: nav, cards, chips */
    --ink-3:      #1A1D26;   /* raised again: inputs, hover, table stripe */
    --hairline:   rgba(255, 255, 255, .09);
    --hairline-2: rgba(255, 255, 255, .22);

    /* text ramp */
    --tx:   #F2F3EE;
    --tx-2: #B9BDC4;
    --tx-3: #8B8F98;

    /* ---------------------------------------------------------- accents
       Proportion is 70 ink / 22 violet / 8 lime. Lime is scarce on purpose:
       one primary CTA per screen, live and success states, positive money,
       and Leader rank. Nothing else. */
    --lime:     #D6FF4B;
    --lime-dim: #9DBF2E;
    --lime-ink: #1E2405;   /* text placed on a lime field */

    /* urgency, HOT LEAD, Director rank, and the "You" cursor */
    --hot: #FFB443;

    /*
     * Gradient depth only. Never a link, never a state, never a UI colour.
     *
     * Three stops, because the slide gradient in the canonical reference has three.
     * They live here rather than inline in a stylesheet so that violet can only ever
     * enter the codebase through a token the CI blue check knows about.
     */
    --violet:      #3D2E86;
    --violet-2:    #4B39A8;
    --violet-deep: #1A1440;

    /* The slide card ground, assembled once. */
    --grad-slide: radial-gradient(120% 120% at 50% 0%, var(--violet) 0%, var(--violet-deep) 45%, #0D0B18 100%);

    /* errors and clawbacks only */
    --danger: #FF6B5A;

    /* ---------------------------------------------------------- light room
       The morning. Plans, receipts, anything a person reads in daylight. */
    --paper:      #F5F4EE;
    --paper-card: #FFFFFF;
    --paper-line: #E1DFD2;
    --ptx:        #181A16;
    --ptx-2:      #5E6156;
    --ptx-3:      #8A8D7E;
    --lime-ptx:   #7A8B1E;  /* lime that survives on paper */

    /* ---------------------------------------------------------- type
       Three voices, and they do not swap jobs.
       display  Unbounded 700-900, UPPERCASE ONLY, never below 16px, never paragraphs
       body/UI  Figtree 400-700, including slide titles and component labels
       data     Spline Sans Mono 500-600, times, IDs, row amounts, status */
    --disp: 'Unbounded', system-ui, sans-serif;
    --body: 'Figtree', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --mono: 'Spline Sans Mono', ui-monospace, 'SF Mono', Menlo, monospace;

    --w-med:   500;
    --w-semi:  600;
    --w-bold:  700;
    --w-black: 900;

    /*
     * Night Shift specifies a .26em kicker above headlines. The operator has ruled
     * eyebrows out of this platform in every form, twice, and again when adopting
     * this system, so that token does not exist here. The mono voice keeps its other
     * jobs: times, IDs, amounts in rows, and status.
     */
    --track-data: .12em;

    --t-xs:  12px;
    --t-sm:  13px;
    --t-base: 15px;
    --t-md:  16.5px;
    --t-lg:  18px;
    --t-xl:  22px;

    /* ---------------------------------------------------------- geometry
       Radii carry meaning here. Buttons are pills, cards are 20, the slide
       cards that hold live UI are 26, the light sections that break the night
       are 38, and an app window inside a vignette is 16 because that is what
       an app window is. */
    --r-pill:   999px;
    --r-card:   20px;
    --r-slide:  26px;
    --r-light:  38px;
    --r-window: 16px;
    --r-chip:   12px;
    --r-sm:     10px;

    --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
    --sp-5: 22px; --sp-6: 30px; --sp-7: 44px; --sp-8: 64px; --sp-9: 96px;

    --measure: 66ch;

    /* ---------------------------------------------------------- motion
       Travel is how things move across the screen. Arrivals overshoot slightly,
       because something landing should feel like it landed. */
    --ease-travel:  cubic-bezier(.22, .8, .22, 1);
    --ease-arrive:  cubic-bezier(.2, .9, .3, 1.3);
    --dur-fast: 180ms;
    --dur:      320ms;
    --dur-slow: 650ms;

    --glow-lime: 0 0 34px rgba(214, 255, 75, .35);
    --glow-text: 0 0 34px rgba(214, 255, 75, .45);
    --lift:      0 18px 40px rgba(24, 26, 22, .1);
    --lift-dark: 0 40px 110px rgba(30, 20, 80, .55);
}

/* -------------------------------------------------------------------- faces
   Self-hosted woff2, latin subset, per §2.1. No font CDN in production: a brand
   whose headline face fails to load is a brand that looks broken, and a third
   party deciding when that happens is not a risk worth taking for one request. */
@font-face {
    font-family: 'Unbounded';
    src: url('/assets/fonts/unbounded.woff2') format('woff2');
    font-weight: 200 900;
    font-display: swap;
}
@font-face {
    font-family: 'Figtree';
    src: url('/assets/fonts/figtree.woff2') format('woff2');
    font-weight: 300 900;
    font-display: swap;
}
@font-face {
    font-family: 'Spline Sans Mono';
    src: url('/assets/fonts/spline-sans-mono.woff2') format('woff2');
    font-weight: 300 700;
    font-display: swap;
}

/* ---------------------------------------------------------------- baseline */

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

html { scroll-behavior: smooth; }

body {
    margin: 0;
    background: var(--ink);
    color: var(--tx);
    font-family: var(--body);
    font-size: var(--t-base);
    line-height: 1.55;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    /* Tabular numerals platform-wide. Money that jitters as it updates reads as
       money you cannot trust. */
    font-variant-numeric: tabular-nums;
}

::selection { background: var(--lime); color: var(--ink); }

/* No italics anywhere. If a face has no italic the browser will slant it, which
   looks like a bug; this makes the rule explicit rather than accidental. */
i, em, cite, address, dfn, var { font-style: normal; }

img, svg, video { max-width: 100%; }

a { color: var(--lime); text-decoration: none; }
a:hover { color: var(--lime); }

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

/* ------------------------------------------------------------------- type */

/*
 * Display is uppercase or it is not display. The face has no lowercase register
 * that works at these weights, and the guide is explicit: UPPERCASE ONLY, never
 * below 16px, never a paragraph.
 */
h1, h2, .display {
    font-family: var(--disp);
    font-weight: var(--w-black);
    text-transform: uppercase;
    line-height: 1.04;
    margin: 0;
    text-wrap: balance;
}

h3, h4, .display-sm {
    font-family: var(--disp);
    font-weight: var(--w-bold);
    text-transform: uppercase;
    line-height: 1.15;
    margin: 0;
}

h1 { font-size: clamp(32px, 5.6vw, 68px); }
h2 { font-size: clamp(28px, 4.4vw, 50px); }
h3 { font-size: clamp(19px, 2.4vw, 26px); }
h4 { font-size: var(--t-md); }

p { margin: 0; }

/* The lime word inside a headline. One per headline, never two. */
.hl { color: var(--lime); text-shadow: var(--glow-text); }

/* Data voice: times, IDs, amounts in rows, status text. */
.data {
    font-family: var(--mono);
    font-weight: var(--w-semi);
    font-size: var(--t-xs);
    letter-spacing: var(--track-data);
}

.lead { color: var(--tx-2); font-size: var(--t-md); max-width: 560px; }

/* ---------------------------------------------------------------- buttons */

/*
 * Pills, per §2.1. Lime is the single primary action on a screen; everything
 * else is the outline. A screen with two lime buttons has no primary action.
 */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    font-family: var(--body);
    font-weight: var(--w-bold);
    font-size: var(--t-base);
    border-radius: var(--r-pill);
    padding: 15px 26px;
    /*
     * A border that is there but cannot be seen.
     *
     * The outlined variant draws a real 1.5px edge, so without this a filled button and
     * an outlined one sitting next to each other are three pixels different in height
     * and the row of them reads as slightly crooked. Reserving the edge on every button
     * makes the box the same whichever variant is painted into it.
     */
    border: 1.5px solid transparent;
    cursor: pointer;
    text-decoration: none;
    transition: transform var(--dur-fast) var(--ease-travel),
                background var(--dur-fast) linear,
                border-color var(--dur-fast) linear;
}

.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn--lime {
    background: var(--lime);
    color: var(--ink);
    box-shadow: var(--glow-lime);
}
.btn--lime:hover { color: var(--ink); }

.btn--line {
    background: transparent;
    color: var(--tx);
    border: 1.5px solid var(--hairline-2);
}
.btn--line:hover { border-color: var(--lime); color: var(--lime); }

/* On paper. The dark fill is the primary there, because lime on white is illegible. */
.btn--ink   { background: var(--ink); color: var(--tx); }
.btn--quiet { background: #EFEDE2; color: var(--ptx); }

.btn--sm { padding: 11px 18px; font-size: var(--t-sm); }
.btn--full { width: 100%; }

/*
 * Not yet.
 *
 * This existed in the old sheet and did not survive the Night Shift reskin, so every
 * .btn.is-disabled in the app has been rendering as a bright, fully clickable button
 * that does nothing. pointer-events matters as much as the opacity: aria-disabled tells
 * a screen reader, and nothing at all was telling a mouse.
 */
.btn.is-disabled {
    opacity: .4;
    pointer-events: none;
    box-shadow: none;
    filter: none;
}

/* ------------------------------------------------------------------ chips */

/*
 * Status pills. The shape carries the state as much as the colour does, so a
 * screenshot in greyscale still tells you which row needs attention.
 */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--mono);
    font-weight: var(--w-semi);
    font-size: 9.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    border-radius: var(--r-pill);
    padding: 4px 10px;
    background: var(--ink-3);
    color: var(--tx-3);
    white-space: nowrap;
}

.chip--live   { background: rgba(214, 255, 75, .14); color: var(--lime); }
.chip--live::before { content: '●'; font-size: 8px; }
.chip--hot    { background: var(--hot); color: #2A1800; }
.chip--danger { background: rgba(255,107,90,.15); color: var(--danger); }
.chip--paper  { background: #EDF6D2; color: #4E6212; }

/*
 * Status, in the same shape as a chip. Views written against the old system use
 * .state; it is the same component, so it is the same rule rather than a copy of one.
 */

.state {
    display: inline-flex; align-items: center; gap: 6px;
    font-family: var(--mono); font-weight: var(--w-semi); font-size: 9.5px;
    letter-spacing: .1em; text-transform: uppercase;
    border-radius: var(--r-pill); padding: 4px 10px;
    background: var(--ink-3); color: var(--tx-3); white-space: nowrap;
}

/*
 * Tinted, never filled, because a filled pill is a button.
 *
 * These were solid lime and solid amber, which is the same shape, the same fill and
 * the same contrast as the primary button sitting a few pixels away. On My AI that put
 * four unpressable lime pills, DONE, DONE, READY and LIVE, next to the one pill on the
 * page that actually did something, and the page read as six buttons.
 *
 * Colour still carries the meaning, which is why it stays on the text. What goes is the
 * solid fill, which is the part that says press me. `--fail` was already built this way
 * and was the only one that read correctly, so the rest now match it.
 */
.state--ok      { background: rgba(214, 255, 75, .14); color: var(--lime); }
.state--caution { background: rgba(255, 180, 67, .16); color: var(--hot); }
.state--fail    { background: rgba(255, 107, 90, .16); color: var(--danger); }
.state--info    { background: var(--ink-3); color: var(--tx-3); }

/* Used on the admin surfaces and never defined, so it fell back to the plain grey. */
.state--warn    { background: rgba(255, 180, 67, .16); color: var(--hot); }

/* ------------------------------------------------------------------ money */

/*
 * Positive money is lime, money leaving is plain, money owed is danger. Nothing
 * else on the platform gets to use lime for a number.
 */
.money      { font-variant-numeric: tabular-nums; font-family: var(--mono); font-weight: var(--w-semi); }
.money--in  { color: var(--lime); }
.money--out { color: var(--tx-2); }
.money--bad { color: var(--danger); }

/* Hero money: display face, black weight, lime, glowing. */
.money-hero {
    font-family: var(--disp);
    font-weight: var(--w-black);
    color: var(--lime);
    text-shadow: var(--glow-text);
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

/* ------------------------------------------------------------------ ranks */

.rank        { font-family: var(--mono); font-weight: var(--w-semi); letter-spacing: var(--track-data); text-transform: uppercase; }
.rank--member  { color: var(--tx-3); }
.rank--builder  { color: var(--lime); }
.rank--architect{ color: var(--hot); }

/* --------------------------------------------------------------- surfaces */

.card {
    background: var(--ink-2);
    border: 1px solid var(--hairline);
    border-radius: var(--r-card);
    padding: var(--sp-6);
}

/* The light room, dropped into the night as a rounded slab. */
.paper {
    background: var(--paper);
    color: var(--ptx);
    border-radius: var(--r-light);
    margin: 0 12px;
    padding: var(--sp-9) var(--sp-5);
}

.paper h1, .paper h2, .paper h3, .paper h4 { color: var(--ptx); }
.paper p { color: var(--ptx-2); }
.paper .card { background: var(--paper-card); border: 1.5px solid var(--paper-line); color: var(--ptx); }

.wrap { max-width: 1040px; margin: 0 auto; padding: 0 var(--sp-5); }
.wrap--narrow { max-width: 760px; }

/*
 * There is no .kick / .eyebrow / .eb class in this system, deliberately. A headline
 * carries its own section. Two CI tests hold the line: one scans the templates for
 * the class names, one scans rendered copy for the tracked-caps label pattern.
 */

/* -------------------------------------------------------------- reduced motion
   Mandatory on every animated surface per §2.1. Declared once, here, so no page
   can forget it. */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        animation-delay: 0s !important;
        transition-duration: .01ms !important;
    }
}
