/*
  MT-CalSync hosted - portal stylesheet.

  Every style in the portal lives here, marketing pages and signed-in app alike. There are no <style>
  blocks and no style="" attributes, because the host sends a Content-Security-Policy with no
  'unsafe-inline' (see Program.cs). One inline style would force 'unsafe-inline' back into style-src,
  which re-enables every injected style at once - so the constraint is deliberate, and the utility
  classes at the bottom exist to satisfy it.

  Palette is the Melsson Technology design system: red is the shared action colour across every
  Melsson product, and each product keeps one accent of its own. MT-CalSync's is the indigo it has
  always used. The engine (SelfHost.MT-CalSync) is deliberately NOT re-skinned - it is the
  open-source build a self-hoster downloads, and its look is a parity statement. The consequence to
  know about: the self-host portal is still indigo and system-font while this one is red and Geist.

  Fonts are self-hosted rather than pulled from the Google CDN, because default-src 'self' would block
  the CDN and widening the policy would cost more than the convenience is worth.
*/

/*
  Self-hosted, not the Google CDN: default-src 'self' would block the CDN, and widening the policy to
  admit it costs more than the convenience is worth. All three families are SIL OFL 1.1, so hosting
  the files ourselves is permitted. Sources: DM Sans from Google Fonts, Geist and Geist Mono from
  Vercel's `geist` package.

  Do not remove a file without also removing its @font-face. A missing static file does NOT 404 here:
  it falls through to MVC, hits the authenticated-by-default fallback policy and 302s to /login, so
  each miss returns ~3 KB of login HTML with a 200 on every page view. Measured, not assumed.

  DM Sans is split latin / latin-ext exactly as Google serves it. The unicode-range on each lets the
  browser skip the 31 KB ext file for pages with no accented characters, while still covering the
  European names people put in the signup form. Geist ships one file per family covering both.
*/
@font-face{font-family:"DM Sans";src:url("../fonts/DMSans-Variable.woff2") format("woff2-variations");font-weight:400 700;font-style:normal;font-display:swap;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:"DM Sans";src:url("../fonts/DMSans-Variable-ext.woff2") format("woff2-variations");font-weight:400 700;font-style:normal;font-display:swap;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:"Geist";src:url("../fonts/Geist-Variable.woff2") format("woff2-variations");font-weight:300 900;font-style:normal;font-display:swap}
@font-face{font-family:"Geist Mono";src:url("../fonts/GeistMono-Variable.woff2") format("woff2-variations");font-weight:300 700;font-style:normal;font-display:swap}

