/* ============================================================================
 *  app.css  -  Gentelella-toned admin shell
 *  Palette : sidebar #2A3F54 / accent #1ABB9C / canvas #F7F7F7
 * ==========================================================================*/

:root {
    --nav-bg:        #2A3F54;
    --nav-bg-dark:   #22354A;
    --nav-hover:     #1E3446;
    --nav-text:      #E7E7E7;
    --nav-muted:     #8B99A6;
    --nav-section:   #6A7A89;

    --accent:        #1ABB9C;
    --accent-dark:   #16A085;
    --accent-soft:   #E8F8F5;

    --canvas:        #F7F7F7;
    --panel:         #FFFFFF;
    --line:          #E6E9ED;
    --line-strong:   #D9DEE4;

    --text:          #4A5568;
    --text-strong:   #2A3F54;
    --text-muted:    #909BA6;

    --blue:          #3498DB;
    /* For TEXT. `--blue` is an edge and a fill; set as type it reads as
       disabled rather than as information, and in the reference view it
       is read against black source code, fainter still. */
    --blue-ink:      #14507B;
    /* For type ON a mark. A tint behind coloured letters is weaker than
       the code around it, whichever hue; the mark has to be the lightest
       thing on the line, so the fill goes dark and the letters go white. */
    --red-ink:       #A93226;
    --amber:         #F0AD4E;
    --red:           #E74C3C;
    --red-soft:      #FDECEA;
    --amber-soft:    #FDF6E7;
    --green-soft:    #E8F8F5;

    --sidebar-w:     272px;
    /* The bar holds a menu button and the chips, and nothing else since
       the page title came out - 36px of content, measured. 40 leaves it
       air. Shared with .brand-logo so the sidebar header and the topbar
       still end on the same line; the logo scales with it. */
    --topbar-h:      40px;
    --radius:        3px;
    --shadow:        0 1px 2px rgba(42, 63, 84, .08);
}

* { box-sizing: border-box; }

/* Text-only scaling: every font-size below is expressed in rem, so changing
 * this single value resizes text without touching layout metrics.          */
html {
    font-size: var(--app-fs, 13px);
}

html, body {
    height: 100%;
    margin: 0;
}

body {
    /* --app-font is set by the typeface picker; the literal stack is the
       fallback for a first run before fontscale.js has applied anything */
    font-family: var(--app-font, "Segoe UI", "Helvetica Neue", Roboto, "Malgun Gothic", sans-serif);
    font-size: 1.0rem;
    line-height: 1.45;
    color: var(--text);
    background: var(--canvas);
    overflow: hidden;
}

a { color: var(--accent-dark); text-decoration: none; }

/* ============================================================== LAYOUT === */

.shell {
    display: flex;
    height: 100vh;
    width: 100vw;
}

/* ------------------------------------------------------------ SIDEBAR --- */

.sidebar {
    width: var(--sidebar-w);
    flex: 0 0 var(--sidebar-w);
    background: var(--nav-bg);
    color: var(--nav-text);
    display: flex;
    flex-direction: column;
    transition: width .18s ease, flex-basis .18s ease;
    overflow: hidden;
}

body.nav-collapsed .sidebar { width: 62px; flex-basis: 62px; }
body.nav-collapsed .nav-label,
body.nav-collapsed .nav-section,
body.nav-collapsed .brand-text,
body.nav-collapsed .nav-sub,
body.nav-collapsed .nav-badge { display: none; }
body.nav-collapsed .nav-item { justify-content: center; padding-left: 0; padding-right: 0; }
body.nav-collapsed .brand { justify-content: center; padding: 0; }
body.nav-collapsed .brand::after {
    content: 'SMX';
    color: var(--accent); font-weight: 700; font-size: 0.7692rem; letter-spacing: .5px;
}

.brand {
    /*
     * A COLUMN: the company mark sits above the application's own name.
     *
     * This was a row locked to `height: var(--topbar-h)`, which lined the
     * sidebar header up with the topbar and was right while the only thing
     * in it was one line of text. Adding an image to a row made it a
     * neighbour of the title rather than something above it, and it took
     * 190px of a 272px pane - which is why the title read "SERV".
     *
     * `min-height` keeps that alignment exactly as it was when there is no
     * logo file, and the header grows only when there is something to show.
     */
    min-height: var(--topbar-h);
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    /* stretch, not center: the logo band spans the pane and the title is
       centred by its own text-align. Centring items here would shrink the
       band to its content. */
    align-items: stretch;
    gap: 0;
    /* The 16px sides stay because fitBrand() measures this element and
       subtracts exactly this padding to size the title. The logo steps
       outside it with negative margins rather than this changing. */
    padding: 0 16px 11px;
    border-bottom: 1px solid rgba(255, 255, 255, .07);
    background: var(--nav-bg-dark);
    white-space: nowrap;
}

/* Sized by script to fill the pane - see fitBrand() in app.js. The
   letter-spacing is in `em` so width stays linear in font-size, which is what
   lets one measurement give the answer. */
.brand-text {
    font-size: 0.9231rem;            /* replaced on load; a sane fallback */
    font-weight: 700;
    letter-spacing: .07em;
    color: var(--accent);            /* teal, so it reads as the product name
                                        rather than another menu entry       */
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    /* `.brand` stretches its children now, so the title centres itself */
    text-align: center;
}

.nav-scroll { flex: 1 1 auto; overflow-y: auto; overflow-x: hidden; padding-bottom: 20px; }
.nav-scroll::-webkit-scrollbar { width: 6px; }
.nav-scroll::-webkit-scrollbar-thumb { background: #3E5871; border-radius: 3px; }

.nav-section {
    padding: 16px 16px 6px;
    font-size: 0.7692rem;
    font-weight: 700;
    letter-spacing: 1.1px;
    color: var(--nav-section);
    text-transform: uppercase;
    white-space: nowrap;
}

.nav-item {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 18px;
    color: var(--nav-text);
    font-size: 0.9615rem;
    cursor: pointer;
    border-left: 3px solid transparent;
    white-space: nowrap;
    user-select: none;
}
.nav-item:hover { background: var(--nav-hover); }
.nav-item.active {
    background: var(--nav-hover);
    border-left-color: var(--accent);
    color: #fff;
    font-weight: 600;
}
.nav-item.disabled { color: #5F7285; cursor: not-allowed; }
.nav-item.disabled:hover { background: transparent; }

.nav-label { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; }

.nav-badge {
    font-size: 0.6923rem; font-weight: 700; letter-spacing: .4px;
    padding: 1px 6px; border-radius: 8px;
    background: #3E5871; color: #A8B7C5;
    text-transform: uppercase;
}

.nav-sub { background: rgba(0, 0, 0, .12); }
.nav-sub .nav-item {
    padding-left: 45px;
    font-size: 0.9231rem;
    color: #BCC7D2;
    border-left-color: transparent;
}
.nav-sub .nav-item:hover { color: #fff; }
.nav-sub .nav-item.active { color: #fff; font-weight: 600; background: transparent; }
.nav-sub .nav-item.active::before {
    content: ''; width: 5px; height: 5px; border-radius: 50%;
    background: var(--accent); margin-left: -12px; margin-right: 7px;
}

.sidebar-foot {
    flex: 0 0 auto;
    display: flex;
    border-top: 1px solid rgba(255, 255, 255, .07);
    background: var(--nav-bg-dark);
}
.sidebar-foot button {
    flex: 1; padding: 10px 0;
    background: none; border: 0; cursor: pointer;
    color: var(--nav-muted); font-size: 1.0rem;
}
.sidebar-foot button:hover { color: #fff; background: var(--nav-hover); }

/* --------------------------------------------------------- MAIN AREA --- */

.main {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.topbar {
    height: var(--topbar-h);
    flex: 0 0 var(--topbar-h);
    background: #fff;
    border-bottom: 1px solid var(--line-strong);
    display: flex; align-items: center;
    padding: 0 18px;
    gap: 14px;
    box-shadow: var(--shadow);
    z-index: 5;
}

.hamburger {
    background: none; border: 0; cursor: pointer;
    font-size: 1.3077rem; color: var(--text-strong);
    padding: 6px 8px; border-radius: var(--radius);
}
.hamburger:hover { background: var(--canvas); }


.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }

/* ---- typeface picker + size stepper (text only, not browser zoom) ---- */
.fontctl {
    display: flex; align-items: center;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    overflow: hidden;
    background: #fff;
}
.fontctl button {
    background: none; border: 0; cursor: pointer;
    color: var(--text-strong);
    font-family: inherit;
    padding: 4px 9px;
    line-height: 1.2;
    border-right: 1px solid var(--line);
}
.fontctl button:last-child { border-right: 0; }
.fontctl button:hover { background: var(--canvas); }
.fontctl button:disabled { opacity: .35; cursor: not-allowed; background: none; }
.fontctl .fs-dn { font-size: 0.8462rem; }
.fontctl .fs-up { font-size: 1.1538rem; }
/* sits between A- and A+, and shows itself in the face it names */
.fontctl .fs-face {
    appearance: none; -webkit-appearance: none;
    background: none; border: 0; cursor: pointer;
    color: var(--text-strong);
    font-size: 0.8462rem;
    padding: 4px 7px;
    line-height: 1.2;
    max-width: 8.5rem;
    border-right: 1px solid var(--line);
}
.fontctl .fs-face:hover { background: var(--canvas); }
.fontctl .fs-face:focus { outline: 2px solid var(--accent); outline-offset: -2px; }

.file-chip {
    display: flex; align-items: center; gap: 7px;
    font-size: 0.8846rem;
    background: var(--canvas);
    border: 1px solid var(--line-strong);
    border-radius: 12px;
    padding: 4px 11px;
    color: var(--text);
    max-width: 420px;
}
.file-chip .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--text-muted); flex: 0 0 7px; }
.file-chip.loaded .dot { background: var(--accent); }
.file-chip .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.content {
    flex: 1 1 auto;
    overflow: hidden;          /* only the inner regions scroll */
    padding: 12px;
    min-height: 0;
}

/* --- two-column work area: table on the left, checks / save on the right - */

.workspace {
    display: flex;
    gap: 12px;
    height: 100%;
    min-height: 0;
}

.col-main,
.col-side {
    display: flex;
    flex-direction: column;
    gap: 11px;
    min-height: 0;
}
.col-main { flex: 1 1 auto; min-width: 340px; overflow: hidden; }

/* the whole right column scrolls - no nested scrollbars inside it */
.col-side {
    flex: 0 0 var(--side-w, 390px);
    min-width: 260px;
    overflow-y: auto;
    overflow-x: hidden;
    padding-right: 2px;
}

/* ------------------------------------------------------- BRAND LOGO --- */

/*
 * Above the application title, and sized to the pane rather than to itself.
 *
 * `height: auto` with a width cap keeps the aspect ratio whatever file is
 * dropped in - a wordmark and a square badge both land correctly. Nothing
 * reserves space: with no image the element is removed by its own onerror
 * and the title sits where it always did.
 *
 * It is not a link and not a button. It says whose tool this is; it does not
 * do anything, and giving it a hover state would suggest otherwise.
 */
.brand-logo {
    display: block;

    /*
     * The pane's full width, and the topbar's exact height.
     *
     * `--topbar-h` is the same variable the bar opposite uses, so the white
     * band and the "Memory Map Auto Coding" title line up by construction -
     * not by a number in two places that has to be kept in step.
     *
     * The negative margins take it back out through `.brand`'s side padding,
     * which has to stay for fitBrand()'s sake. `box-sizing: border-box` keeps
     * the inner padding INSIDE the band height, so the band stays exactly
     * one topbar tall.
     */
    width: calc(100% + 32px);
    margin: 0 -16px 11px;
    height: var(--topbar-h);
    box-sizing: border-box;
    /* contain, never cover: a company mark is not cropped and not stretched */
    object-fit: contain;

    /*
     * Its own white plate.
     *
     * The mark is navy on a transparent ground and the sidebar is navy, so
     * on its own it was almost invisible. Recolouring the logo was the other
     * option and is not ours to take - a company mark is used as it is given.
     * So the plate goes behind the IMAGE only, not behind the header.
     *
     * The padding is part of it: a logo touching the edge of its own
     * background reads as a cropped screenshot rather than a mark.
     */
    background: #fff;
    padding: 7px 14px;
    /* square: it is a band across the header now, not a card on top of it */
    border-radius: 0;
}
.nav-collapsed .brand-logo { display: none; }

.workspace .panel { margin-bottom: 0; }

#gridPanel { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
#gridPanel .table-wrap { flex: 1 1 auto; min-height: 0; max-height: none; }

/* ---- Model Array Generator: one column, and the table fills it ---------
   The page reuses .workspace / .col-main with no .col-side, so the panel is
   the only thing in the column and takes the whole height. */
#maPanel { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
#maPanel .table-wrap { flex: 1 1 auto; min-height: 0; max-height: none; }

/* A model column is a column of numbers with the occasional macro in it, so
   the digits are lined up and anything longer clips like every other cell. */
table.mm td.col-val { font-variant-numeric: tabular-nums; }

/* The grid takes focus, so it must show that it has it - without a browser
   outline drawn around a 4000px-wide table. */
#maWrap:focus { outline: none; box-shadow: inset 0 0 0 2px var(--accent-soft); }

/* One changed cell, one mark. Amber is already "modified" everywhere else in
   this app, and red is spent on errors - it is not borrowed here.

   THE BORDER IS EVEN ON ALL FOUR SIDES. It was a 2px bar down the left edge
   only, and the user's objection to that is a readability one and correct: a
   mark that is heavy on one side pulls the eye sideways across a grid that is
   already 28 columns wide, so the cell reads as an arrow rather than as a box.
   Same weight all round, and the amber fill does the rest of the work. */
#page-model-array table.mm td.col-val.is-mod {
    background: #FDF7E9;
    box-shadow: inset 0 0 0 1px var(--amber);
}
/* Outside its own declared min/max. This IS an error, so it is the one thing
   on this page that spends the red - even all round, like the amber. */
#page-model-array table.mm td.col-val.is-bad {
    background: var(--red-soft);
    box-shadow: inset 0 0 0 1px var(--red);
    color: var(--red);
}
/* Not compared at all - the bound would not resolve, or the value is a macro.
   Not an error, so not red: a dotted underline and a tooltip that says why. */
#page-model-array table.mm td.col-val.is-unchecked {
    text-decoration: underline dotted var(--text-muted);
    text-underline-offset: 3px;
}

