/* Theme tokens.

   Light sits on bare :root, so a browser reporting no colour preference lands somewhere
   readable rather than on a dark value over a light ground. Dark is then layered on twice:
   once for the system setting, once for an explicit choice, so the toggle wins in both
   directions. Style components through these tokens only - a colour defined solely inside
   a media query never applies when the system reports no preference. */
:root {
    --nx-bg: #f5f7fb;
    --nx-panel: #ffffff;
    --nx-panel-2: #eef2f8;
    --nx-border: #dde4ef;
    --nx-border-strong: #c3cddc;
    --nx-text: #0f1722;
    --nx-muted: #5a6675;
    --nx-primary: #2f62c9;
    --nx-primary-hover: #244fa8;
    --nx-danger: #d13340;
    --nx-radius: 14px;
    --nx-radius-sm: 9px;
    --nx-shadow: 0 10px 28px -18px rgba(15, 23, 34, .28);
    --nx-shadow-sm: 0 1px 2px rgba(15, 23, 34, .12);
    --nx-shadow-hover: 0 18px 38px -20px rgba(15, 23, 34, .32);
    --nx-grad: linear-gradient(135deg, #2f62c9, #6d3fd4);

    --nx-on-accent: #ffffff;
    --nx-accent-soft: rgba(47, 98, 201, .09);
    --nx-accent-line: rgba(47, 98, 201, .28);
    --nx-accent-line-soft: rgba(47, 98, 201, .20);
    --nx-accent-ring: rgba(47, 98, 201, .20);
    --nx-accent-text: #234480;
    --nx-accent-ink: #2f62c9;

    --nx-glow-1: rgba(47, 98, 201, .07);
    --nx-glow-2: rgba(109, 63, 212, .05);
    --nx-topbar-bg: rgba(255, 255, 255, .80);
    --nx-overlay-bg: rgba(255, 255, 255, .90);
    --nx-row-hover: rgba(15, 23, 34, .03);
    --nx-badge-bg: #e4e9f2;

    --nx-cloud-grad: linear-gradient(160deg, #e8effb, #f7f9fd);
    --nx-cloud-border: #c2d0e8;
    --nx-cloud-shadow: rgba(47, 98, 201, .28);
    --nx-feature-border: #a9c0e8;
    --nx-support-grad: linear-gradient(160deg, rgba(47, 98, 201, .06), var(--nx-panel));

    --nx-btn-shadow: rgba(47, 98, 201, .35);
    --nx-btn-shadow-hover: rgba(47, 98, 201, .45);

    --nx-danger-text: #b3202c;
    --nx-danger-soft: rgba(209, 51, 64, .09);
    --nx-danger-line: rgba(209, 51, 64, .32);
    --nx-danger-line-strong: rgba(209, 51, 64, .55);

    --nx-ok-text: #1c6b45;
    --nx-ok-soft: rgba(28, 107, 69, .10);
    --nx-ok-line: rgba(28, 107, 69, .30);

    --nx-warn-text: #9a5a00;
    --nx-warn-soft: rgba(196, 122, 0, .11);
    --nx-warn-line: rgba(196, 122, 0, .38);

    --nx-progress-text: #6a3fb5;
    --nx-progress-soft: rgba(106, 63, 181, .09);
    --nx-progress-line: rgba(106, 63, 181, .30);

    --nx-select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%235a6675' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");

    /* The map control icons are dark artwork; they only need inverting on a dark panel. */
    --nx-map-icon-filter: none;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
    --nx-bg: #0a0e16;
    --nx-panel: #111825;
    --nx-panel-2: #172030;
    --nx-border: #212b3b;
    --nx-border-strong: #2c3a4d;
    --nx-text: #eaf0f7;
    --nx-muted: #8b98ac;
    --nx-primary: #5b8def;
    --nx-primary-hover: #74a1ff;
    --nx-danger: #ef4d5a;
    --nx-shadow: 0 12px 34px -14px rgba(0, 0, 0, .6);
    --nx-shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
    --nx-shadow-hover: 0 20px 44px -18px rgba(0, 0, 0, .7);
    --nx-grad: linear-gradient(135deg, #5b8def, #8b5cf6);

    --nx-on-accent: #ffffff;
    --nx-accent-soft: rgba(91, 141, 239, .12);
    --nx-accent-line: rgba(91, 141, 239, .30);
    --nx-accent-line-soft: rgba(91, 141, 239, .22);
    --nx-accent-ring: rgba(91, 141, 239, .22);
    --nx-accent-text: #cfe0ff;
    --nx-accent-ink: #bcd0f7;

    --nx-glow-1: rgba(91, 141, 239, .12);
    --nx-glow-2: rgba(139, 92, 246, .10);
    --nx-topbar-bg: rgba(10, 14, 22, .72);
    --nx-overlay-bg: rgba(10, 14, 22, .82);
    --nx-row-hover: rgba(255, 255, 255, .02);
    --nx-badge-bg: #2c384a;

    --nx-cloud-grad: linear-gradient(160deg, #223050, #171f30);
    --nx-cloud-border: #2b3b5c;
    --nx-cloud-shadow: rgba(91, 141, 239, .50);
    --nx-feature-border: #34496b;
    --nx-support-grad: linear-gradient(160deg, rgba(91, 141, 239, .08), var(--nx-panel));

    --nx-btn-shadow: rgba(91, 141, 239, .70);
    --nx-btn-shadow-hover: rgba(91, 141, 239, .80);

    --nx-danger-text: #ff9aa2;
    --nx-danger-soft: rgba(239, 77, 90, .12);
    --nx-danger-line: rgba(239, 77, 90, .35);
    --nx-danger-line-strong: rgba(239, 77, 90, .60);

    --nx-ok-text: #7ad9a6;
    --nx-ok-soft: rgba(52, 199, 123, .13);
    --nx-ok-line: rgba(52, 199, 123, .34);

    --nx-warn-text: #f2b263;
    --nx-warn-soft: rgba(242, 178, 99, .13);
    --nx-warn-line: rgba(242, 178, 99, .38);

    --nx-progress-text: #c3a6f5;
    --nx-progress-soft: rgba(166, 125, 240, .14);
    --nx-progress-line: rgba(166, 125, 240, .38);

    --nx-select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b98ac' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");

    --nx-map-icon-filter: invert(1) hue-rotate(180deg) brightness(1.6);
    }
}

:root[data-theme="dark"] {
    --nx-bg: #0a0e16;
    --nx-panel: #111825;
    --nx-panel-2: #172030;
    --nx-border: #212b3b;
    --nx-border-strong: #2c3a4d;
    --nx-text: #eaf0f7;
    --nx-muted: #8b98ac;
    --nx-primary: #5b8def;
    --nx-primary-hover: #74a1ff;
    --nx-danger: #ef4d5a;
    --nx-shadow: 0 12px 34px -14px rgba(0, 0, 0, .6);
    --nx-shadow-sm: 0 1px 2px rgba(0, 0, 0, .3);
    --nx-shadow-hover: 0 20px 44px -18px rgba(0, 0, 0, .7);
    --nx-grad: linear-gradient(135deg, #5b8def, #8b5cf6);

    --nx-on-accent: #ffffff;
    --nx-accent-soft: rgba(91, 141, 239, .12);
    --nx-accent-line: rgba(91, 141, 239, .30);
    --nx-accent-line-soft: rgba(91, 141, 239, .22);
    --nx-accent-ring: rgba(91, 141, 239, .22);
    --nx-accent-text: #cfe0ff;
    --nx-accent-ink: #bcd0f7;

    --nx-glow-1: rgba(91, 141, 239, .12);
    --nx-glow-2: rgba(139, 92, 246, .10);
    --nx-topbar-bg: rgba(10, 14, 22, .72);
    --nx-overlay-bg: rgba(10, 14, 22, .82);
    --nx-row-hover: rgba(255, 255, 255, .02);
    --nx-badge-bg: #2c384a;

    --nx-cloud-grad: linear-gradient(160deg, #223050, #171f30);
    --nx-cloud-border: #2b3b5c;
    --nx-cloud-shadow: rgba(91, 141, 239, .50);
    --nx-feature-border: #34496b;
    --nx-support-grad: linear-gradient(160deg, rgba(91, 141, 239, .08), var(--nx-panel));

    --nx-btn-shadow: rgba(91, 141, 239, .70);
    --nx-btn-shadow-hover: rgba(91, 141, 239, .80);

    --nx-danger-text: #ff9aa2;
    --nx-danger-soft: rgba(239, 77, 90, .12);
    --nx-danger-line: rgba(239, 77, 90, .35);
    --nx-danger-line-strong: rgba(239, 77, 90, .60);

    --nx-ok-text: #7ad9a6;
    --nx-ok-soft: rgba(52, 199, 123, .13);
    --nx-ok-line: rgba(52, 199, 123, .34);

    --nx-warn-text: #f2b263;
    --nx-warn-soft: rgba(242, 178, 99, .13);
    --nx-warn-line: rgba(242, 178, 99, .38);

    --nx-progress-text: #c3a6f5;
    --nx-progress-soft: rgba(166, 125, 240, .14);
    --nx-progress-line: rgba(166, 125, 240, .38);

    --nx-select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b98ac' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");

    --nx-map-icon-filter: invert(1) hue-rotate(180deg) brightness(1.6);
}


* { box-sizing: border-box; }

html { position: relative; min-height: 100%; font-size: 16px; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; }

body {
    margin: 0;
    color: var(--nx-text);
    font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
    line-height: 1.55;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background:
        radial-gradient(1100px 620px at 82% -12%, var(--nx-glow-1), transparent 60%),
        radial-gradient(900px 520px at -5% 0%, var(--nx-glow-2), transparent 55%),
        var(--nx-bg);
    background-attachment: fixed;
}

a { color: var(--nx-primary); }

/* Available to assistive technology, invisible on screen. Used to announce that a link opens
   in a new tab, which the arrow only conveys visually. */
.nx-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.nx-container { width: 100%; max-width: 1140px; margin: 0 auto; padding: 0 24px; }

/* Top bar */
.nx-topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--nx-topbar-bg);
    backdrop-filter: saturate(150%) blur(14px);
    -webkit-backdrop-filter: saturate(150%) blur(14px);
    border-bottom: 1px solid var(--nx-border);
}
.nx-topbar-inner { display: flex; align-items: center; justify-content: space-between; height: 66px; }
.nx-brand { display: inline-flex; align-items: center; gap: 11px; color: var(--nx-text); text-decoration: none; font-size: 1.15rem; font-weight: 650; letter-spacing: -.01em; }
/* The ThinkGeo mark carries its own colour on a transparent ground, so it is shown bare rather
   than in the tinted tile the old glyph needed. contain keeps it square if the file is ever
   replaced with one of a different ratio. */
.nx-brand-mark { display: block; width: 30px; height: 30px; object-fit: contain; }
.nx-brand-sub { color: var(--nx-muted); font-weight: 500; }
.nx-topbar-user { display: flex; align-items: center; gap: 18px; }
.nx-user { color: var(--nx-muted); font-size: .9rem; }
.nx-user strong { color: var(--nx-text); font-weight: 600; }
/* Account-level destinations, sat between the identity and the logout button. Muted until
   hovered so they read as navigation rather than as competing calls to action. */
.nx-topbar-link { color: var(--nx-muted); text-decoration: none; font-size: .9rem; font-weight: 500; white-space: nowrap; }
.nx-topbar-link:hover { color: var(--nx-text); }
/* The bar is a fixed-height single row on desktop, which already pushed Logout off-screen on a
   phone before these links existed; they made it wider still. Below this width it becomes a
   wrapping block instead, so every control stays reachable and nothing scrolls sideways. */
@media (max-width: 720px) {
    .nx-topbar-inner { flex-wrap: wrap; height: auto; min-height: 66px; padding-top: 10px; padding-bottom: 10px; gap: 6px 14px; }
    .nx-topbar-user { flex-wrap: wrap; gap: 10px 14px; }
    /* Keep the account visible - it is the one thing worth knowing here - but let it give way
       first, since it is by far the widest item. */
    .nx-user { flex: 1 1 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
.nx-inline-form { margin: 0; }


.nx-main { flex: 1 0 auto; padding: 40px 0 60px; }
.nx-footer { flex-shrink: 0; border-top: 1px solid var(--nx-border); color: var(--nx-muted); padding: 24px 0; font-size: .85rem; }
.nx-footer-help { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 28px; padding-bottom: 16px; margin-bottom: 16px; border-bottom: 1px solid var(--nx-border); }
.nx-footer-help strong { color: var(--nx-text); font-weight: 600; }
.nx-footer-inner { display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap; }

/* Theme preference: three explicit choices, so the current one is always readable
   and any of the others is one click away. */
.nx-theme { display: flex; align-items: center; gap: 10px; }
.nx-theme-label { color: var(--nx-muted); font-size: .8rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; }
.nx-theme-options { display: inline-flex; border: 1px solid var(--nx-border-strong); border-radius: 999px; padding: 2px; background: var(--nx-panel); }
.nx-theme-option {
    border: none;
    background: transparent;
    color: var(--nx-muted);
    font: inherit;
    font-size: .82rem;
    font-weight: 600;
    padding: 5px 14px;
    border-radius: 999px;
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}
.nx-theme-option:hover { color: var(--nx-text); }
.nx-theme-option:focus-visible { outline: 2px solid var(--nx-primary); outline-offset: 2px; }
/* The selected state uses a raised surface rather than a filled accent: it reads clearly
   in both themes, and a footer preference does not want the weight of a primary button. */
.nx-theme-option.is-on {
    background: var(--nx-panel-2);
    color: var(--nx-text);
    box-shadow: inset 0 0 0 1px var(--nx-border-strong);
}

@media (max-width: 560px) {
    .nx-footer-inner { flex-direction: column; align-items: flex-start; gap: 14px; }
}

/* Cards */
.nx-card {
    position: relative;
    background: var(--nx-panel);
    border: 1px solid var(--nx-border);
    border-radius: var(--nx-radius);
    padding: 26px;
    box-shadow: var(--nx-shadow);
}
.nx-muted { color: var(--nx-muted); }
.nx-link { color: var(--nx-primary); text-decoration: none; font-weight: 500; }
.nx-link:hover { color: var(--nx-primary-hover); }
.nx-section-title { margin: 42px 0 16px; font-size: .8rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; color: var(--nx-muted); }

/* Hero */
.nx-hero-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 22px; }
.nx-hero h1 { margin: 0; font-size: 1.5rem; letter-spacing: -.02em; }
/* Icon and heading on one line for the two product cards. */
.nx-card-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.nx-card-head .nx-tile-icon { margin-bottom: 0; flex: none; }
/* The platforms carry the sentence; the frameworks in brackets are detail for those who need it. */
.nx-hero p strong { font-weight: 600; color: var(--nx-text); }
.nx-frameworks { font-size: .88em; font-style: italic; white-space: nowrap; }
.nx-download-box { margin-top: 22px; background: var(--nx-panel-2); border: 1px solid var(--nx-border); border-radius: 12px; padding: 20px; }
.nx-download-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.nx-pill { background: var(--nx-accent-soft); border: 1px solid var(--nx-accent-line); color: var(--nx-accent-text); border-radius: 999px; padding: 7px 15px; font-size: .85rem; font-weight: 500; }
/* align-items keeps the buttons at their own height rather than stretching to whatever space
   the panel has been held open to. */
.nx-download-actions { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-start; align-content: flex-start; }

/* The download box's tabs sit inside a card, so they run tighter than the page-level ones. */
.nx-download-box .nx-tabs { margin: 0; flex-wrap: wrap; }
.nx-download-box .nx-tab { padding: 7px 15px; font-size: .84rem; }

/* The tab body is a lighter surface than the box it sits in, so the selected tab reads as
   joined to it while the strip and the unselected tabs stay on the box colour. */
.nx-tab-panel {
    background: var(--nx-panel);
    border: 1px solid var(--nx-border);
    border-top: none;
    border-radius: 0 0 var(--nx-radius-sm) var(--nx-radius-sm);
    padding: 18px;
}
/* Both download panels occupy one grid cell, so the box is always as tall as the taller of
   them and switching tabs does not move the rest of the page. The hidden one keeps its space
   but is taken out of sight, hit-testing and the accessibility tree. */
.nx-tab-bodies { display: grid; }
.nx-tab-bodies > .nx-tab-panel { grid-area: 1 / 1; }
/* !important is needed, not preferred: bootstrap.min.css loads first and carries
   [hidden]{display:none!important}, which nothing short of this can outrank. The panel has to
   keep its box to hold the height open; visibility takes it out of sight and off the tab order. */
.nx-tab-bodies > .nx-tab-panel[hidden] {
    display: block !important;
    visibility: hidden;
    pointer-events: none;
}

/* Notes stack tightly against each other, and the last one clears the buttons below it. */
.nx-download-note { font-size: .85rem; margin: 0 0 6px; }
.nx-download-note:last-of-type { margin-bottom: 14px; }
/* The version-matching caveat is supporting detail rather than a headline, so it sits smaller
   and set in from the note above it. */
.nx-note-detail { font-size: 11.5px; padding-left: 14px; }
/* Older releases are a secondary action, so they are paired rather than stacked and styled
   quieter than the current build - six primary buttons would shout louder than the one that
   most people actually want. */
.nx-download-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.nx-download-grid .nx-btn { padding: 9px 14px; font-size: .86rem; }

@media (max-width: 620px) {
    .nx-download-grid { grid-template-columns: 1fr; }
}
/* No hover lift: the card is no longer one link, so lifting the whole thing would promise a click
   target that is not there. Its two buttons carry their own hover states. */
.nx-cloud { display: flex; flex-direction: column; color: var(--nx-text); overflow: hidden; background: var(--nx-cloud-grad); border-color: var(--nx-cloud-border); }
/* Same size as the SDKs heading beside it: the two cards are the two product lines, as equals. */
.nx-cloud h2 { margin: 0; font-size: 1.5rem; letter-spacing: -.02em; }
/* Between the one-column layout (800px) and ~1000px the Cloud card cannot fit the icon and
   "ThinkGeo Cloud" side by side: the heading needs ~173px (a tenth more in the Mac system font)
   plus 54px for the icon, against as little as 185px of room. There the icon goes back above the
   heading and both headings step down a little, together, so they stay the same size. */
@media (min-width: 801px) and (max-width: 1000px) {
    .nx-card-head { flex-direction: column; align-items: flex-start; }
    .nx-hero h1, .nx-cloud h2 { font-size: 1.35rem; }
}
/* With the keys open the Cloud card runs long; let Product Center keep its own height rather than
   stretching into an empty block beside it. */
.nx-hero-grid:has(.nx-cloud-keys:not([hidden])) { align-items: start; }
.nx-cloud-links { list-style: none; margin: 2px 0 0; padding: 0; display: grid; gap: 12px; }
.nx-cloud-link-desc { display: block; margin-top: 2px; font-size: .86rem; color: var(--nx-muted); }
.nx-cloud-links a { text-decoration: none; }
.nx-cloud-links a:hover { color: var(--nx-primary-hover); }
.nx-cloud-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
.nx-cloud-actions .nx-btn { padding: 9px 14px; font-size: .88rem; }
.nx-cloud-keys { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--nx-cloud-border); }
.nx-cloud-keys-status { margin: 0 0 10px; font-size: .88rem; color: var(--nx-muted); }
.nx-cloud-key-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.nx-cloud-key { background: var(--nx-panel); border: 1px solid var(--nx-border); border-radius: 10px; padding: 10px 12px; }
.nx-cloud-key.is-inactive { opacity: .7; }
.nx-cloud-key-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.nx-cloud-key-name { font-weight: 600; font-size: .92rem; overflow-wrap: anywhere; }
.nx-cloud-key-badges { flex: none; }
.nx-cloud-key-badges .nx-badge { margin-left: 4px; }
/* Label and buttons share the top line so the value gets the card's full width: a 32-character
   key beside its buttons wrapped a stray character onto a second line. */
.nx-cloud-key-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "label actions" "value value"; align-items: center; gap: 3px 8px; margin-top: 10px; }
.nx-cloud-key-label { grid-area: label; font-size: .7rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--nx-muted); }
.nx-cloud-key-actions { grid-area: actions; }
.nx-cloud-key-value { grid-area: value; font-family: var(--nx-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: .8rem; word-break: break-all; }
.nx-cloud-key-value.is-masked { letter-spacing: .08em; color: var(--nx-muted); }
.nx-cloud-key-actions { display: inline-flex; gap: 6px; }
.nx-cloud-key-btn { font-family: inherit; font-size: .78rem; font-weight: 600; line-height: 1.2; padding: 4px 9px; border-radius: 7px; border: 1px solid var(--nx-border-strong); background: var(--nx-panel-2); color: var(--nx-text); cursor: pointer; }
.nx-cloud-key-btn:hover { border-color: var(--nx-primary); color: var(--nx-primary); }

/* Tiles */
/* Three fixed columns rather than auto-fit. auto-fit collapses a track that has no card in
   it, so hiding a tile stretched the survivors; a fixed count keeps every card the same width
   and leaves the empty slot where the hidden one would have been. */
.nx-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }
.nx-tile { display: flex; flex-direction: column; background: var(--nx-panel); border: 1px solid var(--nx-border); border-radius: var(--nx-radius); padding: 24px; text-decoration: none; color: var(--nx-text); box-shadow: var(--nx-shadow); transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease; }
.nx-tile:hover { transform: translateY(-3px); border-color: var(--nx-border-strong); box-shadow: var(--nx-shadow-hover); }
.nx-tile-icon { width: 42px; height: 42px; border-radius: 11px; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 16px; color: var(--nx-accent-ink); background: var(--nx-accent-soft); border: 1px solid var(--nx-accent-line-soft); }
.nx-tile-icon svg { width: 21px; height: 21px; }
.nx-tile h3 { margin: 0 0 8px; font-size: 1.1rem; letter-spacing: -.01em; }
.nx-tile p { color: var(--nx-muted); flex: 1; margin: 0 0 16px; font-size: .92rem; }
.nx-tile-soon { opacity: .62; box-shadow: none; }
.nx-tile-soon:hover { transform: none; border-color: var(--nx-border); box-shadow: none; }
.nx-go { color: var(--nx-primary); font-weight: 600; font-size: .9rem; transition: transform .18s ease; }
.nx-tile:hover .nx-go { transform: translateX(3px); }
.nx-badge { background: var(--nx-badge-bg); color: var(--nx-muted); font-size: .68rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; padding: 3px 8px; border-radius: 999px; vertical-align: middle; margin-left: 6px; }

/* Buttons */
.nx-btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; border: 1px solid transparent; border-radius: 10px; padding: 11px 20px; font-size: .95rem; font-weight: 600; cursor: pointer; text-decoration: none; text-align: center; transition: transform .12s ease, background .15s ease, box-shadow .15s ease; }
.nx-btn:active { transform: translateY(1px); }
.nx-btn-primary { background: var(--nx-primary); color: var(--nx-on-accent); box-shadow: 0 8px 20px -8px var(--nx-btn-shadow); }
.nx-btn-primary:hover { background: var(--nx-primary-hover); box-shadow: 0 10px 26px -8px var(--nx-btn-shadow-hover); }
.nx-btn-danger { background: transparent; color: var(--nx-danger-text); border-color: var(--nx-danger-line); }
.nx-btn-danger:hover { background: var(--nx-danger-soft); border-color: var(--nx-danger-line-strong); }
.nx-btn-secondary { background: var(--nx-panel-2); color: var(--nx-text); border-color: var(--nx-border-strong); }
.nx-btn-secondary:hover { border-color: var(--nx-primary); color: var(--nx-primary); }
.nx-btn-block { width: 100%; margin-top: 8px; }