:root {
    /* --- type ------------------------------------------------------------------------------- */
    --font-display: "Geist", "DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-body: "DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    /* --- surfaces and ink ------------------------------------------------------------------- */
    --bg: #f6f7f9;
    --bg-soft: #edf0f3;
    --panel: #ffffff;
    --ink: #1f2430;
    --ink-strong: #1a1c1e;
    --body: #4a4f57;
    --muted: #6b7280;
    --faint: #9aa1ab;
    --border: #e3e6ea;
    --border-strong: #cfd4da;

    /* --- action: the Melsson red. Buttons, footer rule, feature checks. ---------------------- */
    --action: #dc1c12;
    --action-hover: #b41610;
    --action-soft: #fef2f1;
    --action-line: #fbd9d6;
    --action-ring: rgba(220, 28, 18, .16);

    /* --- accent: MT-CalSync's own indigo. Eyebrows, active nav, links, focus. ----------------- */
    --brand: #4f46e5;
    --brand-hover: #4338ca;
    --brand-tint: #eef2ff;
    --brand-line: #c7d2fe;
    /* The accent as it appears over the hero photograph, and the one colour on this page whose
       value is set by a contrast measurement rather than by taste: the eyebrow is 12px, so it needs
       4.5:1 against the brightest pixel the scrim lets through, and the darker #a5b4fc managed only
       3.86:1. Anything more saturated fails the same way. */
    --brand-on-dark: #c7d2fe;

    /* --- status: connected / needs attention / failing / paused ------------------------------ */
    --ok: #22a06b;
    --ok-soft: #e7f6ee;
    --ok-line: #b6e2c9;
    --warn: #b45309;
    --warn-soft: #fef6e7;
    --warn-line: #f6ddb0;
    --danger: #d13b40;
    --danger-soft: #fdecec;
    --danger-line: #f5c2c4;
    --off: #6b7280;
    --off-soft: #f1f3f5;
    --off-line: #e3e6ea;

    /* --- shape ------------------------------------------------------------------------------- */
    --radius-sm: 6px;
    --radius: 10px;
    --radius-lg: 14px;
    --radius-pill: 999px;

    /* --- shadows: neutral-tinted, never coloured --------------------------------------------- */
    --shadow-xs: 0 1px 2px rgba(31, 36, 48, .06);
    --shadow-sm: 0 2px 6px rgba(31, 36, 48, .07);
    --shadow-md: 0 8px 24px rgba(31, 36, 48, .08);
    --shadow-lg: 0 18px 44px rgba(31, 36, 48, .12);

    /* --- auth ground -------------------------------------------------------------------------
       The sign-in, sign-up and password-reset cards sit on this rather than on the page background,
       so arriving at a sign-in from the marketing site is visibly the same product. No photograph
       and no scrim here: the ground is flat colour, so its contrast is a fixed number rather than
       something that shifts with a crop. Measured against the brightest pixel it produces
       (rgb 23,46,53), white clears 14.2:1 and the muted label colour 6.4:1. */
    --auth-ground: radial-gradient(900px 460px at 12% -10%, rgba(79, 70, 229, .30), transparent 60%),
                   radial-gradient(760px 420px at 88% 0%, rgba(34, 211, 238, .16), transparent 55%),
                   #16181b;

    /* --- imagery protection --------------------------------------------------------------------
       Held at .78 through the band the headline and lead occupy, easing to .84 at the foot so the
       small print clears too.

       Measured, not chosen, and measured PER ELEMENT. Each hero text box was mapped onto the
       image's actual object-fit:cover crop, the brightest pixel behind that box taken, the scrim
       alpha interpolated at that box's own y, and the ratio computed against the element's real
       computed colour including its alpha. Swept across 320 / 397 / 768 / 1280 / 1920, the worst
       result at any width -

           h1, white                       8.19:1
           lead, white at 82%              6.80:1
           cta-note, white at 72%          6.46:1
           eyebrow, --brand-on-dark        5.08:1

       - against a 4.5:1 bar. The sweep is the point: cover crops a different part of the frame at
       every width, so one measurement at one width proves nothing. That is the trap the sibling
       product hit, with a scrim that passed where it was tested and failed elsewhere.

       An earlier pass here was far darker (h1 at 16.5:1) and the calendar had effectively
       disappeared behind it. Contrast headroom above the bar buys nothing and costs the picture;
       these stops spend it deliberately.

       RE-MEASURE IF THE PHOTOGRAPH IS EVER REPLACED. This is tuned to one image's tonal range, not
       a general-purpose value. */
    --scrim: linear-gradient(180deg,
        rgba(0,0,0,.52) 0%,
        rgba(0,0,0,.62) 16%,
        rgba(0,0,0,.62) 66%,
        rgba(0,0,0,.70) 100%);

    /* --- motion ------------------------------------------------------------------------------ */
    --dur-fast: 120ms;
    --dur-base: 200ms;
    --ease: cubic-bezier(.2, 0, .2, 1);

    /* --- layout ------------------------------------------------------------------------------ */
    --shell: 1060px;
    --shell-narrow: 760px;
}

* { box-sizing: border-box; }

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

body {
    margin: 0;
    font-family: var(--font-body);
    color: var(--body);
    background: var(--bg);
    font-size: 15px;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--brand); }
a:hover { color: var(--brand-hover); }

/* Keyboard focus must stay visible everywhere. Removing outlines is the most common accessibility
   regression in a hand-written stylesheet, so the ring is defined once, globally. The shadow is an
   addition to the outline, never a replacement for it. */
:focus-visible {
    outline: 2px solid var(--action);
    outline-offset: 2px;
    border-radius: 4px;
    box-shadow: 0 0 0 3px var(--action-ring);
}