#page-model-array table.mm td.active-cell { box-shadow: inset 0 0 0 2px var(--accent); }
/* The cursor wins on a cell that is both: the amber fill still says
   "changed", so stacking two rings would only add noise. */
#page-model-array table.mm td.col-val.is-mod.active-cell {
    box-shadow: inset 0 0 0 2px var(--accent);
}

/* The change count wears the SAME amber the changed cells wear. On this page
   amber means exactly one thing - a value that is no longer what the file
   said - and red stays spent on errors alone. */
.ma-edit-chip {
    flex: 0 0 auto;
    font-size: 0.7692rem; font-weight: 700; letter-spacing: .4px;
    padding: 1px 8px; border-radius: 10px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    background: var(--amber-soft); color: #B77A15; border: 1px solid #F0DCB2;
}

/* "not range-checked" is the app saying it did not know. Muted on purpose:
   it is neither good news nor an error, and it must not compete with either. */
.ma-unchecked-chip {
    flex: 0 0 auto;
    font-size: 0.7692rem; font-weight: 600; letter-spacing: .3px;
    padding: 1px 8px; border-radius: 10px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    background: var(--canvas); color: var(--text-muted);
    border: 1px dashed var(--line-strong);
    cursor: help;
}

/* the popup is placed against this section's box */
#page-model-array { position: relative; }

/* --- "apply to all models?" ------------------------------------------
   It appears beside the cell that was just changed, because that is where
   the eye already is. */
.ma-ask {
    position: absolute;
    z-index: 40;
    background: #fff;
    border: 1px solid var(--line-strong);
    border-radius: 3px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .16);
    padding: 9px 11px;
    font-size: 0.8846rem;
    max-width: 340px;
}
.ma-ask-q { color: var(--text-strong); margin-bottom: 7px; }
.ma-ask-acts { display: flex; gap: 6px; justify-content: flex-end; }

/* The key that answers it, on the button that answers it. A shortcut nobody
   is told about is a shortcut nobody uses. */
.ma-key {
    display: inline-block;
    margin-left: 5px;
    padding: 0 4px;
    border: 1px solid rgba(255, 255, 255, .55);
    border-radius: 3px;
    font-size: 0.9em;
    line-height: 1.35;
    opacity: .9;
}

#issuePanel { flex: 0 0 auto; }
#issuePanel .issue-list { max-height: none; overflow: visible; }

#exportPanel { flex: 0 0 auto; }
#exportPanel .grid3 { grid-template-columns: 1fr; }
#exportPanel .field.span2 { grid-column: auto; }

.splitter {
    flex: 0 0 7px;
    cursor: col-resize;
    border-radius: 3px;
    background: var(--line-strong);     /* visible at rest, not invisible */
    transition: background .12s;
    position: relative;
}
.splitter::after {                      /* grip marks, so it reads as a handle */
    content: '';
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 3px; height: 34px;
    border-radius: 2px;
    background: #fff;
    opacity: .75;
}
.splitter:hover, body.side-resizing .splitter { background: var(--accent); }
.splitter:hover::after, body.side-resizing .splitter::after { opacity: 1; }
body.side-resizing { cursor: col-resize; user-select: none; }

/* the stats strip is narrower here - let the tiles wrap instead of squeezing */
.col-main .stat { flex: 1 1 110px; }

.page-head { margin-bottom: 14px; }
.page-head h1 { margin: 0; font-size: 1.5385rem; font-weight: 400; color: var(--text-strong); }
.page-head p { margin: 3px 0 0; font-size: 0.9231rem; color: var(--text-muted); }

/* ------------------------------------------------------------- PANEL --- */

.panel {
    background: var(--panel);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    margin-bottom: 11px;
}
.panel-head {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 15px;
    border-bottom: 2px solid var(--line);
}
.panel-head h2 {
    margin: 0; font-size: 1.0769rem; font-weight: 500; color: var(--text-strong);
}
.panel-head .sub { font-size: 0.8846rem; color: var(--text-muted); }
.panel-head .spacer { flex: 1 1 auto; }
.panel-body { padding: 11px 15px; }
.panel-body.tight { padding: 0; }

/* ---- right column: the section bar wears the table header's livery ---- */
.col-side .panel-head {
    background: var(--nav-bg);
    border-bottom: 0;
    padding: 7px 12px;
}
.col-side .panel-head h2 {
    color: #fff;
    font-size: 0.8077rem;
    font-weight: 600;
    letter-spacing: .3px;
    text-transform: uppercase;
}
.col-side .panel-head .sub { color: rgba(255, 255, 255, .72); }

/* controls sitting on the dark bar */
.col-side .panel-head .btn {
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .22);
    color: #fff;
}
.col-side .panel-head .btn:hover:not(:disabled) {
    background: rgba(255, 255, 255, .20);
    border-color: rgba(255, 255, 255, .35);
}
.col-side .panel-head .btn:disabled { opacity: .38; }
.col-side .panel-head .rule-chip {
    background: rgba(255, 255, 255, .12);
    border-color: rgba(255, 255, 255, .22);
    color: #fff;
}
.col-side .panel-head .rule-chip.bad {
    background: var(--red); border-color: var(--red); color: #fff;
}
/* the status-count legend */
.col-side .panel-head .lg { color: rgba(255, 255, 255, .78); }

/* ------------------------------------------------------------ BUTTON --- */

.btn {
    display: inline-flex; align-items: center; gap: 6px;
    font-family: inherit; font-size: 0.9231rem; font-weight: 500;
    padding: 6px 13px;
    border-radius: var(--radius);
    border: 1px solid var(--line-strong);
    background: #fff; color: var(--text);
    cursor: pointer;
    white-space: nowrap;
}
.btn:hover { background: #F2F4F6; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn:disabled:hover { background: #fff; }

.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--accent-dark); border-color: var(--accent-dark); }
.btn-dark { background: var(--nav-bg); border-color: var(--nav-bg); color: #fff; }
.btn-dark:hover:not(:disabled) { background: var(--nav-hover); }
.btn-danger { color: var(--red); border-color: #F0C4BF; }
.btn-danger:hover:not(:disabled) { background: var(--red-soft); }
.btn-sm { padding: 3px 8px; font-size: 0.8462rem; }
.btn-icon { padding: 4px 7px; font-size: 0.9231rem; line-height: 1; }

/* ------------------------------------------------------------- FORMS --- */

.field { margin-bottom: 12px; }
.field label {
    display: block;
    font-size: 0.8462rem; font-weight: 600; letter-spacing: .3px;
    color: var(--text-strong);
    margin-bottom: 4px;
    text-transform: uppercase;
}
.field .hint { font-size: 0.8077rem; color: var(--text-muted); margin-top: 3px; font-weight: 400; text-transform: none; }
.field .hint.err { color: var(--red); }
.field .hint.ok  { color: var(--accent-dark); }

input[type=text], input[type=number], select, textarea {
    width: 100%;
    font-family: inherit; font-size: 0.9615rem;
    color: var(--text);
    padding: 6px 9px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: #fff;
}
input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px rgba(26, 187, 156, .15);
}
input.invalid, select.invalid, textarea.invalid { border-color: var(--red); }
textarea { resize: vertical; min-height: 58px; }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
.field.span2 { grid-column: span 2; }
.grid3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0 12px; }

/* ----------------------------------------------------------- TOOLBAR --- */

.toolbar {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 15px;
    border-bottom: 1px solid var(--line);
    background: #FCFDFD;
    flex-wrap: wrap;
}
.toolbar .sep { width: 1px; height: 20px; background: var(--line-strong); margin: 0 3px; }
.toolbar .spacer { flex: 1 1 auto; }
.toolbar input[type=text] { width: 140px; }   /* two-thirds of the old 210 */
.toolbar select { width: auto; min-width: 108px; }

/* ---- search box with an inline Clear ---- */
/* The wrapper is inline-flex so the field keeps the height the toolbar's
   other controls have; a plain block would have made it a row of its own. */
.search-wrap { position: relative; display: inline-flex; }

/* Room for the button, so a long query never runs underneath it. */
.search-wrap input[type=text] { padding-right: 26px; }

.search-clear {
    position: absolute;
    top: 50%; right: 5px;
    transform: translateY(-50%);
    width: 17px; height: 17px;
    display: flex; align-items: center; justify-content: center;
    padding: 0;
    border: 0; border-radius: 50%;
    background: var(--line-strong);
    color: #fff;
    font-family: inherit;
    font-size: 0.8462rem; line-height: 1;
    cursor: pointer;
}
.search-clear:hover { background: var(--text-muted); }
.search-clear:active { background: var(--text); }
.search-clear.hide { display: none; }

/* ------------------------------------------------------------- TABLE --- */

.table-wrap {
    overflow: auto;
    min-height: 140px;
    border-top: 1px solid var(--line);
}

table.mm {
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.8846rem;
    table-layout: fixed;          /* header widths drive the columns */
    width: max-content;
}

table.mm thead th {
    position: sticky; top: 0; z-index: 3;
    background: var(--nav-bg);
    color: #fff;
    font-weight: 600;
    font-size: 0.8077rem;
    letter-spacing: .3px;
    text-transform: uppercase;
    text-align: left;
    padding: 8px 9px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border-right: 1px solid rgba(255, 255, 255, .08);
}