/* Login / forms */
.nx-login-wrap { display: flex; justify-content: center; padding-top: 36px; }
.nx-login-card { width: 100%; max-width: 430px; }
.nx-login-title { margin: 0 0 6px; font-size: 1.6rem; letter-spacing: -.02em; }
.nx-field { margin: 18px 0; display: flex; flex-direction: column; }
.nx-field label { margin-bottom: 7px; font-size: .88rem; font-weight: 500; color: var(--nx-muted); }
.nx-input { background: var(--nx-bg); border: 1px solid var(--nx-border-strong); border-radius: 10px; padding: 12px 13px; color: var(--nx-text); font-size: 1rem; font-family: inherit; transition: border-color .15s ease, box-shadow .15s ease; }
.nx-input:focus { outline: none; border-color: var(--nx-primary); box-shadow: 0 0 0 3px var(--nx-accent-ring); }
select.nx-input { appearance: none; background-image: var(--nx-select-arrow); background-repeat: no-repeat; background-position: right 14px center; padding-right: 36px; }
.nx-check { display: flex; align-items: center; gap: 9px; color: var(--nx-muted); font-size: .9rem; margin: 8px 0 4px; }
/* Short fields pair up on a wide screen; the first row's margin is already supplied by .nx-field. */
.nx-field-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 20px; }
.nx-form-actions { display: flex; gap: 12px; margin-top: 22px; }
@media (max-width: 720px) {
    .nx-field-row { grid-template-columns: 1fr; }
}
.nx-error { color: var(--nx-danger-text); font-size: .85rem; }
/* Separated from the action above it: it is the fallback, not a second thing to try first. */
/* Narrow enough not to steal width from the columns that carry meaning. */
/* The enrolment window: same accent language as the other callouts, but it sits above the key
   list because it is usually the right answer and removing every key is not. */