/* Skip link: present for keyboard and screen-reader users, off-screen until focused. */
.skip {
    position: absolute;
    left: -9999px;
    top: 0;
    background: var(--action);
    color: #fff;
    padding: 10px 16px;
    z-index: 100;
    text-decoration: none;
    border-radius: 0 0 var(--radius) 0;
}
.skip:focus { left: 0; }

/* -- shell ------------------------------------------------------------------------------------- */

.container {
    max-width: var(--shell);
    margin: 0 auto;
    padding: 24px;
}

.narrow { max-width: var(--shell-narrow); }

.section { padding: 16px 0 8px; }

/* -- nav --------------------------------------------------------------------------------------- */

.nav {
    display: flex;
    align-items: center;
    gap: 12px 24px;
    padding: 18px 24px;
    max-width: var(--shell);
    margin: 0 auto;
    flex-wrap: wrap;
}

/* The two groups the markup provides. `.nav-links` taking the free space is what used to be a
   `<span class="spacer">` sibling; margin-right:auto does the same job without an empty element,
   and unlike the spacer it survives wrapping instead of becoming a stray blank row. */
.nav-links {
    display: flex;
    align-items: center;
    gap: 12px 24px;
    flex-wrap: wrap;
    margin-right: auto;
}

.nav-actions {
    display: flex;
    align-items: center;
    gap: 16px;
}

/* An account address is arbitrary length and must not push the sign-out button off the row. */
.nav-who {
    max-width: 22ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.nav .brand {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 17px;
    color: var(--ink);
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 9px;
    letter-spacing: -.03em;
}

.nav .brand-mark { height: 24px; width: auto; display: block; }

.nav a {
    color: var(--muted);
    text-decoration: none;
    font-size: 15px;
    font-weight: 550;
}
.nav a:hover { color: var(--ink); }
.nav a.on { color: var(--ink); padding-bottom: 2px; border-bottom: 2px solid var(--brand); }

/* A specificity fix, not decoration. `.nav a` above is (0,1,1) - one class plus an element - while
   `.btn-primary` is only (0,1,0), so for a CTA that is BOTH a nav link and a button the muted link
   colour won the cascade and the label rendered #6b7280 on #dc1c12: a contrast ratio of 1.03:1,
   which is to say invisible.

   This is not new and the re-skin did not cause it. The live blue build has the same conflict and
   renders the same label grey on #2f6fed at 1.06:1 - it has simply never been legible. Raising
   specificity fixes the cause; !important would fix the symptom and leave the trap in place for the
   next person who adds a button to the nav. These must stay AFTER `.nav a:hover`, which ties them
   on specificity and would otherwise win on source order. */
.nav a.btn { color: var(--ink); }
.nav a.btn-primary, .nav a.btn-primary:hover { color: #fff; }
.nav .brand:hover { color: var(--ink); }

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

.btn {
    display: inline-block;
    padding: 11px 20px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border);
    background: var(--panel);
    color: var(--ink);
    text-decoration: none;
    font-family: var(--font-body);
    font-weight: 600;
    font-size: 15px;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.btn:hover { background: var(--bg-soft); border-color: var(--border-strong); }
.btn:active { transform: scale(.985); }

.btn-primary {
    background: var(--action);
    border-color: var(--action);
    color: #fff;
    box-shadow: none;
}
.btn-primary:hover { background: var(--action-hover); border-color: var(--action-hover); }
.btn-primary:active { transform: scale(.985); }

/* A button is not a link, for colour purposes.
   `a:hover` near the top of this file is (0,1,1) and outranks `.btn`, `.btn-primary` and
   `.btn-danger`, all (0,1,0). Each of those sets a colour at rest but their :hover rules only change
   background and border - so on hover the colour fell through to the link rule and every anchor
   styled as a button turned blue under the cursor. White-on-red became blue-on-red mid-press.

   This is the general case of the nav bug. The `.nav a.btn*` rules above are still needed and are not
   redundant: they fix the REST state, where `.nav a` (0,1,1) also outranks `.btn`. Hover needs a
   pseudo-class of its own to win - `a.btn:hover` is (0,2,1).

   `.hero .btn:not(.btn-primary)` is (0,3,0) and still outranks these, so the hero's outline button
   keeps its white label on hover rather than picking up --ink and vanishing into the dark ground. */
a.btn:hover { color: var(--ink); }
a.btn-primary:hover { color: #fff; }
a.btn-danger:hover { color: var(--danger); }

/* Marketing CTAs only - the hero and the closing row. Forms and the price card keep the standard
   radius so the pill reads as an invitation rather than as the default button shape. */
.btn-pill { border-radius: var(--radius-pill); padding: 14px 28px; font-size: 16px; }

.btn-block { display: block; width: 100%; text-align: center; }

.btn-danger { color: var(--danger); border-color: var(--danger-line); }
.btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); }

/* -- hero -------------------------------------------------------------------------------------- */

.hero {
    position: relative;
    overflow: hidden;
    min-height: 600px;
    display: flex;
    align-items: center;
    text-align: center;
    padding: 0;
    /* Shows through only while the photograph is still loading, so it must be the dark end of the
       image rather than the page background - otherwise the hero flashes white and the white
       headline is invisible for that frame. */
    background: var(--ink-strong);
}

/* Greyscaled and dimmed before the scrim even lands. The photograph is a page of printed calendar,
   which is to say almost entirely white paper - the hardest case for text over an image, and the
   opposite of the sibling product's dark server room. brightness(.58) is what brings the paper down
   far enough for a scrim to finish the job without erasing the picture; see --scrim for the numbers
   the pair of them produce. */
.hero-bg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    filter: grayscale(1) contrast(.94) brightness(.88);
}