/* ---- drag-to-resize handle ---- */
table.mm thead th { position: sticky; }
.col-resizer {
    position: absolute;
    top: 0; right: 0;
    width: 7px; height: 100%;
    cursor: col-resize;
    user-select: none;
}
.col-resizer:hover { background: var(--accent); }
body.col-resizing { cursor: col-resize; user-select: none; }
body.col-resizing .col-resizer { background: var(--accent); }

table.mm tbody td {
    padding: 5px 9px;
    border-bottom: 1px solid var(--line);
    border-right: 1px solid #F2F4F6;
    vertical-align: top;
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

table.mm tbody tr:hover td { background: #F6FBFA; }
table.mm tbody tr.selected td { background: var(--accent-soft); }
table.mm tbody tr.selected td.sticky-1 { box-shadow: inset 3px 0 0 var(--accent); }
table.mm tbody tr.is-new td { background: #EAFBF6; }
table.mm tbody tr.is-new:hover td { background: #DFF7F0; }
table.mm tbody tr.is-mod td { background: #FDF7E9; }
table.mm tbody tr.has-error td { background: var(--red-soft); }
table.mm tbody tr.spacer td { background: #FAFAFA; height: 12px; }

table.mm td.col-num, table.mm th.col-num { text-align: right; font-variant-numeric: tabular-nums; }

table.mm td.col-param { font-weight: 600; color: var(--text-strong); }
table.mm td.cell-error { box-shadow: inset 0 0 0 1px var(--red); }

/* frozen columns: Action, # and Parameter. The left offsets are set from
   the live column widths in applyWidths(), because they are resizable. */
table.mm th.sticky-1, table.mm td.sticky-1,
table.mm th.sticky-2, table.mm td.sticky-2 { position: sticky; z-index: 2; }
table.mm thead th.sticky-1,
table.mm thead th.sticky-2 { z-index: 4; }

table.mm tbody td.sticky-1,
table.mm tbody td.sticky-2 { background: #fff; }
table.mm tbody tr:hover td.sticky-1,
table.mm tbody tr:hover td.sticky-2 { background: #F6FBFA; }
table.mm tbody tr.selected td.sticky-1,
table.mm tbody tr.selected td.sticky-2 { background: var(--accent-soft); }
table.mm tbody tr.is-new td.sticky-1,
table.mm tbody tr.is-new td.sticky-2 { background: #EAFBF6; }
table.mm tbody tr.is-mod td.sticky-1,
table.mm tbody tr.is-mod td.sticky-2 { background: #FDF7E9; }
table.mm tbody tr.has-error td.sticky-1,
table.mm tbody tr.has-error td.sticky-2 { background: var(--red-soft); }
table.mm td.sticky-2 { border-right: 1px solid var(--line-strong); }

.row-acts { display: flex; gap: 3px; }
.row-acts button {
    border: 1px solid transparent; background: none; cursor: pointer;
    font-size: 0.9231rem; line-height: 1; padding: 2px 4px; border-radius: 2px;
    color: var(--text-muted);
}
.row-acts button:hover { background: #fff; border-color: var(--line-strong); color: var(--text-strong); }
.row-acts button.del:hover { color: var(--red); border-color: #F0C4BF; }
/* The bin is drawn, not typed - there is no bin in the text fonts, only an
   emoji, and an emoji is a picture the page cannot colour. Sized in em so the
   font controls still scale it, stroked in currentColor so :hover reddens it
   with everything else. */
.row-acts button.del svg { display: block; width: 1em; height: 1em; }

.tag {
    display: inline-block;
    font-size: 0.7308rem; font-weight: 700; letter-spacing: .4px;
    padding: 1px 5px; border-radius: 2px; text-transform: uppercase;
}
.tag-new { background: var(--accent); color: #fff; }
.tag-mod { background: var(--amber); color: #fff; }
.tag-err { background: var(--red); color: #fff; }

/* --------------------------------------------------------- DROP ZONE --- */

.grid-empty {
    padding: 34px 20px;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.9231rem;
}
.grid-empty b { color: var(--text-strong); font-weight: 600; }
.grid-empty code {
    background: var(--canvas); border: 1px solid var(--line-strong);
    padding: 1px 5px; border-radius: 2px; font-size: 0.8462rem;
}

/* whole-window drop target - costs no layout space */
body.dragging .content {
    outline: 2px dashed var(--accent);
    outline-offset: -7px;
    background: var(--green-soft);
}

/* ------------------------------------------------- INLINE CELL EDITOR --- */

table.mm td.cell { cursor: default; }
table.mm td.editing { padding: 0; box-shadow: inset 0 0 0 2px var(--accent); }
table.mm td.editing .cv { display: none; }

.cell-input {
    width: 100%;
    min-width: 90px;
    border: 0;
    border-radius: 0;
    padding: 4px 8px;
    font-family: inherit;
    font-size: inherit;
    color: var(--text-strong);
    background: #fff;
}
.cell-input:focus { outline: none; box-shadow: none; }
select.cell-input { padding: 3px 6px; }

table.mm td.empty-cell { max-width: none; white-space: normal; }

/* ------------------------------------------------------------- STATS --- */

.stats { display: flex; gap: 0; flex-wrap: wrap; border-bottom: 1px solid var(--line); }
.stat {
    flex: 1 1 140px;
    padding: 12px 15px;
    border-right: 1px solid var(--line);
}
.stat:last-child { border-right: 0; }
.stat .k { font-size: 0.8077rem; text-transform: uppercase; letter-spacing: .5px; color: var(--text-muted); }
.stat .v { font-size: 1.6154rem; font-weight: 300; color: var(--text-strong); line-height: 1.25; }
.stat .v.accent { color: var(--accent); }
.stat .v.warn   { color: var(--amber); }
.stat .v.err    { color: var(--red); }

/* ------------------------------------------------------------ ISSUES --- */

.issue-list { max-height: 190px; overflow: auto; }
.issue {
    display: flex; gap: 8px; align-items: baseline;
    padding: 6px 15px;
    border-bottom: 1px solid var(--line);
    font-size: 0.8846rem;
    cursor: pointer;
}
.issue:hover { background: var(--canvas); }
.issue .lvl {
    flex: 0 0 auto; font-size: 0.7308rem; font-weight: 700;
    padding: 1px 5px; border-radius: 2px; text-transform: uppercase;
}
.issue.error .lvl { background: var(--red-soft); color: var(--red); }
.issue.warn  .lvl { background: var(--amber-soft); color: #B77A15; }
.issue .who { font-weight: 600; color: var(--text-strong); }
.empty-note { padding: 16px 15px; color: var(--text-muted); font-size: 0.9231rem; text-align: center; }

/* ------------------------------------------------------------ DIALOG --- */

dialog.side {
    border: 0; padding: 0; margin: 0;
    position: fixed; top: 0; right: 0; left: auto;
    height: 100vh; max-height: 100vh;
    width: 520px; max-width: 96vw;
    background: #fff;
    box-shadow: -3px 0 18px rgba(42, 63, 84, .18);
}
dialog.side::backdrop { background: rgba(42, 63, 84, .35); }

.dlg { display: flex; flex-direction: column; height: 100%; }
.dlg-head {
    flex: 0 0 auto;
    background: var(--nav-bg); color: #fff;
    padding: 13px 18px;
    display: flex; align-items: center; gap: 10px;
}
.dlg-head h3 { margin: 0; font-size: 1.1538rem; font-weight: 500; }
.dlg-head .sub { font-size: 0.8462rem; color: var(--nav-muted); }
.dlg-head .spacer { flex: 1 1 auto; }
.dlg-head button {
    background: none; border: 0; color: var(--nav-muted);
    font-size: 1.3846rem; cursor: pointer; line-height: 1;
}
.dlg-head button:hover { color: #fff; }

.dlg-body { flex: 1 1 auto; overflow-y: auto; padding: 16px 18px; }
.dlg-foot {
    flex: 0 0 auto;
    padding: 11px 18px;
    border-top: 1px solid var(--line);
    background: #FCFDFD;
    display: flex; gap: 8px; align-items: center;
}
.dlg-foot .spacer { flex: 1 1 auto; }

.tabs { display: flex; gap: 0; border-bottom: 1px solid var(--line); margin-bottom: 14px; }
.tab {
    padding: 7px 14px; font-size: 0.9231rem; cursor: pointer;
    border-bottom: 2px solid transparent; color: var(--text-muted);
}
.tab:hover { color: var(--text-strong); }
.tab.active { color: var(--accent-dark); border-bottom-color: var(--accent); font-weight: 600; }

.preview-box {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    max-height: 220px; overflow: auto;
    font-size: 0.8462rem;
}
.preview-box table { width: 100%; border-collapse: collapse; }
.preview-box th {
    position: sticky; top: 0;
    background: var(--canvas); color: var(--text-strong);
    text-align: left; padding: 5px 8px; font-size: 0.7692rem;
    text-transform: uppercase; letter-spacing: .3px;
    border-bottom: 1px solid var(--line);
}
.preview-box td { padding: 4px 8px; border-bottom: 1px solid var(--line); }
.preview-box td.bad { color: var(--red); }

/* ------------------------------------------------------------- TOAST --- */


/* ------------------------------------------------------- PLACEHOLDER --- */

.placeholder {
    text-align: center;
    padding: 70px 20px;
    color: var(--text-muted);
}
.placeholder .big { font-size: 3.2308rem; opacity: .35; }
.placeholder h2 { margin: 12px 0 5px; font-size: 1.3077rem; font-weight: 400; color: var(--text-strong); }
.placeholder p { margin: 0; font-size: 0.9615rem; }

.note {
    /* one weight all round - see .gen-block.warn */
    border: 1px solid var(--amber);
    background: var(--amber-soft);
    border-radius: var(--radius);
    padding: 9px 12px;
    margin-bottom: 12px;
    font-size: 0.8846rem;
    color: #7A5A18;
    max-width: 760px;
}
.note code {
    background: #fff; border: 1px solid #EBD9B4;
    padding: 0 4px; border-radius: 2px; font-size: 0.8462rem;
}

.hide { display: none !important; }


/* ------------------------------------------------ ACTIVE CELL CURSOR --- */

table.mm td.active-cell {
    box-shadow: inset 0 0 0 2px var(--accent);
}
table.mm td.active-cell.cell-error {
    box-shadow: inset 0 0 0 2px var(--red);
}

/* --------------------------------------------------- RULE WARNING ----- */

.cell-input.bad { background: var(--red-soft); color: var(--red); }

.cell-warn {
    position: absolute;
    z-index: 20;
    left: 0; top: 100%;
    min-width: 100%;
    max-width: 340px;
    width: max-content;
    background: var(--red);
    color: #fff;
    font-size: 0.8077rem;
    line-height: 1.35;
    padding: 4px 8px;
    border-radius: 0 0 var(--radius) var(--radius);
    box-shadow: 0 3px 10px rgba(42, 63, 84, .25);
    white-space: normal;
}
/*
 * The warning hangs off the bottom of the cell, so the cell has to be a
 * positioned element for it to hang off.
 *
 * `relative` on its own is wrong for the two FROZEN columns, and visibly so.
 * They are `position: sticky` with an inline `left` written by
 * setStickyLeft() - Parameter's is the Action column's width. Turning the
 * cell relative does not drop that inline `left`, it re-reads it: on a
 * relatively positioned element `left` is an offset from where the element
 * would otherwise be, so double-clicking Parameter to edit it shoved the cell
 * to the right by exactly the width of Action.
 *
 * Sticky is a positioned element too, so the frozen cells keep it and the
 * warning still anchors to them - and the column stays frozen while it is
 * being typed in, which it should have been all along.
 */
table.mm td.editing { position: relative; }
table.mm td.sticky-1.editing,
table.mm td.sticky-2.editing { position: sticky; }
.cell-warn.as-warn { background: var(--amber); color: #4A3608; }


/* map version / live error count, in the grid header */
.err-chip {
    flex: 0 0 auto;
    font-size: 0.7692rem; font-weight: 700; letter-spacing: .4px;
    padding: 1px 8px; border-radius: 10px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    background: var(--red-soft); color: var(--red); border: 1px solid #F0C4BF;
}

/* the loaded map's version - boxed at the head of the toolbar, where the eye
   lands before it reaches the buttons */
.ver-box {
    flex: 0 0 auto;
    display: flex; align-items: baseline; gap: 6px;
    padding: 4px 11px;
    border: 2px solid var(--nav-bg);
    border-radius: var(--radius);
    background: #fff;
    white-space: nowrap;
}
/* the label reads as a column heading, so it wears the heading's type */
.ver-box .k {
    font-family: inherit;
    font-size: 0.8077rem; font-weight: 600; letter-spacing: .3px;
    text-transform: uppercase;
    color: var(--nav-bg);
}
/* larger than the label so the version is the thing you read first, and in
   --accent: the same fill as the Download .xlsx button */
.ver-box .v {
    font-family: inherit;
    font-size: 1.1538rem; font-weight: 700; letter-spacing: .3px;
    color: var(--accent);
}

.rule-chip.bad {
    color: var(--red);
    background: var(--red-soft);
    border-color: #F0C4BF;
}

.rule-chip {
    font-size: 0.8077rem;
    color: var(--accent-dark);
    background: var(--green-soft);
    border: 1px solid #BFE9DE;
    border-radius: 10px;
    padding: 1px 9px;
    cursor: help;
    white-space: nowrap;
}


/* ------------------------------------------------- MENU REORDER (DND) --- */

.nav-group { display: block; }

.nav-item { position: relative; }
.nav-item[draggable="true"] { cursor: grab; }
.nav-item.dragging { opacity: .45; cursor: grabbing; }

.nav-item.drop-before::after,
.nav-item.drop-after::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    height: 2px;
    background: var(--accent);
    pointer-events: none;
}
.nav-item.drop-before::after { top: 0; }
.nav-item.drop-after::after { bottom: 0; }


/* ------------------------------------------------ PROCESSING SEQUENCE --- */

#procPanel { flex: 0 0 auto; }

/* dedicated scroll container - the list gets long */
.chk-scroll {
    max-height: var(--chk-h, 340px);
    overflow-y: auto;
    overflow-x: hidden;
}

/* horizontal grab bar under the Processing panel */
.hsplit {
    flex: 0 0 7px;
    margin: -2px 0;
    cursor: row-resize;
    border-radius: 3px;
    background: var(--line-strong);     /* visible at rest */
    transition: background .12s;
    position: relative;
}
.hsplit::after {                 /* the grip */
    content: '';
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 42px; height: 3px;
    border-radius: 2px;
    background: #fff;
    opacity: .75;
}
.hsplit:hover::after, body.proc-resizing .hsplit::after { opacity: 1; }
.hsplit:hover { background: var(--accent); }
.hsplit:hover::after { background: #fff; }
body.proc-resizing { cursor: row-resize; user-select: none; }
body.proc-resizing .hsplit { background: var(--accent); }

.chk-list { padding: 3px 0 6px; }

.chk-phase {
    padding: 8px 12px 3px;
    font-size: 0.7692rem; font-weight: 700; letter-spacing: .9px;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* ---- collapsed: exactly one line ---- */
.chk { position: relative; }

.chk-line {
    display: flex; align-items: center; gap: 8px;
    padding: 4px 12px;
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}
.chk-line:hover { background: #F6FBFA; }

.chk-node {
    flex: 0 0 19px;
    width: 19px; height: 19px;
    /* A rounded square, not a circle - the engineer's call. `--radius` is the
       page's own 3px, so this corner matches every other corner rather than
       being a fourth opinion about roundness. */
    border-radius: var(--radius);
    display: flex; align-items: center; justify-content: center;
    font-size: 0.7308rem; font-weight: 700;
    font-variant-numeric: tabular-nums;
    background: #fff;
    border: 2px solid var(--text);      /* dark grey, not a hairline */
    color: var(--text);
}

/* the title keeps its width; the detail is what gives way first */
.chk-title {
    flex: 0 1 auto;
    min-width: 5rem;
    overflow: hidden; text-overflow: ellipsis;
    font-size: 0.8846rem; font-weight: 600; color: var(--text-strong);
}
.chk-detail {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden; text-overflow: ellipsis;
    text-align: right;
    font-size: 0.8846rem; color: var(--text-muted);
}
.chk-icon {
    flex: 0 0 17px;
    width: 17px; height: 17px;
    /* Square, like the number beside it - see .chk-node. Round marks against
       a page of straight edges read as noise. */
    border-radius: var(--radius);
    display: flex; align-items: center; justify-content: center;
    font-size: 0.7692rem; font-weight: 700; line-height: 1;
    background: var(--canvas); color: var(--text-muted);
}


/* summary chips in the panel head - they are also the icon legend */
#procSummary { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }
.lg {
    display: inline-flex; align-items: center; gap: 3px;
    font-size: 0.7308rem; font-variant-numeric: tabular-nums;
    color: var(--text-muted);
    cursor: help;
}
.lg i {
    font-style: normal;
    width: 14px; height: 14px;
    border-radius: var(--radius);   /* the same mark, smaller - see .chk-icon */
    display: flex; align-items: center; justify-content: center;
    font-size: 0.6923rem; font-weight: 700; line-height: 1;
    background: var(--canvas); color: var(--text-muted);
}
.lg.pass i    { background: var(--accent); color: #fff; }
.lg.fail i    { background: var(--red);    color: #fff; }
.lg.warn i    { background: var(--amber);  color: #fff; }
.lg.pending i { background: var(--text); color: #fff; font-size: 0.7692rem; }

/* ---- expanded detail ---- */
.chk-body { display: none; padding: 2px 12px 10px 39px; }
.chk.open .chk-body { display: block; }
.chk.open .chk-line { background: #F1F7F6; }
.chk.open .chk-detail { visibility: hidden; }

.chk-result {
    font-size: 0.8846rem; color: var(--text-strong);
    margin-bottom: 5px;
}
.chk-why {
    font-size: 0.8846rem; line-height: 1.6;
    color: var(--text);              /* dark grey on the light panel */
    padding: 6px 9px;
    background: var(--canvas);
    border-left: 2px solid var(--line-strong);
    border-radius: 0 2px 2px 0;
    /* the text is written with paragraph breaks and indented code blocks;
       `normal` would collapse every one of them */
    white-space: pre-wrap;
}
.chk-jump { margin-top: 7px; }

/* ---- status colours: the icon carries the result ---- */
.chk.pass .chk-node { border-color: var(--accent); color: var(--accent-dark); }
.chk.pass .chk-icon { background: var(--accent); color: #fff; }

.chk.fail .chk-node { border-color: var(--red); color: var(--red); }
.chk.fail .chk-icon { background: var(--red); color: #fff; }
.chk.fail .chk-title { color: var(--red); }

.chk.warn .chk-node { border-color: var(--amber); color: #B77A15; }
.chk.warn .chk-icon { background: var(--amber); color: #fff; }

.chk.pending .chk-node {
    border-color: var(--text);
    color: var(--text);
}
.chk.pending .chk-title { color: var(--text-muted); font-weight: 500; }
.chk.pending .chk-icon {
    background: var(--text);            /* dark grey - the most legible chip */
    color: #fff;
    font-size: 0.8462rem;               /* the hourglass is a narrow glyph */
}



/* ------------------------------------------- FINDINGS INSIDE A CHECK --- */

.chk-findings { margin: 4px 0 6px; }
.finding {
    display: flex; gap: 6px; align-items: baseline;
    padding: 3px 7px;
    border-left: 2px solid var(--line-strong);
    font-size: 0.8846rem;
    cursor: pointer;
    white-space: normal;
}
.finding:hover { background: var(--canvas); }
.finding.error { border-left-color: var(--red); }
.finding.warn  { border-left-color: var(--amber); }
.finding.more  { color: var(--text-muted); cursor: default; border-left-color: transparent; }
.f-who { font-weight: 600; color: var(--text-strong); flex: 0 0 auto; }
.f-msg { color: var(--text); }

/* results go stale as soon as the map is edited again */
.panel.stale .panel-head::after {
    content: 'stale - press Process';
    font-size: 0.7308rem;
    color: #B77A15;
    background: var(--amber-soft);
    border: 1px solid #F3D9A8;
    border-radius: 8px;
    padding: 1px 7px;
    margin-left: 6px;
}
.panel.stale .chk-list, .panel.stale .snip-list { opacity: .55; }

/* ------------------------------------------------------------ SNIPPETS --- */

/* Snippets is the LAST panel in the right column, so it takes what is left
   rather than stopping at a number. It was capped at a flat 420px, which left
   dead space under it on a tall window and a scrollbar on a short one.

   `--snip-h` is set only when the divider above it is DRAGGED (see
   initPanelSplitter). So the default is "fill", and a drag still overrides it
   - the divider keeps working and doing nothing is no longer a fixed size. */
#snipPanel {
    flex: 1 1 auto;
    min-height: 160px;
    display: flex;
    flex-direction: column;
}
/* Generate Files is what the divider below it sizes. `height: var(--gen-h)`
   is INVALID while the variable is unset, which computes to `height: auto` -
   so the untouched default is still "as tall as its contents" and a drag
   pins it. Snippets absorbs the difference, so the column total never moves
   and dragging can no longer push it into a scroll. */
#genPanel {
    flex: 0 0 auto;
    min-height: 120px;
    height: var(--gen-h);
    display: flex;
    flex-direction: column;
}
#genPanel .panel-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}
#snipPanel .panel-body {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
.snip-scroll {
    flex: 1 1 auto;
    min-height: 0;
    max-height: var(--snip-h, none);
    overflow-y: auto; overflow-x: hidden;
}
.snip-list { padding: 6px 0 8px; }

.snip-note {
    margin: 0 12px 6px;
    font-size: 0.8846rem; color: var(--text-muted);
}

.snip-group { border-top: 1px solid var(--line); padding: 7px 12px 4px; }
.snip-group:first-child { border-top: 0; }

.snip-head { display: flex; align-items: baseline; gap: 7px; margin-bottom: 5px; }
.snip-kind {
    flex: 0 0 auto;
    font-size: 0.6923rem; font-weight: 700; letter-spacing: .4px;
    text-transform: uppercase;
    padding: 1px 6px; border-radius: 8px;
}
.snip-kind.add    { background: var(--green-soft); color: var(--accent-dark); }
.snip-kind.change { background: var(--amber-soft); color: #B77A15; }
.snip-kind.delete { background: var(--red-soft);   color: var(--red); }
.snip-param {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: 0.8846rem; font-weight: 600; color: var(--text-strong);
}
.snip-addr {
    flex: 0 0 auto;
    font-size: 0.8846rem; color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.snip { margin-bottom: 7px; }
.snip-meta {
    display: flex; align-items: center; gap: 6px;
    font-size: 0.8846rem;
    margin-bottom: 2px;
}
.snip-file { font-weight: 600; color: var(--text-strong); flex: 0 0 auto; }
/* where the snippet goes - the one field you must not misread, so it is the
   only coloured thing on the meta line */
.snip-line {
    flex: 0 0 auto;
    color: var(--red); font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.snip-where {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    color: var(--text-muted);
}
.snip-copy { flex: 0 0 auto; padding: 1px 8px; font-size: 0.7308rem; }

.snip-code {
    margin: 0;
    padding: 6px 9px;
    background: #EAFBF6;                /* same green as a newly added row */
    color: var(--text-strong);
    border: 1px solid #BFE9DE;
    border-radius: var(--radius);
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.8846rem;
    line-height: 1.5;
    overflow-x: auto;
    white-space: pre;
    tab-size: 4;
}
.snip-code.removed {
    background: var(--red-soft);
    border-color: #F0C4BF;
    color: #7A2E24;
}

.snip-hint {
    margin-top: 3px;
    font-size: 0.8846rem; color: #B77A15;
    white-space: pre-wrap;
    line-height: 1.55;
}

.offscreen { position: fixed; left: -9999px; top: 0; }



/* icon pair in a panel head (expand / collapse) */
.icon-group { display: flex; gap: 0; flex: 0 0 auto; }
.icon-group .btn-icon {
    padding: 2px 8px;
    font-size: 0.9231rem; font-weight: 700; line-height: 1.15;
    border-radius: 0;
}
.icon-group .btn-icon:first-child { border-radius: var(--radius) 0 0 var(--radius); }
.icon-group .btn-icon:last-child  { border-radius: 0 var(--radius) var(--radius) 0; margin-left: -1px; }


/* ------------------------------------------------ HOW TO FIX (a check) --- */

/* The row's address, beside its name - quieter than the name, since it is
   the lookup key rather than the subject. */
.f-addr {
    font-family: Consolas, "Courier New", monospace;
    font-weight: 600;
    opacity: .78;
}

/* The reasoning, on demand. Sized to the text rather than the window: long
   enough to read a paragraph without a ragged line, capped so it never fills
   the screen. */
.why-back {
    position: fixed; inset: 0; z-index: 1200;
    display: none;
    align-items: center; justify-content: center;
    background: rgba(42, 63, 84, .34);
    padding: 24px;
}
.why-pop {
    display: flex; flex-direction: column;
    width: min(720px, 100%); max-height: min(72vh, 100%);
    background: var(--panel);
    border: 1px solid var(--line-strong);
    border-radius: 4px;
    box-shadow: 0 12px 40px rgba(42, 63, 84, .28);
    overflow: hidden;
}
.why-head {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 10px;
    background: var(--nav-bg);          /* same livery as the section bars */
    border-bottom: 1px solid var(--line-strong);
}
.why-id {
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.8077rem; font-weight: 700;
    color: #fff; opacity: .82;
}
.why-title {
    flex: 1 1 auto; min-width: 0;
    font-size: 0.8846rem; font-weight: 600;
    letter-spacing: .3px;
    color: #fff;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.why-x {
    flex: none;
    background: none; border: 0; cursor: pointer;
    font-size: 1.15rem; line-height: 1;
    padding: 0 2px;
    color: #fff; opacity: .72;
}
.why-x:hover { opacity: 1; }
.why-body { padding: 10px; overflow: auto; }
.why-body .chk-why { background: none; border-left: 0; padding: 0; }

/* the circle is a button now */
.chk-node { cursor: help; }

/* Coverage, not a fix: no accent bar, no code box, nothing to copy.
   `pre` keeps the address column aligned; long names scroll rather than
   wrap into a ragged second line. */
.chk-notes { margin: 6px 0 8px; }
.note-head {
    font-size: 0.7308rem; font-weight: 700; letter-spacing: .6px;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 3px;
}
.note-head + .note-list { margin-bottom: 8px; }
.note-list {
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.8077rem; line-height: 1.5;
    color: var(--text);
    white-space: pre;
    overflow-x: auto;
    padding-left: 2px;
}

.chk-fix {
    margin: 6px 0 8px;
    border-left: 2px solid var(--accent);
    padding-left: 8px;
}
.fix-head {
    font-size: 0.7308rem; font-weight: 700; letter-spacing: .6px;
    text-transform: uppercase;
    color: var(--accent-dark);
    margin-bottom: 4px;
}
.fix { margin-bottom: 8px; }
.fix:last-child { margin-bottom: 0; }

.fix-meta {
    display: flex; align-items: center; gap: 6px;
    font-size: 0.8846rem;
    margin-bottom: 2px;
}
.fix-meta .snip-where { flex: 1 1 auto; }

/* Notes are written with line breaks and indented comparisons; `normal`
   would collapse every one of them. Same reasoning as .chk-why. */
.fix-note {
    margin-top: 3px;
    font-size: 0.8846rem; line-height: 1.55;
    color: var(--text);
    white-space: pre-wrap;
}
.fix-action { margin-bottom: 2px; }


/* --------------------------------------------------------- SNIPPET GATE --- */

.gate { padding: 14px 14px 16px; }

.gate-title {
    font-size: 0.9231rem; font-weight: 600;
    color: var(--text-strong);
    margin-bottom: 4px;
}
.gate.blocked .gate-title { color: var(--red); }
.gate.review  .gate-title { color: #B77A15; }

.gate-text {
    font-size: 0.8846rem; line-height: 1.55;
    color: var(--text);
    margin-bottom: 9px;
}

.gate-list { display: flex; flex-direction: column; gap: 3px; margin-bottom: 11px; }
.gate-item {
    text-align: left;
    font: inherit;
    font-size: 0.8846rem;
    color: var(--text);
    background: var(--canvas);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    padding: 4px 9px;
    cursor: pointer;
}
.gate-item:hover { border-color: var(--accent); background: #fff; }
.gate-item b { color: var(--text-strong); margin-right: 5px; font-variant-numeric: tabular-nums; }
.gate.blocked .gate-item { border-color: var(--red); }
.gate.review  .gate-item { border-color: var(--amber); }


.warn-banner {
    margin: 0 12px 8px;
    padding: 8px 10px;
    background: var(--amber-soft);
    border: 1px solid var(--amber);
    border-radius: var(--radius);
}
.warn-head {
    font-size: 0.8846rem; font-weight: 600;
    color: #B77A15;
    margin-bottom: 5px;
}
.warn-banner .gate-list { margin-bottom: 0; }
.warn-banner .gate-item { border-color: var(--amber); background: #fff; }

.snip-todo {
    margin: 0 12px 7px;
    padding: 6px 9px;
    background: var(--amber-soft);
    border: 1px solid #F3D9A8;
    border-radius: var(--radius);
    font-size: 0.8846rem;
    color: #7A5A18;
}
.snip-todo code {
    background: #fff; border: 1px solid #EBD9B4;
    padding: 0 3px; border-radius: 2px; font-size: inherit;
}


/* ------------------------------------------------ GENERATE FILE(S) --- */
/* States what would be written before anything is. Sized to the same base
   as the map table, like everything else in this column. */
.gen-body { padding: 8px 12px 0; }

.gen-block {
    font-size: 0.8846rem;
    line-height: 1.55;
    margin-bottom: 8px;
    color: var(--text);
}
.gen-block b { color: var(--text-strong); }
/* Red tone, and a plain rectangle: a thicker left edge only made the box
   look lopsided. Colour carries the state; the border stays 1px all round. */
.gen-block.warn {
    padding: 7px 9px;
    background: var(--red-soft);
    border: 1px solid #F0C4BF;
    border-radius: var(--radius);
    color: #8A3324;
}
.gen-block.warn b { color: var(--red); }

.gen-lines { margin-top: 6px; }
.gl {
    display: flex; gap: 8px; align-items: baseline;
    padding: 2px 0;
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.8462rem;
}
.gl-w {
    flex: 0 0 auto;
    color: var(--red); font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.gl-t {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    color: var(--text-strong);
}

.gen-block.bad {
    padding: 7px 9px;
    background: var(--red-soft);
    border: 1px solid var(--red);
    border-radius: var(--radius);
    color: var(--red);
}

/* the full rename table - same columns as the exported .xlsx */
.gen-scroll {
    max-height: 360px;
    overflow: auto;                 /* both directions: names are long */
    border: 1px solid var(--line-strong);
    border-radius: var(--radius) var(--radius) 0 0;   /* the grip closes it */
}

.gen-table {
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.8846rem;
    table-layout: fixed;          /* header widths drive the columns */
    /* no width here: the script sets it from the column widths, and
       `max-content` would have overridden anything narrower */
}
/* Light, so it cannot be read as a second section bar under the navy one
   directly above it. Same type as a table header, a different surface. */
.gen-table thead th {
    position: relative;           /* the resize grip anchors here */
    position: sticky; top: 0; z-index: 2;
    background: #E3E9EE;
    color: var(--text-strong);
    font-weight: 600;
    font-size: 0.8077rem;
    letter-spacing: .3px;
    text-transform: uppercase;
    text-align: left;
    padding: 6px 9px;
    white-space: nowrap;
    /* clip rather than push back, so a column can be dragged narrower than
       the word in its header */
    overflow: hidden;
    text-overflow: ellipsis;
    border-right: 1px solid #CFD8E0;
    box-shadow: inset 0 -2px 0 var(--line-strong);
}
.gen-table td {
    padding: 3px 9px;
    border-top: 1px solid var(--line);
    border-right: 1px solid var(--line);
    white-space: nowrap;
    vertical-align: top;
    overflow: hidden;
    text-overflow: ellipsis;      /* a dragged-narrow column truncates */
}
.gen-table tbody tr:nth-child(even) td { background: #FAFBFB; }
.gen-table tbody tr:hover td { background: var(--accent-soft); }

/* the rename whose parameter is selected in the map - same light green as a
   reviewed file and a newly added row, because it means the same thing:
   "this is the one you are looking at" */
/* the first row of each parameter's block */
.gen-table tbody tr.grp td { border-top: 1px solid var(--line-strong); }

.gen-table tbody tr.cur td { background: #EAFBF6; }
.gen-table tbody tr.cur td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

.gen-kind {
    display: inline-block;
    font-size: 0.7692rem; font-weight: 700; letter-spacing: .3px;
    text-transform: uppercase;
    padding: 0 6px; border-radius: 8px;
}
.gen-kind.define { background: var(--green-soft); color: var(--accent-dark); }
.gen-kind.member { background: #E8F1FA; color: #2A6496; }

.gen-table .mono { font-family: Consolas, "Courier New", monospace; }
.gen-table .old { color: var(--text-muted); text-decoration: line-through; }
.gen-table .new { color: var(--accent-dark); font-weight: 700; }
.gen-table .num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}
/* Past the 50-character pad in gst_Model_Master[]. Only worth flagging when
   re-alignment is switched OFF: with it on, the block is re-padded to its own
   longest name and nothing is left ragged. */
.gen-scroll.show-over .gen-table td.over { color: var(--red); font-weight: 700; }

.gen-warn {
    margin-top: 4px;
    color: #B77A15;
    font-weight: 600;
}

/* Why the button will not work, beside the button. This is the one thing in
   the panel the reader cannot act on without, so it takes the red - the same
   red an added line takes, because both mean "look here". Uniform border, no
   accent bar on one edge. */
.gen-stop {
    border: 1px solid var(--red);
    border-radius: var(--radius);
    background: var(--red-soft);
    padding: 8px 10px;
}
.gen-stop b { color: var(--red); }

/* Where the removed name is still read. BLUE, because nothing here changes
   and nothing is blocked - it is told to the engineer so they know what is
   waiting. Red is kept for lines that actually change, which is the only way
   it goes on meaning one thing. */
.gen-heads {
    border: 1px solid var(--blue);
    border-radius: var(--radius);
    background: #EAF4FB;
    padding: 8px 10px;
}
.gen-heads b { color: var(--blue-ink); }

/* The use sites themselves, read-only. The line is quoted because a file and
   a number send the reader hunting; the line tells them whether they care. */
.gen-uses { margin-top: 6px; }
.gen-uses-h {
    font-weight: 600;
    color: var(--text-muted);
}
.gen-use {
    display: flex;
    gap: 8px;
    align-items: baseline;
    padding: 1px 0;
    font-weight: 400;
}
.gen-use-at {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-size: 12px;
    min-width: 160px;
}
.gen-use code {
    /* The literal stack, because there is no `--mono` token - `.snip-code`
       and `.d-t` spell it out too. An undefined custom property does not
       fall back to anything: the declaration is invalid at computed-value
       time and the element quietly inherits the UI font, so this read as
       Segoe UI and nothing anywhere said so. Rule 6's shape, in CSS. */
    font-family: Consolas, "Courier New", monospace;
    font-size: 12px;
    white-space: pre;
    overflow: hidden;
    text-overflow: ellipsis;
}
.gen-stop-how {
    margin-top: 6px;
    color: var(--text-muted);
    font-weight: 400;
}

.gen-actions {
    display: flex; gap: 7px; align-items: center;
    padding: 0 12px 11px;
}
.gen-actions .spacer { flex: 1 1 auto; }
.gen-opt {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 0.8846rem;
    color: var(--text);
    cursor: pointer;
    user-select: none;
}
.gen-opt input { margin: 0; cursor: pointer; }


/* ------------------------------------------- CHANGED FILE LIST ------- */

.gen-files { padding: 0 12px 10px; }
.gen-files-scroll {
    max-height: 240px;
    overflow: auto;               /* sideways too, once columns are dragged */
    border-radius: var(--radius) var(--radius) 0 0;   /* the grip closes it */
}

/* The bottom edge of the table above it, with a handle - NOT a pane
   splitter. It was built by copying .hsplit and consequently read as one
   appearing between the two tables. Flush, light, muted grip mark. */
.tbl-grip {
    height: 11px;
    margin: -1px 0 9px;           /* sits on the table's own border */
    background: var(--canvas);
    border: 1px solid var(--line-strong);
    border-radius: 0 0 var(--radius) var(--radius);
    cursor: row-resize;
    position: relative;
    transition: background .12s, border-color .12s;
}
.tbl-grip::after {                /* two short rules, not the splitter's pill */
    content: '';
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 26px; height: 3px;
    border-top: 1px solid var(--text-muted);
    border-bottom: 1px solid var(--text-muted);
    opacity: .7;
}
.tbl-grip:hover {
    background: var(--accent-soft);
    border-color: var(--accent);
}
.tbl-grip:hover::after { opacity: 1; border-color: var(--accent-dark); }

/* how many rows now fit, while the bar is being dragged */
.tbl-grip[data-rows]:not([data-rows=""])::before {
    content: attr(data-rows);
    position: absolute;
    right: 4px; top: 50%;
    transform: translateY(-50%);
    font-size: 0.7692rem; font-weight: 700;
    color: #fff;
    background: var(--nav-bg);
    padding: 1px 7px;
    border-radius: 8px;
    white-space: nowrap;
}

body.tbl-resizing { cursor: row-resize; user-select: none; }
body.tbl-resizing .tbl-grip {
    background: var(--accent-soft);
    border-color: var(--accent);
}
/* The app's green, not muted grey. This line carries the count of files about
   to be overwritten and the breakdown by kind - the numbers a reviewer checks
   before committing to the change - and at --text-muted it read as a caption
   nobody was meant to look at. Same green as the Changes column beside it. */
/* The line above the file list: how many files are about to be overwritten and
   what kinds. It was --text-muted at 11.5px, 2.83:1 - a caption nobody was
   meant to read, carrying the numbers a reviewer checks before committing.
   The app green at 3.28:1 was better and still short. This is the same green
   walked down in lightness until it clears 4.5:1 (5.27), with the size up a
   step. Bold alone would not have fixed it: WCAG relaxes to 3.0 only above
   18.66px, and this line is nowhere near that. */
.gen-files-head {
    font-size: 1rem;
    color: #0F7A64;
    font-weight: 700;
    margin-bottom: 6px;
}
/* A grid, not a flex row: with an auto-width first column every row put its
   folder at a different x, and the header's FOLDER looked like it was missing.
   Shared column tracks are what make a header row mean anything. */
.gen-file {
    display: grid;
    grid-template-columns: var(--gf-cols, 5.5rem 17rem 1fr 5rem);
    /* `width: max-content` was here, and it is why the last track could never
       grow: inside a max-content box an `fr` has no free space to take, so the
       row stopped at the sum of the fixed columns and the right-hand header
       was clipped to "CHAN…". The pair below is the other way round - fill the
       pane, and overflow it only when the fixed columns need more than it
       has, which is what the scroll container is for. */
    width: 100%;
    min-width: max-content;
    align-items: baseline;
    padding: 3px 0;               /* the cells carry the horizontal padding */
    border: 1px solid var(--line);
    border-bottom: 0;
    font-size: 0.8846rem;
    cursor: pointer;
    user-select: none;
}

/* Column rules, as in the rename table. There is deliberately no grid `gap`:
   a border on a cell edge would float in the middle of one. */
.gen-file > span {
    padding: 0 9px;
    border-right: 1px solid var(--line);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.gen-file > span:last-child { border-right: 0; }
.gen-file.head > span { border-right-color: #CFD8E0; position: relative; }
.gen-file:first-of-type { border-radius: var(--radius) var(--radius) 0 0; }

/* the column names, in the rename table's own livery so the two lists in this
   panel read as the same kind of thing */
.gen-file.head {
    position: sticky; top: 0; z-index: 2;
    cursor: default;
    background: #E3E9EE;
    border-color: #CFD8E0;
    box-shadow: inset 0 -2px 0 var(--line-strong);
    cursor: default;
    font-size: 0.8077rem; font-weight: 600;
    letter-spacing: .3px;
    text-transform: uppercase;
    color: var(--text-strong);
}
.gen-file.head:hover { background: #E3E9EE; border-color: #CFD8E0; }
.gen-file.head .gf-name,
.gen-file.head .gf-dir,
.gen-file.head .gf-hits,
.gen-file.head .gf-done { color: inherit; font-weight: inherit; }
.gen-file:last-child {
    border-bottom: 1px solid var(--line);
    border-radius: 0 0 var(--radius) var(--radius);
}
.gen-file:hover { background: var(--accent-soft); border-color: var(--accent); }
.gf-name {
    font-weight: 600; color: var(--text-strong);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gf-dir {
    min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    color: var(--text-muted);
}
/* Left, like every other column. Right-aligning a count is the usual habit -
   it lines the digits up against each other - but this column now stretches to
   fill the pane, so the numbers were pushed far from the names they belong to
   and sat alone against the edge. `tabular-nums` still keeps the digits in
   step with each other. */
.gf-hits {
    font-variant-numeric: tabular-nums;
    font-weight: 700; color: var(--accent-dark);
}

/* opened in the viewer - stays marked, so a long list can be worked through
   without having to remember which ones were read */
.gen-file.done { background: #EAFBF6; }
.gen-file.done:hover { background: #D7F3E9; border-color: var(--accent); }

/* The tick has its own column. As a ::before on the file name it shifted every
   reviewed name two characters right - the column stayed put, the text in it
   did not. */
.gf-done { text-align: center; }
.gen-file.done .gf-done::before {
    content: '\2713';
    color: var(--accent-dark);
    font-weight: 700;
}

/* ------------------------------------------------ CHANGE VIEWER ------ */

.diff-wrap {
    position: fixed; inset: 0; z-index: 60;
    background: rgba(42, 63, 84, .45);
}
/* Placed by script so it can be dragged; resized by the browser's own grip,
   which `overflow: hidden` is what enables. */
.diff-dlg {
    position: absolute;
    display: flex; flex-direction: column;
    background: #fff;
    border-radius: var(--radius);
    box-shadow: 0 10px 40px rgba(42, 63, 84, .35);
    overflow: hidden;
    min-width: 34rem;
    min-height: 14rem;
}

/* Eight grips instead of the browser's single bottom-right one, so the
   pointer changes shape on every edge and corner. Edges are thin bands just
   outside the content; corners sit on top of them. */
.d-rz { position: absolute; z-index: 3; }
.d-rz.n  { top: -3px; left: 10px; right: 10px; height: 7px; cursor: ns-resize; }
.d-rz.s  { bottom: -3px; left: 10px; right: 10px; height: 7px; cursor: ns-resize; }
.d-rz.w  { left: -3px; top: 10px; bottom: 10px; width: 7px; cursor: ew-resize; }
.d-rz.e  { right: -3px; top: 10px; bottom: 10px; width: 7px; cursor: ew-resize; }
.d-rz.nw { top: -3px; left: -3px; width: 14px; height: 14px; cursor: nwse-resize; }
.d-rz.se { bottom: -3px; right: -3px; width: 14px; height: 14px; cursor: nwse-resize; }
.d-rz.ne { top: -3px; right: -3px; width: 14px; height: 14px; cursor: nesw-resize; }
.d-rz.sw { bottom: -3px; left: -3px; width: 14px; height: 14px; cursor: nesw-resize; }

/* a hint that the bottom-right corner is grabbable, as the native grip was */
.d-rz.se::after {
    content: '';
    position: absolute; right: 4px; bottom: 4px;
    width: 7px; height: 7px;
    border-right: 2px solid var(--line-strong);
    border-bottom: 2px solid var(--line-strong);
}

body.diff-resizing { user-select: none; }

.diff-head {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: 10px;
    padding: 7px 12px;
    background: var(--nav-bg);
    color: #fff;
    overflow: hidden;             /* nothing may push Close off the end */
    cursor: move;                 /* the bar is the drag handle */
    user-select: none;
    /* A- / A+ move this bar too. Its children were sized in `rem`, so they
       followed the APP's font and stayed put while the code under them grew -
       and the bar is where the file name, the path and the counts live, which
       is exactly what the user could not read. They are in `em` below, so the
       whole bar scales from here. */
    font-size: var(--diff-fs, 12px);
}
/* the controls on the bar are not drag handles */
.diff-head .btn,
.diff-head .diff-filter { cursor: pointer; }

/* A- / A+ grow the INFORMATION on this bar - the file name, its path, the
   counts - because that is what could not be read. The controls stay put, in
   `rem`: growing them too pushed Close out of a bar that is `overflow: hidden`,
   and a viewer you cannot close is worse than one you must squint at. */
.diff-head .btn,
.diff-head .fs-group button { font-size: 0.9231rem; }
.diff-head .diff-filter,
.diff-head .diff-pos { font-size: 0.8462rem; }

/* The title shrinks rather than shoving. At the largest size the fixed items
   alone filled the bar, and the first thing off the end was Close. */
.diff-head b {
    flex: 0 1 auto;
    font-size: 1em;
    white-space: nowrap;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.diff-head .spacer { flex: 1 1 auto; }
/* the one element that gives way when the bar runs out of room */
.diff-path {
    flex: 0 1 auto; min-width: 0;
    font-size: 0.875em; color: rgba(255, 255, 255, .6);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.diff-head .btn {
    flex: 0 0 auto;
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .22);
    color: #fff;
}
.diff-head .btn:hover:not(:disabled) { background: rgba(255, 255, 255, .20); }
.diff-head .btn:disabled { opacity: .38; }

/* second to yield, after the path: on a narrow window the counts clip
   rather than pushing Close out of the bar */
.diff-counts { display: flex; gap: 5px; flex: 0 1 auto; min-width: 0; overflow: hidden; }
.d-c {
    /* 1em, not 0.833: the bar is already smaller than the page, and taking
       another sixth off put the counts below the dropdown beside them - while
       the note above says A-/A+ grow the counts BECAUSE they are what could
       not be read. Relative, so the control still moves them. */
    font-size: 1em; font-weight: 700;
    /* `--radius`, like every other surface here. A full pill on a 14px chip
       curves the first and last characters away from the line the eye is
       scanning, which is the opposite of what a count is for. */
    padding: 1px 7px; border-radius: var(--radius);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Informing, not changing - the same blue as the panel block and the file
   row. A reference view showed its count in red, the colour that means "this
   line changes", on the one screen where nothing does. */
#diffCounts .d-c.info { background: var(--blue); color: #fff; }
#diffCounts .d-c.quiet {
    background: rgba(255, 255, 255, .16);
    color: rgba(255, 255, 255, .92);
}
.d-c.ren { background: var(--red); color: #fff; }
.d-c.add { background: var(--red); color: #fff; }
/* Red, like the rows it counts and like the chip beside it. Struck through so
   the two are told apart at a glance without a second colour. */
.d-c.del { background: var(--red); color: #fff; text-decoration: line-through; }

/* A file that only READS what is going. Not in the archive, so it does not
   take the green tick treatment of a reviewed file, and its count column
   says so in words rather than a number that would be read as changes. */
/* Plain. Never italic - the colour already says which kind of row this is,
   and a second face for one fact is the same overspend as a second red. */
.gen-file.ref .gf-hits { color: var(--blue-ink); }
.gen-file.ref .gf-name { color: var(--blue-ink); }
.d-c.sp  { background: rgba(255, 255, 255, .16); color: #fff; }
.d-c.tot { background: rgba(255, 255, 255, .10); color: rgba(255, 255, 255, .75); }

/* A select is as wide as its widest option, and the options carry their
   coverage in brackets. Pin the closed control; the list may be wider. */
.diff-filter {
    /* Shrinks before Close does. It was `0 0 auto`, so on a narrow dialog this
       one control held its full 15.5rem and pushed the close button out of a
       bar that clips its overflow. A truncated label is recoverable - the drop
       -down still lists every option in full - and a missing Close is not. */
    flex: 0 1 auto;
    width: 15.5rem;
    min-width: 3.5rem;
    font-family: inherit;
    font-size: 0.917em;
    padding: 3px 7px;
    border-radius: var(--radius);
    border: 1px solid rgba(255, 255, 255, .22);
    background: rgba(255, 255, 255, .10);
    color: #fff;
    cursor: pointer;
}
.diff-filter option { color: var(--text-strong); background: #fff; }
.diff-pos {
    flex: 0 0 auto;
    font-size: 0.917em; color: rgba(255, 255, 255, .75);
    font-variant-numeric: tabular-nums;
    min-width: 58px; text-align: center;
    white-space: nowrap;
}

.diff-body {
    flex: 1 1 auto;
    overflow: auto;
    background: #fff;
    /* Fixed monospace, and not the header's typeface.
       Following the app font was tried on request and reverted on sight: with
       a proportional face the columns stop lining up, and lining up is the
       whole subject of the re-spaced half of this viewer. The user's words -
       "Space가 깨지네". The SIZE is still the reader's to choose; the family
       is not, because getting it wrong costs the thing the viewer is for. */
    font-family: Consolas, "Courier New", monospace;
    /* Its own size, set from the popup's own A- / A+. In px, not rem: the
       viewer is read at a different distance from the panels behind it, and a
       size tied to the app's would move under you every time the GUI changed. */
    font-size: var(--diff-fs, 12px);
    line-height: 1.5;
    padding: 6px 0 20px;
}

/* the size stepper on the dark dialog header */
.diff-head .fs-group { flex: 0 0 auto; }
.diff-head .fs-group button {
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .22);
    color: #fff;
}
.diff-head .fs-group button:hover:not(:disabled) { background: rgba(255, 255, 255, .20); }
.diff-head .fs-group button:disabled { opacity: .38; }

/* A row is as wide as its CODE, never as wide as the popup.
   `.diff-body` scrolls horizontally, and a block child fills the CONTAINER,
   not the scroll extent - so a line longer than the popup was tinted only as
   far as the fold and the rest of it sat on white. Measured before the fix:
   content 1318px, painted box 420px, 898px unpainted. It looked intermittent
   because it depends on whether any line happens to exceed the current popup
   width, and the popup is resizable and remembers its size.
   `min-width: 100%` keeps short rows full-width, so the cursor tint and the
   added tint still span the visible line. */
.d-row {
    display: flex;
    white-space: pre;
    width: max-content;
    min-width: 100%;
}
.d-n {
    flex: 0 0 auto;
    width: 5.2rem;
    padding: 0 10px 0 0;
    text-align: right;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    user-select: none;
    border-right: 1px solid var(--line);
    margin-right: 10px;
}
.d-t { flex: 1 1 auto; color: var(--text-strong); }

/* An added line is new code from end to end, so the whole line is tinted and
   its text takes the same red - there is no "unchanged part" of it to leave
   alone. This is what the user asked for when insertion was agreed: "Code
   Snippet 전체가 빨강으로 보이겠지". */
.d-row.added { background: var(--red-ink); }
.d-row.added .d-t { color: #FFFFFF; }
/* No bar down the gutter. The row is tinted and the code is dark red
   already; a third mark for one fact, running the full height of the block,
   is the loudest thing on screen and says the least. */
.d-row.added .d-n {
    background: var(--red-ink);
    color: #FFFFFF;
}

/*
 * A removed line is red, like an added one.
 *
 * It was muted and struck through first, on the reasoning that red is a
 * budget and new code already spends it. Tried on screen, that was wrong in
 * the way that matters: the user could not see it. An emphasis rule exists to
 * keep the important thing legible, so a reading of it that hides the
 * important thing has been misapplied.
 *
 * The budget is not overspent here, because red still carries ONE meaning in
 * this view - "this line is not the same as the file you loaded". Which
 * direction it moved is said by the strike and by the dash in the gutter,
 * neither of which competes for attention with the colour.
 */
.d-row.removed { background: var(--red-ink); }
.d-row.removed .d-t {
    color: #FFFFFF;
    text-decoration: line-through;
}
.d-row.removed .d-n {
    background: var(--red-ink);
    color: #FFFFFF;
}

/* A renamed identifier, in place. Plain weight: the colour and the tint
   already carry it, and bold on top was a second mark for the same fact.
   One step darker than --red for the glyphs, not two. The palette red washed
   out on this pale tint at code size; #A82315 was tried next and went the
   other way - dark enough that it stopped reading as a highlight and started
   reading as ordinary dark text. #C0392B is the same hue with the brightness
   taken down once. Kept local: --red itself is used for chips and buttons,
   where it sits under white text and is right as it stands. */
/* The renamed identifier, in place. Solid, like the reference view's mark
   and for the same reason - the rest of this line is ordinary code, so the
   mark has to carry itself. It never lands on an added or removed row: the
   viewer marks identifiers only on lines it calls `renamed`. */
.d-hit {
    color: #FFFFFF;
    background: var(--red-ink);
    border-radius: 2px;
    padding: 0 2px;
}

/* A reference view marks where a name is USED, not where a line changed, so
   it takes blue. The row keeps no tint at all: nothing about it is going to
   be written, and a tinted row would read as one that is. */
#diffBody.ref .d-hit {
    /*
     * Inverted, and for a reason the two darker-ink attempts missed: the mark
     * sits INSIDE black source code, so anything lighter than the code
     * recedes no matter which blue it is. Solid fill, light type - now it is
     * the only light thing on the line and reads before any letter in it.
     *
     * The renamed-identifier mark keeps its pale form on purpose: that one
     * sits on a row already tinted red, where a solid fill would be a block
     * on a block.
     */
    background: var(--blue-ink);
    color: #FFFFFF;
    padding: 0 2px;
}
/* Spacing only - marked on the LINE NUMBER, not across the line.
   A full-row wash was tried first and the user rejected it for a good reason:
   after re-alignment these lines come in long runs, and a run of tinted rows
   stops reading as a mark and starts reading as the page background. The
   gutter is a narrow column with an unmarked one right beside it, so a run of
   them still reads as a run of marks.
   Never red either - that belongs to the renames, which must be findable
   among hundreds of re-spaced lines. */
.d-row.spaced .d-n {
    background: #E4E9EE;
    color: var(--text);
    box-shadow: inset 3px 0 0 var(--line-strong);
}
/* A renamed line gets NOTHING in the gutter. Red appears in exactly one place
   in this viewer - the identifier that changed - and three attempts to put it
   somewhere else were all removed on the user's judgement: the cell filled
   red, the 3px bar that had been there from the start, and the line number in
   red type. Every one of them was red competing with the only red that has to
   be found. */
.d-row.cursor { background: #FFF8E1; }

.d-empty { padding: 20px; color: var(--text-muted); font-family: inherit; }

/* ------------------------------------------------------------- SAVE --- */
/* One line: the version you are writing, and the button that writes it. The
   explanation goes underneath, out of the way of both. */
.save-row {
    display: flex; align-items: center; gap: 9px;
    margin-bottom: 6px;
}
.save-row label {
    flex: 0 0 auto;
    font-size: 0.8462rem; font-weight: 600; letter-spacing: .3px;
    text-transform: uppercase;
    color: var(--text-muted);
}
.save-row input {
    flex: 0 1 8rem;
    min-width: 0;
}
.save-row .btn { flex: 0 0 auto; }

/* what the JSON is for, on the same line as the button - a panel this small
   should not need a paragraph */
.save-note {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: 0.8077rem;
    color: var(--text-muted);
}

/* ------------------------------------------------------------- LOG --- */
/* Along the bottom, under the Memory Map column. Toasts fade; this does not. */

.logbar {
    flex: 0 0 auto;
    display: flex; flex-direction: column;
    background: #fff;
    border-top: 1px solid var(--line-strong);
    min-height: 0;
}

/*
 * The log lives INSIDE .col-main, so it is exactly as wide as the table and
 * needs no width arithmetic. The first attempt kept it below the whole
 * workspace and pulled it in with
 *     margin-right: calc(var(--side-w) + 43px)
 * which looked right and was wrong: a full-width row at the bottom still owns
 * the full height, so the check column ended at the top of the log and the
 * bottom-right corner of the window sat empty. Moving one element beat four
 * terms of arithmetic and a clamp for narrow windows.
 *
 * .col-main is a flex column whose table panel takes the slack, so the log
 * keeps its own height and the table gives way to it.
 */
.col-main > .logbar { flex: 0 0 auto; }
.col-main > #logGrip { flex: 0 0 7px; margin: -4px 0; }

.log-head {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: 9px;
    padding: 5px 14px;
    background: var(--nav-bg);
    color: #fff;
    cursor: pointer;
    user-select: none;
}
.log-head b {
    font-size: 0.8077rem; font-weight: 600;
    letter-spacing: .3px; text-transform: uppercase;
}
.log-head .spacer { flex: 1 1 auto; }
.log-count {
    font-size: 0.8077rem;
    color: rgba(255, 255, 255, .65);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.log-unseen {
    flex: 0 0 auto;
    font-size: 0.7692rem; font-weight: 700;
    background: var(--red); color: #fff;
    padding: 0 7px; border-radius: 9px;
    font-variant-numeric: tabular-nums;
}
.log-head .btn {
    flex: 0 0 auto;
    background: rgba(255, 255, 255, .10);
    border-color: rgba(255, 255, 255, .22);
    color: #fff;
}
.log-head .btn:hover { background: rgba(255, 255, 255, .20); }

.log-body {
    flex: 0 0 auto;
    height: var(--log-h, 150px);
    overflow-y: auto;
    padding: 5px 0;
    font-family: Consolas, "Courier New", monospace;
    font-size: 0.8462rem;
    line-height: 1.55;
}

/* A rule across the log whenever the date changes - work runs past midnight
   and 23:58 sitting next to 00:03 says nothing about a day having passed. */
.log-day {
    display: flex; align-items: center; gap: 9px;
    padding: 6px 14px 2px;
    color: var(--text-muted);
    font-size: 0.7692rem; font-weight: 700; letter-spacing: .6px;
}
.log-day::after {
    content: ""; flex: 1 1 auto; height: 1px; background: var(--line);
}

.log-row { display: flex; gap: 10px; padding: 0 14px; }
.log-row:hover { background: var(--canvas); }
.log-t {
    flex: 0 0 auto;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.log-m { flex: 1 1 auto; min-width: 0; color: var(--text-strong); white-space: pre-wrap; }

.log-row.err  .log-m { color: var(--red); font-weight: 700; }
.log-row.warn .log-m { color: #B77A15; }
.log-row.info .log-m { color: var(--text-muted); }

/* collapsed: the bar stays, the entries go */
body.log-collapsed .log-body { display: none; }
body.log-collapsed #logGrip { display: none; }

body.log-resizing { cursor: row-resize; user-select: none; }
body.log-resizing #logGrip { background: var(--accent); }

/* --------------------------------------------------------- TOOLTIPS --- */
/* Native title tooltips appear under the pointer and get covered by it,
   so hints that matter are drawn above the element instead. */

[data-tip] { position: relative; }

[data-tip]::after,
[data-tip]::before {
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease .25s;
    position: absolute;
    z-index: 40;
}

[data-tip]::after {
    content: attr(data-tip);
    left: calc(100% + 9px);
    top: 50%;
    transform: translateY(-50%);
    background: var(--nav-bg);
    color: #fff;
    font-size: 0.7308rem;
    font-weight: 500;
    line-height: 1.35;
    white-space: nowrap;
    padding: 4px 8px;
    border-radius: var(--radius);
    box-shadow: 0 3px 10px rgba(42, 63, 84, .28);
}

[data-tip]::before {           /* the little arrow */
    content: '';
    left: calc(100% + 4px);
    top: 50%;
    transform: translateY(-50%);
    border: 5px solid transparent;
    border-right-color: var(--nav-bg);
}

/* near the right edge of a panel, flip the tooltip to the left instead */
[data-tip].tip-left::after {
    left: auto;
    right: calc(100% + 9px);
}
[data-tip].tip-left::before {
    left: auto;
    right: calc(100% + 4px);
    border-right-color: transparent;
    border-left-color: var(--nav-bg);
}

[data-tip]:hover::after,
[data-tip]:hover::before { opacity: 1; }

/* ------------------------------------------------------ ACCESSIBILITY --- */

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}
.nav-item:focus-visible { outline-offset: -2px; }

@media (prefers-reduced-motion: reduce) {
    * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
    html { scroll-behavior: auto; }
}

/* ============================================================================
 *  New-parameter values dialog  (js/values.js)
 *
 *  A form, not code, so it follows the app's typeface and A-/A+ like every
 *  other panel - the Change viewer is the exception, and only because column
 *  alignment there is the thing being read.
 * ==========================================================================*/
.nv-wrap {
    position: fixed; inset: 0; z-index: 70;
    background: rgba(42, 63, 84, .45);
}
/* Placed by script so it can be dragged and resized - the same idiom as the
   Change viewer, and now the same code (js/dialog.js). */
.nv-dlg {
    position: absolute;
    display: flex; flex-direction: column;
    background: var(--panel);
    border-radius: var(--radius);
    box-shadow: 0 10px 40px rgba(42, 63, 84, .35);
    overflow: hidden;
    min-width: 30rem;
    min-height: 12rem;
}
.nv-dhead {
    flex: 0 0 auto;
    display: flex; align-items: center; gap: 10px;
    padding: 8px 12px;
    background: var(--nav-bg); color: #fff;
    cursor: move;                 /* the bar is the drag handle */
    user-select: none;
    overflow: hidden;             /* nothing may push Cancel off the end */
}
.nv-dhead button { cursor: pointer; }
.nv-dhead b { font-size: 1.0769rem; }
.nv-sub { color: var(--nav-muted); font-size: .8462rem; }
.nv-dhead .spacer { flex: 1 1 auto; }
.nv-dhead .btn {
    background: rgba(255, 255, 255, .12);
    border-color: transparent; color: #fff;
}
.nv-dhead .btn:hover:not(:disabled) { background: rgba(255, 255, 255, .20); }
.nv-dhead .btn-primary { background: var(--accent); }
.nv-dhead .btn-primary:hover:not(:disabled) { background: var(--accent-dark); }

.nv-count { font-size: .8462rem; padding: 2px 8px; border-radius: 10px; }
.nv-count.ok  { background: var(--accent); color: #fff; }
.nv-count.bad { background: var(--amber); color: #4A3A12; }

.nv-body { flex: 1 1 auto; overflow: auto; padding: 12px; background: var(--canvas); }

.nv-card {
    background: var(--panel);
    /* One border weight in this dialog. The 3px coloured stripe down the
       left was the only heavy rule on screen and it drew the eye away from
       the fields, which are what the card is for. */
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 10px 12px; margin-bottom: 12px;
}

.nv-head { display: flex; align-items: center; gap: 8px; }
.nv-head b { color: var(--text-strong); }
.nv-head .spacer { flex: 1 1 auto; }
.nv-badge {
    background: var(--accent); color: #fff;
    font-size: .7692rem; font-weight: 600;
    padding: 1px 6px; border-radius: 2px;
}
.nv-meta { color: var(--text-muted); font-size: .8462rem; }
.nv-state { font-size: .8462rem; }
.nv-state.ok  { color: var(--accent-dark); }
.nv-state.bad { color: #B8860B; }

/* One line, always. It reads left to right as one sentence about where the
   parameter landed, and a wrap in the middle of it turns that into two
   fragments that look like separate facts. Too long for the card scrolls. */
.nv-def {
    font-family: Consolas, "Courier New", monospace;
    font-size: .8462rem; color: var(--text-muted);
    margin: 2px 0 8px;
    white-space: nowrap; overflow-x: auto;
}

.nv-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.nv-line label { font-size: .8462rem; color: var(--text-muted); }

.nv-in {
    font: inherit; font-size: .9231rem;
    padding: 3px 6px;
    border: 1px solid var(--line-strong); border-radius: var(--radius);
    background: #fff; color: var(--text);
    width: 8.5rem;
}
.nv-in:focus { outline: none; border-color: var(--accent); }
/* Not an error - a box nobody has filled in yet. Amber, because red is spent
   on the Change viewer's renamed identifiers and it has to keep meaning that. */
.nv-in.empty { background: var(--amber-soft); border-color: var(--amber); }
.nv-in.lock { background: var(--canvas); color: var(--text-muted); }

.nv-why {
    margin-top: 6px;
    font-size: .8462rem; color: var(--text-muted);
}

.nv-models { margin-top: 10px; border-top: 1px solid var(--line); padding-top: 8px; }
.nv-mhead { display: flex; align-items: center; gap: 8px; font-size: .9231rem; }
.nv-mhead .spacer { flex: 1 1 auto; }
.nv-mhead label { font-size: .8462rem; color: var(--text-muted); }

/* The per-model list is a table: the model list only grows, and a column
   holds the whole name where a grid cell had to cut it short. The header
   stays put while the rows scroll. */
.nv-tbl-wrap {
    margin-top: 8px;
    overflow: auto;
    border: 1px solid var(--line); border-radius: var(--radius);
}
.nv-tbl { width: 100%; border-collapse: separate; border-spacing: 0; }
.nv-tbl thead th {
    position: sticky; top: 0; z-index: 1;
    background: #E3E9EE; color: var(--text-strong);
    text-align: left; font-weight: 600;
    font-size: .8077rem; text-transform: uppercase; letter-spacing: .3px;
    padding: 5px 10px;
    border-bottom: 1px solid var(--line-strong);
}
.nv-tbl thead th:last-child { width: 10rem; }
.nv-tbl tbody td {
    padding: 3px 10px;
    border-bottom: 1px solid var(--line);
    font-size: .9231rem;
}
.nv-tbl tbody tr:last-child td { border-bottom: 0; }
.nv-tbl tbody tr:hover td { background: #F6FBFA; }
.nv-tbl tbody tr.bad td { background: var(--red-soft); }
.nv-tbl tbody td .nv-in { width: 100%; }

/* The unsaved-edits dialog borrows the same shell, narrower - it asks one
   question and the buttons are the whole content. */
/* The unsaved-edits question is not draggable: it is one sentence and three
   buttons, and it keeps its own centring. */
.nv-wrap.fixed { display: flex; align-items: center; justify-content: center; }
.nv-wrap.fixed .nv-dlg {
    position: static; width: min(560px, 92vw); max-height: 90vh;
    min-width: 0; min-height: 0;
}
.nv-acts { display: flex; align-items: center; gap: 8px; }
.nv-acts .spacer { flex: 1 1 auto; }

/* A card that cannot be filled in yet - the reason takes the place of the
   fields, rather than sitting above a form that would accept a wrong answer. */
.nv-stop {
    margin-top: 6px; padding: 8px 10px;
    background: var(--red-soft);
    border: 1px solid #F0C4BF; border-radius: var(--radius);
    font-size: .9231rem;
}
.nv-stop b { color: var(--red); display: block; margin-bottom: 3px; }
.nv-slot { margin-left: 10px; font-family: inherit; }

/* The one question the code cannot answer for itself. */
.nv-ask {
    margin-top: 8px; padding: 8px 10px;
    background: var(--amber-soft);
    border: 1px solid #EBD9A8; border-radius: var(--radius);
}
.nv-ask label { display: flex; align-items: center; gap: 7px; cursor: pointer; }
.nv-ask input { margin: 0; }
.nv-ask .nv-why { margin-top: 4px; }

/* Filled in, but with a number the firmware would refuse. Distinct from
   `.empty` on purpose: one is work not done, the other is work done wrong. */
.nv-in.bad { background: #FDECEA; border-color: var(--red); color: var(--red); }
.nv-chk { display: flex; align-items: center; gap: 6px; cursor: pointer;
          font-size: .8462rem; color: var(--text); }
.nv-chk input { margin: 0; }

/* The cursor lock while a dialog is being dragged or resized. */
body.dlg-resizing, body.diff-resizing { user-select: none; }

/* Save Map, in the toolbar beside Process. The version box that used to sit
   here is gone - the MAP VER. panel already showed the same value. */

/* The last panel still on screen takes the leftover height, so the right
   column has no strip of bare canvas under it. Which one that is changes, so
   the class is put on by script (fillSideColumn). */
.col-side > .panel.fill {
    flex: 1 1 auto; min-height: 0;
    display: flex; flex-direction: column;
}
.col-side > .panel.fill > .panel-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
/* A dragged height becomes a MINIMUM here rather than a fixed size: the
   splitter still sets it, and the panel may grow past it into free space. */
.col-side > .panel.fill .chk-scroll { height: auto; min-height: var(--chk-h, 90px); flex: 1 1 auto; }
.col-side > .panel.fill .snip-scroll { height: auto; min-height: var(--snip-h, 90px); flex: 1 1 auto; }

/* The seam notice is a warning, and it has to read as one from across the
   room. Amber, never red: red in this dialog means a value the firmware would
   refuse, and adding a second meaning to it would weaken the first. */
.nv-ask b {
    display: block;
    color: #7A5B00;
    font-size: 1.0rem;
}
.nv-warn {
    display: inline-block;
    background: var(--amber);
    color: #3D2E00;
    font-weight: 700;
    font-size: .8462rem; letter-spacing: .3px;
    padding: 1px 7px; margin-right: 7px;
    border-radius: 2px;
    vertical-align: 1px;
}
/* The explanation under it was --text-muted on a tinted panel, which is the
   one place that grey does not survive. It carries the numbers the engineer
   needs, so it is body text here. */
.nv-ask .nv-why { color: #5C4708; font-size: .9231rem; }

/* The file name gets the room the folder used to take. It is the one thing on
   this bar that says which file is open, and it was being truncated to
   "SM_004..." while the path beside it repeated what the file list shows. */
.diff-head b { flex: 0 1 auto; max-width: 22rem; }

/* The per-model table folds; its header is the control. It is disabled while
   the table holds something that has to be seen - a fold that can hide a
   problem is worse than the scrolling it saves. */
.nv-fold {
    display: flex; align-items: center; gap: 8px;
    width: 100%; text-align: left;
    font: inherit; font-size: .9231rem; color: var(--text-strong);
    background: none; border: 0; padding: 2px 0;
    cursor: pointer;
}
.nv-fold:disabled { cursor: default; }
.nv-fold:hover:not(:disabled) .nv-meta { color: var(--text); }
.nv-caret { color: var(--text-muted); font-size: .8462rem; width: .8rem; }
.nv-fold:disabled .nv-caret { opacity: .35; }

/* No address for this row - a mark that is not a number, because a blank
   would read as a value and 0 is a real address. */
#page-model-array table.mm td.sticky-1 .ma-no-addr { color: var(--text-muted); }

/* ADDR[DEC] reads left, not right.
   It keeps `col-num` for the tabular figures - the digits still line up in a
   column - but an address is an identifier you scan down, not a quantity you
   compare magnitudes of, so it starts where the eye starts. Scoped to this
   page: the memory map's own numeric columns are still right-aligned. */
#page-model-array table.mm th.sticky-1,
#page-model-array table.mm td.sticky-1 { text-align: left; }