.nx-window { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; padding: 14px 16px; border-radius: var(--nx-radius-sm); background: var(--nx-accent-soft); border: 1px solid var(--nx-accent-line); color: var(--nx-accent-text); }
.nx-window-body { flex: 1 1 260px; min-width: 0; display: flex; flex-direction: column; gap: 3px; font-size: .9rem; }
/* --- License parse results --- */

/* The three headline facts about a file, before the per-product detail. */
.nx-facts { display: flex; flex-wrap: wrap; gap: 12px 32px; margin-bottom: 20px; }
.nx-fact { display: flex; flex-direction: column; gap: 2px; }
.nx-fact-label { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--nx-muted); }
.nx-fact-value { font-size: 1.02rem; font-weight: 600; }

/* One product entry. Separated by a rule rather than nested cards, which would stack borders. */
.nx-license-node { padding: 18px 0; border-top: 1px solid var(--nx-border); }
.nx-license-node-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.nx-license-node-head h3 { margin: 0; font-size: 1.05rem; letter-spacing: -.01em; }

/* Label/value pairs. Two columns on desktop, stacked once the labels would crowd the values. */
.nx-detail-grid { display: grid; grid-template-columns: minmax(140px, max-content) 1fr; gap: 7px 20px; margin: 0; font-size: .92rem; }
.nx-detail-grid dt { color: var(--nx-muted); }
.nx-detail-grid dd { margin: 0; overflow-wrap: anywhere; }
@media (max-width: 560px) {
    .nx-detail-grid { grid-template-columns: 1fr; gap: 2px; }
    .nx-detail-grid dd { margin-bottom: 10px; }
}