.hero-scrim { position: absolute; inset: 0; background: var(--scrim); }

.hero-inner {
    position: relative;
    width: 100%;
    max-width: var(--shell);
    margin: 0 auto;
    padding: 96px 24px;
}

.hero h1 {
    font-size: 64px;
    margin: 0 0 14px;
    letter-spacing: -.045em;
    line-height: 1.12;
    color: #fff;
    font-weight: 600;
}

.hero .lead {
    font-size: 20px;
    color: rgba(255, 255, 255, .82);
    max-width: 640px;
    margin: 0 auto 28px;
}

/* Lifted from the .62 that reads fine on a light page. The faintest text is what sets how dark the
   scrim has to be, so half a stop here buys real visibility in the photograph behind it. */
.hero .cta-note { color: rgba(255, 255, 255, .72); }

/* The secondary CTA sits on the dark ground, so it cannot rely on the default panel background. */
.hero .btn:not(.btn-primary) {
    background: transparent;
    border-color: rgba(255, 255, 255, .45);
    color: #fff;
}
.hero .btn:not(.btn-primary):hover {
    background: rgba(255, 255, 255, .12);
    border-color: #fff;
}

.cta-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

.cta-note { color: var(--muted); font-size: 14px; margin-top: 12px; }

/* -- headings and prose ------------------------------------------------------------------------ */

h1, h2, h3 {
    color: var(--ink);
    font-family: var(--font-display);
    font-weight: 600;
    letter-spacing: -.03em;
}

h1 { font-size: 31px; margin: 0 0 6px; }

h2 { font-size: 26px; margin: 40px 0 6px; }

h3 { font-size: 17px; margin: 0 0 7px; }

/* The eyebrow names the page above its heading. Mono, tracked and uppercase, it is the design
   system's one piece of deliberately non-prose typography. */
.eyebrow {
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--brand);
    margin: 0 0 14px;
}
.hero .eyebrow { color: var(--brand-on-dark); }

.subhead { color: var(--muted); margin: 0 0 8px; }

code {
    background: var(--bg-soft);
    border: 1px solid var(--border);
    padding: 1px 6px;
    border-radius: 5px;
    font-size: 13.5px;
    color: var(--ink);
}

.lede { font-size: 17px; }

/* Figures read as data, not prose: the mono face keeps digits aligned and signals measurement. It
   deliberately does NOT cover the comparison table's cells - those carry sentences, and in mono they
   read as code rather than as an answer. */