/* An expiry already in the past, called out where it is read rather than only in the badge. */
.nx-expiry-past { color: var(--nx-danger-text); font-weight: 600; }

.nx-note-list { margin: 6px 0 0; padding-left: 18px; }
.nx-note-list li { margin: 2px 0; }

/* A line we could not parse, shown verbatim so it can still be read by eye. */
.nx-raw { margin: 8px 0 0; padding: 10px 12px; border-radius: var(--nx-radius-sm); background: var(--nx-panel-alt, var(--nx-panel)); border: 1px solid var(--nx-border); font-size: .8rem; overflow-x: auto; white-space: pre-wrap; overflow-wrap: anywhere; }

.nx-tick-col { width: 34px; text-align: center; }
.nx-tick-col input { width: 16px; height: 16px; cursor: pointer; accent-color: var(--nx-primary); }
/* A six digit code, spaced so it can be read back against the email at a glance. */
.nx-code-input { font-size: 1.5rem; letter-spacing: .4em; text-align: center; font-variant-numeric: tabular-nums; }

.nx-challenge-help { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--nx-border); font-size: .85rem; line-height: 1.55; }

/* Page header */
.nx-page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 22px; }
.nx-page-title { margin: 0 0 4px; font-size: 1.7rem; line-height: 1.55; letter-spacing: -.02em; }
/* The back link never wraps and never shrinks: as a flex item it used to give way to a long
   description and break onto two lines. Its height matches the title's first line so it sits
   centred on it, in the same spot on every page. Keep min-height in step with the title's
   font-size and line-height above. */
.nx-back { flex: none; white-space: nowrap; display: inline-flex; align-items: center; min-height: calc(1.7rem * 1.55); }
.nx-page-actions { flex: none; display: flex; align-items: flex-start; gap: 16px; }

/* Too narrow to share a line with the title: the back link goes above it, at the left, on every
   page. Where there is also a button, the two share that line. */
@media (max-width: 620px) {
    .nx-page-head { flex-direction: column; gap: 8px; }
    .nx-page-head > .nx-back, .nx-page-actions { order: -1; }
    .nx-page-head .nx-back { min-height: 0; }
    .nx-page-actions { align-self: stretch; flex-direction: row-reverse; justify-content: space-between; align-items: center; }
}

/* Licenses */
.nx-license { margin-bottom: 18px; }
.nx-license-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 16px; }
.nx-license-head h2 { margin: 0 0 8px; font-size: 1.2rem; letter-spacing: -.01em; }
.nx-key { background: var(--nx-bg); border: 1px solid var(--nx-border-strong); border-radius: 7px; padding: 4px 10px; color: var(--nx-accent-ink); font-size: .9rem; }
.nx-license-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; color: var(--nx-muted); font-size: .85rem; white-space: nowrap; }

/* Admin account picker on the licenses page. Only one account ever sees this. */
.nx-admin-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; padding: 14px 16px; background: var(--nx-panel-2); border: 1px solid var(--nx-border); border-radius: var(--nx-radius-sm); }
.nx-admin-label { font-size: .88rem; font-weight: 600; color: var(--nx-muted); white-space: nowrap; }
/* Sized to an account id rather than greedily, so the bar does not stretch across the page. */
.nx-admin-input { flex: 0 1 400px; min-width: 0; padding: 9px 12px; font-size: .92rem; font-family: var(--nx-mono, ui-monospace, SFMono-Regular, Menlo, monospace); }
.nx-admin-bar .nx-btn { padding: 9px 20px; }
.nx-admin-error { margin: -6px 0 14px; color: var(--nx-danger-text); font-size: .88rem; }
/* Takes the remaining width so it wraps below the controls rather than stretching the bar. */
.nx-admin-hint { flex: 1 1 220px; min-width: 0; color: var(--nx-muted); font-size: .82rem; }

/* Says whose data is on screen, because forgetting is the whole risk of the feature. */
.nx-viewing { margin-bottom: 18px; padding: 12px 16px; border-radius: var(--nx-radius-sm); background: var(--nx-accent-soft); border: 1px solid var(--nx-accent-line); color: var(--nx-accent-text); font-size: .9rem; }
.nx-viewing .nx-link { margin-left: 6px; }

/* A banner that asks for one thing and then retires. Same accent palette as .nx-viewing so the
   page has one voice for "read this", not two. Wraps to stacked on narrow screens rather than
   squeezing the buttons. */
.nx-prompt { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 22px; padding: 16px 18px; border-radius: var(--nx-radius); background: var(--nx-accent-soft); border: 1px solid var(--nx-accent-line); color: var(--nx-accent-text); }
.nx-prompt-icon { flex: none; width: 38px; height: 38px; border-radius: 10px; display: inline-flex; align-items: center; justify-content: center; background: var(--nx-panel); border: 1px solid var(--nx-accent-line-soft); }
.nx-prompt-icon svg { width: 19px; height: 19px; }
/* Takes the slack so the actions stay their natural size and sit hard right. */
.nx-prompt-body { flex: 1 1 210px; min-width: 0; display: flex; flex-direction: column; gap: 3px; font-size: .92rem; }
.nx-prompt-actions { display: flex; align-items: center; gap: 10px; flex: none; }

/* A titled block on a page that has several of them, rather than one card per page. */
.nx-block { margin-bottom: 18px; }
.nx-block-title { margin: 0 0 16px; font-size: 1.15rem; letter-spacing: -.01em; }
.nx-block-note { font-size: .85rem; margin: -8px 0 16px; }
/* A second page-level section, headed like the first so the two read as peers. */
.nx-section-head { margin: 42px 0 22px; }

/* Tables */
.nx-table { width: 100%; border-collapse: collapse; }
/* Wide tables scroll inside their own card instead of widening the page. */
.nx-table-scroll { overflow-x: auto; }
.nx-table .nx-num { text-align: right; white-space: nowrap; }

/* A live expiry reads bold; one inside 30 days also goes amber - a warning to renew, not a
   failure, so it is deliberately not the danger red. Both stay legible on a term sub-row,
   which is otherwise muted. */
.nx-exp-live { font-weight: 600; }
.nx-table tr.nx-term-row td.nx-exp-live { color: var(--nx-text); }
.nx-exp-soon { font-weight: 600; }
.nx-table td.nx-exp-soon, .nx-table tr.nx-term-row td.nx-exp-soon { color: var(--nx-warn-text); }

/* A product holding several terms keeps them attached to it: the rules between them are
   dropped so the block reads as one product, and the dates sit quieter than the product's own. */
/* Qualified with .nx-table so these outrank the base cell rule, which shares their
   specificity and is declared later in the file. */
.nx-table tr.nx-has-terms td { border-bottom: none; }
.nx-table tr.nx-term-row td { border-bottom: none; color: var(--nx-muted); padding-top: 6px; padding-bottom: 6px; }
.nx-table tr.nx-term-row.is-last td { border-bottom: 1px solid var(--nx-border); }
.nx-table tbody tr.nx-term-row.is-last:last-child td { border-bottom: none; }

/* Orders are grouped: a heading row names the order, its items follow beneath. The tint is
   what carries the grouping, so the quantity column can stay narrow and right-aligned. */
.nx-orders th:first-child, .nx-orders td:first-child { width: 80px; }
.nx-orders .nx-order-head th { background: var(--nx-panel-2); color: var(--nx-text); font-size: .86rem; font-weight: 400; text-transform: none; letter-spacing: 0; padding: 9px 12px; }
.nx-orders .nx-order-head + tr td { border-top: none; }
/* Orders past the age cutoff are folded away until the reveal below the table asks for them. */
.nx-orders.is-collapsed .nx-older { display: none; }

.nx-order-no { font-weight: 600; }
.nx-order-date { color: var(--nx-muted); margin-left: 8px; }
/* Set well clear of the order and its date, so the term reads as its own fact. */
.nx-order-term { color: var(--nx-muted); margin-left: 32px; }

/* Full-width so it reads as the end of the list rather than a stray link beside it. */
.nx-reveal {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    margin-top: 14px;
    padding: 9px 14px;
    background: transparent;
    border: 1px dashed var(--nx-border-strong);
    border-radius: var(--nx-radius-sm);
    color: var(--nx-primary);
    font: inherit;
    font-size: .88rem;
    font-weight: 500;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease;
}
.nx-reveal:hover { background: var(--nx-panel-2); border-color: var(--nx-primary); }
.nx-reveal:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--nx-accent-ring); }
/* The caret's direction is set as a character rather than a rotation - one fewer moving part,
   and it cannot end up pointing the wrong way mid-transition. */
.nx-reveal-caret { display: inline-block; }

/* Whether an entitlement is still live. Carries a word as well as a colour, so it survives
   being read without colour vision or printed in grayscale. */
.nx-state { display: inline-block; border-radius: 999px; padding: 3px 11px; font-size: .78rem; font-weight: 600; white-space: nowrap; }
.nx-state.is-active { background: var(--nx-ok-soft); border: 1px solid var(--nx-ok-line); color: var(--nx-ok-text); }
.nx-state.is-expired { background: var(--nx-danger-soft); border: 1px solid var(--nx-danger-line); color: var(--nx-danger-text); }
.nx-table th, .nx-table td { text-align: left; padding: 11px 12px; border-bottom: 1px solid var(--nx-border); }
.nx-table th { color: var(--nx-muted); font-weight: 600; font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; }
.nx-table tbody tr { transition: background .12s ease; }
.nx-table tbody tr:hover { background: var(--nx-row-hover); }
.nx-table tbody tr:last-child td { border-bottom: none; }

/* Tickets */
.nx-status { display: inline-flex; align-items: center; gap: 6px; background: var(--nx-accent-soft); border: 1px solid var(--nx-accent-line); color: var(--nx-accent-text); border-radius: 999px; padding: 3px 11px; font-size: .78rem; font-weight: 500; white-space: nowrap; }
/* The dot carries the colour too, so the tone still reads where the tint is faint. The words stay
   the real signal; colour only groups them. See TicketStatusStyle for which status is which. */
.nx-status::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.nx-status-waiting { background: var(--nx-accent-soft); border-color: var(--nx-accent-line); color: var(--nx-accent-text); }
.nx-status-progress { background: var(--nx-progress-soft); border-color: var(--nx-progress-line); color: var(--nx-progress-text); }
.nx-status-needs-you { background: var(--nx-warn-soft); border-color: var(--nx-warn-line); color: var(--nx-warn-text); font-weight: 600; }
.nx-status-done { background: var(--nx-ok-soft); border-color: var(--nx-ok-line); color: var(--nx-ok-text); }
.nx-status-neutral { background: var(--nx-badge-bg); border-color: var(--nx-border-strong); color: var(--nx-muted); }

/* Busy submit button (site.js): a spinner and "Sending…" while the next page loads. */
.nx-btn[data-idle-html]:disabled { cursor: progress; opacity: .8; }
.nx-spinner { width: 1em; height: 1em; border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%; animation: nx-spin .7s linear infinite; flex: none; }
@keyframes nx-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .nx-spinner { animation-duration: 2.4s; } }
.nx-ticket-msg { margin-bottom: 14px; }
.nx-msg-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--nx-border); }
.nx-msg-support { border-color: var(--nx-feature-border); background: var(--nx-support-grad); }