.price-card .amount, .kv dd, .stat, code { font-family: var(--font-mono); }

/* -- cards ------------------------------------------------------------------------------------- */

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 18px;
    margin: 28px 0;
}

.card {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 24px;
    background: var(--panel);
    box-shadow: var(--shadow-xs);
    transition: border-color .15s, box-shadow .15s, transform .15s;
}
.card:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}
.card p { margin: 0; color: var(--muted); font-size: 15px; }
.center .grid .card { text-align: left; }

/* -- claim bands (the quiet-by-design and open-source arguments) -------------------------------- */

.claim {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--bg-soft);
    padding: 28px 30px;
    margin: 32px 0;
}
.claim h2 { margin-top: 0; }
.claim ul { margin: 14px 0 0; padding-left: 20px; }
.claim li { margin-bottom: 9px; }
.claim li strong { color: var(--ink); }

/* -- pricing ----------------------------------------------------------------------------------- */

.price-card {
    max-width: 420px;
    margin: 32px auto;
    border: 2px solid var(--action);
    border-radius: var(--radius-lg);
    padding: 34px 32px;
    text-align: center;
    background: var(--panel);
    box-shadow: var(--shadow-lg);
}
.price-card .amount {
    font-size: 50px;
    font-weight: 800;
    color: var(--ink);
    letter-spacing: -1.5px;
    line-height: 1.1;
}
.price-card .per { color: var(--muted); font-size: 15px; }
.price-card .amount .per { font-size: 18px; }
.price-card ul {
    text-align: left;
    list-style: none;
    padding-left: 0;
    color: var(--body);
    font-size: 15px;
    margin: 18px 0;
}
.price-card ul li { position: relative; padding-left: 26px; margin-bottom: 7px; }
.price-card ul li::before {
    content: "\2713";
    position: absolute;
    left: 4px;
    color: var(--action);
    font-weight: 700;
}

/* -- comparison table -------------------------------------------------------------------------- */

.table-wrap { overflow-x: auto; margin: 24px 0; }

table.compare {
    width: 100%;
    border-collapse: collapse;
    font-size: 15px;
    min-width: 560px;
}
table.compare th, table.compare td {
    text-align: left;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
    vertical-align: top;
}
table.compare thead th {
    color: var(--ink);
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: .14em;
    border-bottom: 2px solid var(--border-strong);
}
table.compare tbody th { font-weight: 600; color: var(--ink); }
table.compare td { color: var(--muted); }

.yes { color: var(--ok); font-weight: 700; }
.no { color: var(--muted); }

/* -- forms ------------------------------------------------------------------------------------- */

.form-card {
    max-width: 420px;
    margin: 40px auto;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 32px;
    background: var(--panel);
    box-shadow: var(--shadow-md);
}
.form-card h1 { font-size: 25px; margin: 0 0 6px; }

.field { margin-bottom: 16px; }
.field label {
    display: block;
    font-size: 14px;
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 5px;
}
.field input, .field select, .field textarea {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font-size: 15px;
    font-family: inherit;
    color: var(--ink);
    background: var(--panel);
}
.field input:focus, .field select:focus, .field textarea:focus {
    border-color: var(--ink);
    box-shadow: 0 0 0 3px var(--action-ring);
    outline: none;
}
.field textarea { resize: vertical; font-family: var(--font-mono); font-size: 13px; line-height: 1.5; }
.field .help { font-size: 13px; color: var(--muted); margin: 5px 0 0; }
/* A field whose content is short by nature - a confirmation word, a port number. */
.field.short { max-width: 320px; }

/* Fields side by side. Each child gets a min-width so a row of three collapses to one column on a
   phone rather than squeezing three unusable inputs onto one line. */
.row { display: flex; gap: 18px; flex-wrap: wrap; }
.row > * { flex: 1; min-width: 200px; }
.row > .wide { flex: 2; }

.check { display: flex; align-items: center; gap: 9px; font-weight: 600; color: var(--ink); }
.check input { width: auto; accent-color: var(--brand); }