/* Rendered ticket HTML */
.nx-msg-body { word-break: break-word; line-height: 1.6; }
.nx-msg-body > *:first-child { margin-top: 0; }
.nx-msg-body > *:last-child { margin-bottom: 0; }
.nx-msg-body p { margin: 0 0 12px; }
.nx-msg-body ul, .nx-msg-body ol { margin: 0 0 12px; padding-left: 22px; }
.nx-msg-body a { color: var(--nx-primary); }
.nx-msg-body code { background: var(--nx-bg); border: 1px solid var(--nx-border); border-radius: 5px; padding: 1px 5px; font-size: .9em; }
.nx-msg-body pre { background: var(--nx-bg); border: 1px solid var(--nx-border); border-radius: 10px; padding: 13px; overflow: auto; }
.nx-msg-body pre code { background: none; border: none; padding: 0; }
.nx-msg-body blockquote { margin: 0 0 12px; padding: 4px 14px; border-left: 3px solid var(--nx-border-strong); color: var(--nx-muted); }

/* Markdown composer */
.nx-composer { border: 1px solid var(--nx-border-strong); border-radius: 10px; overflow: hidden; background: var(--nx-bg); transition: border-color .15s ease, box-shadow .15s ease; }
.nx-composer:focus-within { border-color: var(--nx-primary); box-shadow: 0 0 0 3px var(--nx-accent-ring); }
.nx-composer-bar { display: flex; align-items: center; gap: 4px; padding: 7px 9px; background: var(--nx-panel-2); border-bottom: 1px solid var(--nx-border); }
.nx-composer-btn { background: transparent; border: 1px solid transparent; border-radius: 7px; color: var(--nx-muted); padding: 5px 9px; font-size: .85rem; cursor: pointer; line-height: 1; transition: background .12s ease, color .12s ease; }
.nx-composer-btn:hover { background: var(--nx-panel); color: var(--nx-text); }
.nx-composer-tabs { margin-left: auto; display: flex; gap: 4px; }
.nx-composer-tab { background: transparent; border: none; color: var(--nx-muted); padding: 5px 12px; border-radius: 7px; cursor: pointer; font-size: .85rem; font-weight: 500; }
.nx-composer-tab.is-active { background: var(--nx-panel); color: var(--nx-text); }
.nx-composer-input { border: none; border-radius: 0; width: 100%; resize: vertical; box-shadow: none; }
.nx-composer-input:focus { outline: none; box-shadow: none; }
.nx-composer-preview { min-height: 130px; padding: 14px; }

@media (max-width: 980px) {
    .nx-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 800px) {
    .nx-hero-grid { grid-template-columns: 1fr; }
    .nx-container { padding: 0 18px; }
}

@media (max-width: 620px) {
    .nx-tiles { grid-template-columns: 1fr; }
}

/* Street map downloads */
.nx-msd-status {
    background: var(--nx-panel-2);
    border: 1px solid var(--nx-border);
    border-left: 3px solid var(--nx-primary);
    border-radius: var(--nx-radius-sm);
    padding: 12px 16px;
    margin-bottom: 8px;
    color: var(--nx-muted);
    font-size: .9rem;
}
.nx-msd-status.is-error { border-left-color: var(--nx-danger); color: var(--nx-danger-text); }

.nx-msd-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(272px, 1fr)); gap: 18px; }
.nx-msd-pkg {
    display: flex;
    flex-direction: column;
    background: var(--nx-panel);
    border: 1px solid var(--nx-border);
    border-radius: var(--nx-radius);
    padding: 20px;
    box-shadow: var(--nx-shadow);
}
/* A package the customer cannot take yet. Shown rather than hidden, so what a subscription adds
   is visible - but set back, so it does not compete with the ones they can actually download. */
.nx-msd-pkg.is-locked { border-style: dashed; box-shadow: none; }
.nx-msd-pkg.is-locked .nx-msd-pkg-name,
.nx-msd-pkg.is-locked .nx-msd-pkg-desc { color: var(--nx-muted); }
.nx-msd-pkg.is-locked .nx-msd-dl { background: transparent; border: 1px solid var(--nx-border-strong); color: var(--nx-muted); box-shadow: none; font-size: .84rem; }
.nx-msd-pkg.is-locked .nx-msd-dl:not(.is-disabled):hover { border-color: var(--nx-primary); color: var(--nx-primary); }

.nx-msd-pkg-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.nx-msd-pkg-name { font-weight: 650; font-size: 1.02rem; letter-spacing: -.01em; }
.nx-msd-fmt {
    background: var(--nx-accent-soft);
    border: 1px solid var(--nx-accent-line);
    color: var(--nx-accent-text);
    border-radius: 999px;
    padding: 2px 9px;
    font-size: .64rem;
    font-weight: 600;
    letter-spacing: .06em;
}
.nx-msd-pkg-desc { color: var(--nx-muted); font-size: .88rem; margin: 10px 0 0; flex: 1; }
.nx-msd-pkg-meta {
    color: var(--nx-muted);
    font-size: .84rem;
    margin: 14px 0 12px;
    font-variant-numeric: tabular-nums;
}
.nx-msd-dl.is-disabled { opacity: .45; pointer-events: none; }


.nx-msd-mapcard { padding: 20px; }
.nx-msd-mapnote { margin: 0 0 14px; font-size: .9rem; }
.nx-msd-map-wrap { position: relative; }
.nx-msd-map {
    height: 460px;
    border-radius: 10px;
    border: 1px solid var(--nx-border);
    overflow: hidden;
}
.nx-msd-map.is-error {
    display: flex; align-items: center; justify-content: center;
    color: var(--nx-muted); font-size: .9rem;
}
/* Bottom-left: the drawing controls occupy the top-left corner. */
.nx-msd-zoombadge {
    position: absolute; bottom: 10px; left: 10px; z-index: 5;
    background: var(--nx-overlay-bg);
    border: 1px solid var(--nx-border-strong);
    color: var(--nx-text);
    font-weight: 600; font-size: .78rem;
    padding: 4px 10px; border-radius: 8px;
    pointer-events: none;
    font-variant-numeric: tabular-nums;
}

/* Tabs - shared by the home download box and the street map downloads page. */
/* Folder tabs: rounded on the top corners only, sitting on a shared baseline that the selected
   tab breaks through, so it reads as continuous with the panel beneath rather than as a button
   that happens to be highlighted. */
.nx-tabs {
    display: flex;
    gap: 4px;
    margin: 22px 0 18px;
    border-bottom: 1px solid var(--nx-border);
}
.nx-tab {
    margin-bottom: -1px;
    border: 1px solid transparent;
    border-radius: 10px 10px 0 0;
    background: transparent;
    color: var(--nx-muted);
    padding: 9px 18px;
    font: inherit;
    font-size: .9rem;
    font-weight: 600;
    cursor: pointer;
    transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.nx-tab:hover { color: var(--nx-text); background: var(--nx-row-hover); }
.nx-tab:focus-visible { outline: 2px solid var(--nx-primary); outline-offset: -2px; }
.nx-tab.is-on {
    background: var(--nx-panel);
    border-color: var(--nx-border);
    /* Erase the baseline under the selected tab so it joins the panel below. */
    border-bottom-color: var(--nx-panel);
    color: var(--nx-text);
}

/* Custom area */
.nx-msd-custom { display: grid; grid-template-columns: 1fr 1.35fr; gap: 24px; padding: 22px; }
.nx-msd-custom h3 { margin: 0 0 8px; font-size: 1.12rem; letter-spacing: -.01em; }
.nx-msd-custom > .nx-msd-custom-side > p { font-size: .92rem; }
.nx-msd-howto { list-style: none; margin: 16px 0 20px; padding: 0; }
.nx-msd-howto li {
    position: relative;
    padding: 6px 0 6px 32px;
    color: var(--nx-muted);
    font-size: .91rem;
}
.nx-msd-n {
    position: absolute; left: 0; top: 7px;
    width: 22px; height: 22px; border-radius: 50%;
    background: var(--nx-panel-2);
    border: 1px solid var(--nx-border-strong);
    color: var(--nx-text);
    display: grid; place-items: center;
    font-size: .7rem; font-weight: 700;
}
.nx-msd-hint { font-size: .8rem; margin: 8px 0 0; }
.nx-msd-readout {
    margin: 18px 0;
    padding: 12px 14px;
    border-radius: var(--nx-radius-sm);
    background: var(--nx-panel-2);
    border: 1px solid var(--nx-border);
    font-weight: 600;
    font-size: .92rem;
    font-variant-numeric: tabular-nums;
}
.nx-msd-readout.is-over { border-color: var(--nx-danger-line-strong); color: var(--nx-danger-text); }
.nx-msd-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.nx-msd-actions .nx-btn { padding: 9px 16px; font-size: .9rem; }
.nx-msd-actions .nx-btn:disabled { opacity: .45; cursor: not-allowed; }
.nx-msd-progress {
    height: 6px; margin-top: 16px;
    background: var(--nx-panel-2);
    border-radius: 4px; overflow: hidden;
}
.nx-msd-progress-bar {
    height: 100%; width: 40%;
    background: var(--nx-grad);
    border-radius: 4px;
    animation: nx-msd-slide 1.1s ease-in-out infinite;
}
@keyframes nx-msd-slide {
    from { margin-left: -40%; }
    to { margin-left: 100%; }
}
.nx-msd-note { margin: 12px 0 0; min-height: 1.2em; font-size: .86rem; color: var(--nx-muted); }

@media (prefers-reduced-motion: reduce) {
    .nx-msd-progress-bar { animation: none; width: 100%; }
}

/* MapLibre's controls ship light; bring them onto the panel palette. */
.nx-msd-map .maplibregl-ctrl-group { background: var(--nx-panel-2); border: 1px solid var(--nx-border-strong); }
.nx-msd-map .maplibregl-ctrl-group button + button { border-top: 1px solid var(--nx-border); }
/* Both sets of icons are dark artwork meant for a light control: MapLibre draws its own
   into a child span, Draw paints its into the button's background. Invert both, or the
   drawing tools are black on a dark panel and read as disabled. */
.nx-msd-map .maplibregl-ctrl-group button .maplibregl-ctrl-icon,
.nx-msd-map .mapbox-gl-draw_ctrl-draw-btn { filter: var(--nx-map-icon-filter); }
.nx-msd-map .maplibregl-ctrl-attrib { background: var(--nx-overlay-bg); color: var(--nx-muted); }
.nx-msd-map .maplibregl-ctrl-attrib a { color: var(--nx-muted); }

@media (max-width: 900px) {
    .nx-msd-custom { grid-template-columns: 1fr; }
}

@media (max-width: 800px) {
    .nx-msd-map { height: 340px; }
}

/* --- Welcome tour --------------------------------------------------------------------------- */

/* A plain button that reads as a link, for actions that are not navigation. Resets only the font
   family and size: the "font" shorthand would also reset the weight, and knock out the weight
   .nx-link gives it - which is why the tour link rendered lighter than the link beside it. */
.nx-link-button { border: 0; background: none; padding: 0; font-family: inherit; font-size: inherit; line-height: inherit; cursor: pointer; }

/* Swallows clicks so nobody navigates away mid-tour. Transparent while a spotlight does the
   dimming; dims the page itself for steps with nothing to point at. */
.nx-tour-backdrop { position: fixed; inset: 0; z-index: 2000; background: transparent; }
.nx-tour-backdrop.is-dim { background: rgba(8, 12, 20, .55); }

/* One box whose huge shadow darkens everything outside it - no hole to cut in an overlay. The
   inner ring marks the edge of what is being described. */
.nx-tour-spot {
    position: fixed; z-index: 2001; pointer-events: none;
    border-radius: calc(var(--nx-radius) + 4px);
    box-shadow: 0 0 0 2px var(--nx-primary), 0 0 0 9999px rgba(8, 12, 20, .55);
    transition: top .28s ease, left .28s ease, width .28s ease, height .28s ease;
}

.nx-tour-pop {
    position: fixed; z-index: 2002;
    width: min(380px, calc(100vw - 24px)); max-height: calc(100vh - 24px); overflow: auto;
    padding: 16px 20px 18px;
    background: var(--nx-panel); color: var(--nx-text);
    border: 1px solid var(--nx-border); border-radius: var(--nx-radius);
    box-shadow: var(--nx-shadow-hover);
}
.nx-tour-pop:focus { outline: none; }
/* Steps with nothing to highlight - the welcome and the overview - get more room, centred. */
.nx-tour-pop.is-center { top: 50%; left: 50%; transform: translate(-50%, -50%); width: min(540px, calc(100vw - 24px)); }
/* On a phone a floating card would cover what it describes, so it docks under it instead. */
.nx-tour-pop.is-sheet { top: auto; left: 12px; right: 12px; bottom: 12px; width: auto; max-height: 55vh; }

.nx-tour-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 4px; }
.nx-tour-count { font-size: .74rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--nx-muted); }
.nx-tour-close { border: 0; background: transparent; color: var(--nx-muted); font-size: 1.45rem; line-height: 1; padding: 2px 8px; border-radius: var(--nx-radius-sm); cursor: pointer; }
.nx-tour-close:hover { color: var(--nx-text); background: var(--nx-accent-soft); }
.nx-tour-title { margin: 0 0 10px; font-size: 1.14rem; letter-spacing: -.01em; }
.nx-tour-body { font-size: .92rem; line-height: 1.55; }
.nx-tour-body p { margin: 0 0 10px; }
.nx-tour-body p:last-child { margin-bottom: 0; }
.nx-tour-groups { margin: 0 0 12px; padding-left: 18px; }
.nx-tour-groups li { margin: 0 0 9px; }
/* The one rule of thumb worth taking away, set apart in the page's usual callout colours. */
.nx-tour-tip { padding: 10px 12px; border-radius: var(--nx-radius-sm); background: var(--nx-accent-soft); border: 1px solid var(--nx-accent-line); }
.nx-tour-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }

@media (prefers-reduced-motion: reduce) {
    .nx-tour-spot { transition: none; }
}
/* A heading inside a tour step, for the opening step that both welcomes and explains. */
.nx-tour-subtitle { margin: 16px 0 8px; font-size: 1rem; letter-spacing: -.01em; }
/* Beside a section that spans most of the page there is no room to the side, so the card sits above
   or below - wider, and so shorter, which is what lets it fit under a whole row of tiles. */
.nx-tour-pop.is-wide { width: min(560px, calc(100vw - 24px)); }