.alert {
    border-radius: var(--radius-sm);
    padding: 11px 14px;
    font-size: 14.5px;
    margin-bottom: 18px;
    border: 1px solid;
}
.alert-error { background: var(--danger-soft); border-color: var(--danger-line); color: #a32126; }
.alert-ok { background: var(--ok-soft); border-color: var(--ok-line); color: #12704a; }
.alert-info { background: var(--brand-tint); border-color: var(--brand-line); color: #3730a3; }

.form-foot { text-align: center; font-size: 14px; color: var(--muted); margin: 18px 0 0; }
.form-foot a { font-weight: 600; }

/* The auth pages stand alone: one card on the same dark-tinted ground the hero uses, so arriving at
   a sign-in from the marketing site is visibly the same product rather than a different app. */
.auth-page { background: var(--auth-ground); min-height: 100vh; }
.auth-shell { max-width: 420px; margin: 0 auto; padding: 9vh 16px 40px; }
.brandline {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 18px;
    letter-spacing: -.03em;
    color: #fff;
    text-decoration: none;
    margin-bottom: 20px;
}
.brandline img { height: 26px; width: auto; display: block; }
.brandline:hover { color: #fff; }
/* Inside .auth-shell the card is the page, so it drops the outer margin the marketing form-card
   uses to sit inside a container. */
.auth-shell .form-card { margin: 0; }

/* -- signed-in shell --------------------------------------------------------------------------- */

.panel {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--panel);
    padding: 24px 26px;
    margin-bottom: 18px;
}
.panel h2 { margin: 0 0 4px; font-size: 19px; }

.kv { display: grid; grid-template-columns: 190px 1fr; gap: 10px 18px; margin: 16px 0 0; font-size: 15px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; color: var(--ink); }

.pill {
    display: inline-block;
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: .2px;
}
.pill-ok { background: var(--ok-soft); color: var(--ok); }
.pill-warn { background: var(--warn-soft); color: var(--warn); }
.pill-off { background: var(--off-soft); color: var(--muted); }

/* -- signed-in app: state, tables, and the setup guide ------------------------------------------
   These have no counterpart on the marketing pages. They came from the three <style> blocks the
   layouts used to carry, retuned onto the tokens above so the app and the storefront are one
   design rather than two that happen to agree. */

/* A card can carry state. Used for the trial banner, the reconnect prompt and the blocking cards
   the entitlement gate renders. */
.card.ok { border-color: var(--ok-line); background: var(--ok-soft); }
.card.warn { border-color: var(--warn-line); background: var(--warn-soft); }
.card.danger { border-color: var(--danger-line); background: var(--danger-soft); }
/* State cards are statements, not affordances - they must not lift under the cursor like the
   marketing feature cards do. */
.card.ok:hover, .card.warn:hover, .card.danger:hover {
    transform: none;
    box-shadow: var(--shadow-xs);
    border-color: inherit;
}
.card h2 { margin-top: 0; font-size: 19px; }
.card.ok p, .card.warn p, .card.danger p { color: var(--body); }

/* Inline status word: a connection's health, a pair's last run, a subscription's state. */
.badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: var(--radius-pill);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .02em;
    border: 1px solid var(--off-line);
    background: var(--off-soft);
    color: var(--off);
    vertical-align: middle;
    white-space: nowrap;
}
.badge.ok { background: var(--ok-soft); color: var(--ok); border-color: var(--ok-line); }
.badge.warn { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line); }
.badge.danger { background: var(--danger-soft); color: var(--danger); border-color: var(--danger-line); }
.badge.off { background: var(--off-soft); color: var(--off); border-color: var(--off-line); }

/* A row of controls in a table cell must not wrap mid-action; the auto-generated pair names are
   long, so the table scrolls inside its own wrapper rather than pushing the page sideways. */
.btn-sm { padding: 5px 12px; font-size: 13px; border-radius: var(--radius-sm); }
.actions { white-space: nowrap; display: flex; gap: 8px; align-items: center; }
.actions form { display: inline; margin: 0; }

table.data { width: 100%; border-collapse: collapse; font-size: 14.5px; }
table.data th, table.data td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); }
table.data thead th {
    font-family: var(--font-mono);
    font-size: 12px;
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: .12em;
    color: var(--muted);
    border-bottom: 2px solid var(--border-strong);
}
table.data tbody tr:hover td { background: var(--bg-soft); }
table.data tbody tr:last-child td { border-bottom: none; }

/* Diagnostic output - the live credential probe and the sync report. Monospace on near-black so a
   wall of provider text reads as machine output and never as prose. */
pre.report {
    background: var(--ink-strong);
    color: #d8dbe0;
    padding: 16px 18px;
    border-radius: var(--radius);
    overflow: auto;
    font-family: var(--font-mono);
    font-size: 12.5px;
    line-height: 1.55;
}

/* The guided setup checklist on the dashboard. It disappears for good once the first pair exists,
   so the done state is a brief moment rather than a permanent decoration. */
.steps { list-style: none; margin: 16px 0 0; padding: 0; }
.step {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    padding: 16px 0;
    border-top: 1px solid var(--border);
}
.step:first-child { border-top: none; padding-top: 4px; }
.step-num {
    flex: none;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-pill);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-mono);
    font-weight: 500;
    font-size: 13px;
    border: 1px solid var(--border-strong);
    background: var(--panel);
    color: var(--muted);
}
.step.current .step-num { background: var(--action); border-color: var(--action); color: #fff; }
.step.done .step-num { background: var(--ok-soft); border-color: var(--ok-line); color: var(--ok); }
.step-body { flex: 1; min-width: 0; }
.step-title { font-weight: 650; color: var(--ink); }
.step.done .step-title, .step.todo .step-title { color: var(--muted); }
.step-desc { font-size: 14px; color: var(--muted); margin: 2px 0 0; }
.step-cta { margin-top: 10px; }

/* -- footer ------------------------------------------------------------------------------------ */

footer {
    border-top: 3px solid var(--action);
    background: var(--ink-strong);
    margin-top: 96px;
}
footer .container {
    display: flex;
    justify-content: space-between;
    gap: 16px 22px;
    flex-wrap: wrap;
    align-items: center;
    color: rgba(255, 255, 255, .72);
    font-size: 14px;
    padding: 22px 24px;
}
footer a { color: rgba(255, 255, 255, .72); text-decoration: none; }
footer a:hover { color: #fff; }

/* The mark and the copyright are one unit; keeping them in a flex box stops a wrap landing between
   them, which is what put "Pricing" on the same line as the copyright and the year on its own. */
.footer-brand { display: flex; align-items: center; gap: 10px; }
.footer-brand img { height: 26px; width: auto; display: block; flex: none; }

.footer-links { display: flex; flex-wrap: wrap; gap: 8px 20px; align-items: center; }

/* -- legal pages ------------------------------------------------------------------------------- */

.legal h1 { font-size: 31px; margin-bottom: 4px; }
.legal h2 { font-size: 19px; margin: 28px 0 4px; }
.legal p, .legal li { font-size: 15px; }
.legal code { font-size: 13px; }

/* -- utilities (these exist because inline styles are forbidden; see the header) ---------------- */

.center { text-align: center; }
.muted { color: var(--muted); }
.small { font-size: 14px; }
.tiny { font-size: 13px; }
.strong { color: var(--ink); font-weight: 650; }
.stack { margin: 32px 0; }
.stack-lg { margin: 48px 0; }
.tight { margin-top: 0; }
.no-bottom { margin-bottom: 0; }
.nowrap { white-space: nowrap; }
.right { text-align: right; }
.inline { display: inline; margin: 0; }
.self-end { align-self: flex-end; }
.stack-sm { margin: 12px 0 0; }
/* Auto-generated pair names are long and have no spaces to break at. */
.break { word-break: break-word; }
/* A wide table scrolls inside its own box; the page body never scrolls sideways. */
.scroll-x { overflow-x: auto; }
/* A one-line clarification under a section heading, tucked up against it. */
.hint { color: var(--muted); font-size: 14px; margin: -4px 0 14px; }
.w-check { width: 40px; }
/* Stat tiles: one figure per card, in the mono face because it is a measurement. */
.stat { font-family: var(--font-mono); font-size: 30px; font-weight: 500; color: var(--ink); line-height: 1.2; }

/* Checkboxes are not text inputs and must not stretch to the field width. */
input[type=checkbox], input[type=radio] { width: auto; accent-color: var(--brand); }

/* Stripe mounts its card fields into this element, so it has to look like one of our inputs
   without being one - Stripe owns everything inside it. */
.card-element {
    padding: 11px 12px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--panel);
}

/* -- responsive -------------------------------------------------------------------------------- */

@media (max-width: 640px) {
    .hero { min-height: 420px; }
    .hero-inner { padding: 56px 20px; }
    .hero h1 { font-size: 34px; letter-spacing: -.03em; }
    .hero .lead { font-size: 17px; }
    h1 { font-size: 26px; }
    h2 { font-size: 22px; }
    .container { padding: 20px 16px; }
    .row { gap: 0; }
    .kv { grid-template-columns: 1fr; gap: 2px 0; }
    .kv dd { margin-bottom: 10px; }
    .form-card, .price-card { padding: 24px 20px; }

    /* Footer stacks: identity, then links. space-between on one line is meaningless once wrapped. */
    footer { margin-top: 64px; }
    footer .container { flex-direction: column; align-items: flex-start; gap: 16px; }
    .footer-links { gap: 10px 18px; }

    /* Marketing pills are full-width targets rather than two odd-sized lozenges. */
    .cta-row { flex-direction: column; align-items: stretch; }
    .cta-row .btn { text-align: center; }

    /* The comparison table fits the column rather than scrolling inside it. Its 560px min-width is
       wider than any phone, so on a 375px screen it scrolled sideways inside a 343px column and the
       Hosted column - the one this page exists to show - started off-screen, with nothing to say it
       was there. Measured on this page with these rules: 137/126/80px columns at 375px wide and
       115/93/80px at 320px, no overflow at either. The label column is capped so the answers keep
       room to wrap. */
    table.compare { min-width: 0; font-size: 14px; }
    table.compare th, table.compare td { padding: 10px 8px; }
    table.compare thead th { font-size: 11px; letter-spacing: .06em; }
    table.compare tbody th { width: 40%; }
}

/* ── the nav gets its own breakpoint, and it is wider than the one above ──────────────────────────
   Measured, not guessed. Laid out on one row this nav needs 774px: 726px of content plus the 24px
   padding on each side, measured in the browser with the real fonts loaded. At the 640px breakpoint
   everything else uses there would be a dead band from 641px to ~774px - ordinary tablet widths -
   where the desktop rule still applied and the row wrapped on its own, putting brand and links on
   line one and marooning the CTA alone on line two. That is the same defect this block exists to
   fix, at a width nobody thinks to check.

   820px gives headroom over the measured 774 for font fallback, which is wider than Geist, and for
   the longest link string ("Self-host or hosted"). Note this is deliberately NOT the 760px the
   sibling product uses - its nav is a different width, and copying the number rather than repeating
   the measurement would have reopened the dead band here. If a nav item is ever added, re-measure:
   the number to beat is the nav's one-row width, not a round number that looks tablet-ish. */
@media (max-width: 820px) {
    /* Two deliberate rows. Row one is the brand and the one action worth reaching for; row two is
       navigation. Previously this was a single wrapping row, so the break landed wherever the text
       ran out - typically mid-link-list, with the CTA marooned on the second line at full size. */
    .nav { gap: 12px 16px; padding: 12px 16px; }
    .nav .brand { font-size: 16px; }
    .nav-actions { order: 1; margin-left: auto; }
    .nav-links {
        order: 2;
        flex: 1 0 100%;      /* claims a whole row, so the break is here and nowhere else */
        margin-right: 0;
        gap: 6px 18px;
        font-size: 14px;
    }
    /* The nav CTA is a compact control at this size, not the marketing pill. */
    .nav-actions .btn { padding: 9px 16px; font-size: 14px; }
    .nav-who { display: none; }   /* the account page states who you are signed in as */
}

/* Respect a reduced-motion preference: the only motion here is card hover, but honouring the setting
   costs three lines and is the difference between "accessible" and "nearly accessible". */
@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; }
}