/* Usage (staff) */
.nx-nowrap { white-space: nowrap; }
.nx-mono { font-family: var(--nx-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: .85em; }
.nx-subnav { display: flex; gap: 4px; margin: 0 0 18px; border-bottom: 1px solid var(--nx-border); }
.nx-subnav a { padding: 9px 14px; margin-bottom: -1px; border-bottom: 2px solid transparent; color: var(--nx-muted); font-weight: 600; font-size: .92rem; text-decoration: none; }
.nx-subnav a:hover { color: var(--nx-text); }
.nx-subnav a.is-on { color: var(--nx-primary); border-bottom-color: var(--nx-primary); }
.nx-usage-periods { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 16px; }
.nx-usage-period { padding: 6px 13px; border-radius: 999px; border: 1px solid var(--nx-border-strong); background: var(--nx-panel); color: var(--nx-text); font-size: .86rem; font-weight: 500; text-decoration: none; }
.nx-usage-period:hover { border-color: var(--nx-primary); color: var(--nx-primary); }
.nx-usage-period.is-on { background: var(--nx-accent-soft); border-color: var(--nx-accent-line); color: var(--nx-accent-text); font-weight: 600; }
.nx-usage-tiles { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 14px; margin-bottom: 18px; }
.nx-usage-tile { background: var(--nx-panel); border: 1px solid var(--nx-border); border-radius: var(--nx-radius); padding: 16px 18px; box-shadow: var(--nx-shadow); }
.nx-usage-tile-label { font-size: .74rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--nx-muted); }
.nx-usage-tile-value { margin-top: 6px; font-size: 1.6rem; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.nx-usage-tile-text { font-size: 1.25rem; }
.nx-usage-columns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
.nx-usage-columns .nx-block { margin-bottom: 0; }
.nx-usage-detail { overflow-wrap: anywhere; max-width: 32em; }
.nx-usage-credit { margin-top: 18px; font-size: .82rem; }
.nx-usage-sub { margin-top: 3px; font-size: .78rem; color: var(--nx-muted); }
.nx-usage-failed { color: var(--nx-danger-text); font-weight: 600; }
.nx-state.is-neutral { background: var(--nx-badge-bg); border: 1px solid var(--nx-border-strong); color: var(--nx-muted); }
.nx-usage-facts { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 8px 22px; margin: 0; }
.nx-usage-facts dt { color: var(--nx-muted); font-size: .85rem; font-weight: 600; }
.nx-usage-facts dd { margin: 0; overflow-wrap: anywhere; }
.nx-log-filter .nx-input { width: auto; }
.nx-log { padding: 6px 14px; font-family: var(--nx-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: .8rem; }
.nx-log-entry { display: grid; grid-template-columns: max-content 3em minmax(0, 1fr); gap: 12px; align-items: baseline; padding: 6px 0; border-bottom: 1px solid var(--nx-border); }
.nx-log-entry:last-child { border-bottom: none; }
.nx-log-time { color: var(--nx-muted); white-space: nowrap; }
.nx-log-level { font-weight: 700; color: var(--nx-muted); }
.nx-log-text { margin: 0; font: inherit; white-space: pre-wrap; overflow-wrap: anywhere; }
.nx-log-entry.is-wrn .nx-log-level { color: var(--nx-warn-text); }
.nx-log-entry.is-err .nx-log-level, .nx-log-entry.is-ftl .nx-log-level { color: var(--nx-danger-text); }
/* Rows that open to show what happened */
.nx-row-open { cursor: pointer; }
.nx-row-open:focus-visible { outline: 2px solid var(--nx-primary); outline-offset: -2px; }
.nx-row-chevron { display: inline-block; width: 0; height: 0; margin-right: 8px; vertical-align: middle; border-top: 4.5px solid transparent; border-bottom: 4.5px solid transparent; border-left: 6px solid var(--nx-muted); transition: transform .15s ease; }
.nx-row-open.is-open .nx-row-chevron { transform: rotate(90deg); }
.nx-row-detail > td { background: var(--nx-panel-2); padding: 14px 16px; }
.nx-row-detail[hidden] { display: none; }
.nx-trail-title { margin: 0 0 10px; font-size: .84rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--nx-muted); }
.nx-trail { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.nx-trail-step { display: grid; grid-template-columns: max-content 5.5em minmax(0, 1fr); gap: 4px 14px; align-items: baseline; font-size: .86rem; }
.nx-trail-time { color: var(--nx-muted); white-space: nowrap; font-size: .8rem; }
.nx-trail-kind { font-size: .74rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--nx-muted); }
.nx-kind-signin { color: var(--nx-accent-text); }
.nx-kind-action { color: var(--nx-ok-text); }
.nx-trail-name { overflow-wrap: anywhere; }
.nx-trail-detail { display: block; font-size: .8rem; overflow-wrap: anywhere; }
.nx-trail-agent { margin: 12px 0 0; font-size: .76rem; overflow-wrap: anywhere; }
.nx-trail-empty { margin: 0; }
@media (prefers-reduced-motion: reduce) { .nx-row-chevron { transition: none; } }
@media (max-width: 620px) {
    .nx-trail-step { grid-template-columns: 1fr; gap: 1px; }
}

@media (max-width: 980px) {
    .nx-usage-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .nx-usage-columns { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
    .nx-log-entry { grid-template-columns: 1fr; gap: 2px; }
}

/* Ticket attachments */
.nx-dropzone { position: relative; border: 1.5px dashed var(--nx-border-strong); border-radius: var(--nx-radius-sm); background: var(--nx-panel-2); padding: 14px 16px; transition: border-color .15s ease, background .15s ease; }
.nx-dropzone:hover, .nx-dropzone.is-dragging { border-color: var(--nx-primary); background: var(--nx-accent-soft); }
.nx-dropzone:focus-within { outline: 2px solid var(--nx-primary); outline-offset: 2px; }
.nx-dropzone.is-invalid { border-color: var(--nx-danger-line-strong); }
.nx-dropzone-input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.nx-dropzone.is-chosen .nx-dropzone-input, .nx-dropzone.is-uploading .nx-dropzone-input { pointer-events: none; }
.nx-dropzone-empty { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.nx-dropzone.is-chosen .nx-dropzone-empty { display: none; }
.nx-dropzone-icon { display: inline-flex; flex: none; color: var(--nx-primary); }
.nx-dropzone-icon svg { width: 20px; height: 20px; }
.nx-dropzone-hint { flex-basis: 100%; font-size: .82rem; color: var(--nx-muted); }
.nx-dropzone-chosen { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.nx-dropzone-chosen[hidden], .nx-upload[hidden] { display: none; }
.nx-dropzone-name { font-weight: 600; overflow-wrap: anywhere; }
.nx-dropzone-size { font-size: .85rem; color: var(--nx-muted); }
.nx-dropzone-remove { position: relative; z-index: 1; margin-left: auto; font-family: inherit; font-size: .8rem; font-weight: 600; padding: 5px 10px; border-radius: 7px; border: 1px solid var(--nx-border-strong); background: var(--nx-panel); color: var(--nx-text); cursor: pointer; }
.nx-dropzone-remove:hover { border-color: var(--nx-danger-line-strong); color: var(--nx-danger-text); }
.nx-dropzone-remove:disabled { opacity: .5; cursor: not-allowed; }
.nx-upload { margin-top: 12px; }
.nx-upload-track { height: 8px; border-radius: 999px; background: var(--nx-border); overflow: hidden; }
.nx-upload-bar { width: 0; height: 100%; background: var(--nx-primary); transition: width .2s ease; }
.nx-upload-text { display: block; margin-top: 6px; font-size: .84rem; color: var(--nx-muted); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
@media (prefers-reduced-motion: reduce) { .nx-upload-bar { transition: none; } }

/* Files on a ticket, listed under the post they arrived with */
.nx-ticket-msg .nx-file-list { margin-top: 12px; }
.nx-file-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.nx-file { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; padding: 9px 12px; border: 1px solid var(--nx-border); border-radius: var(--nx-radius-sm); background: var(--nx-panel-2); }
.nx-file-link { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; color: var(--nx-primary); text-decoration: none; overflow-wrap: anywhere; }
.nx-file-link:hover { color: var(--nx-primary-hover); text-decoration: underline; }
.nx-file-icon { flex: none; transform: rotate(45deg); }
.nx-file-meta { margin-left: auto; font-size: .84rem; }

.nx-ticket-people { display: flex; flex-wrap: wrap; gap: 4px 8px; margin-top: 4px; font-size: .88rem; }
