/* ---------------------------------------------------------------- reset -- */

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

/*
 * One column definition shared by the status bar and the page body, so the two
 * always line up. Both are border-box, so --column includes the gutters.
 */
:root {
    --column: 1500px;
    --gutter: 22px;
}

html { min-height: 100%; }

body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    margin: 0;
    background: var(--bg);
    color: var(--fg);
    font: 14px/1.5 "Cascadia Mono", "Consolas", "DejaVu Sans Mono", ui-monospace, monospace;
    letter-spacing: 0.02em;
    text-shadow: var(--glow);
    -webkit-font-smoothing: antialiased;
}

::selection { background: var(--select); }

button, input, textarea {
    font: inherit;
    letter-spacing: inherit;
    color: inherit;
    background: none;
    border: none;
}

:focus-visible { outline: 1px solid var(--accent2); outline-offset: 2px; }

/* --------------------------------------------------------- CRT overlay --- */

.crt {
    position: fixed;
    inset: 0;
    z-index: 9;
    pointer-events: none;
    /* Lengths in device pixels, set by crt.js, so zoom does not turn the
       period into a fractional number of pixels and start a moire. The
       fallbacks are the plain CSS-pixel values, which is what shows for the
       frame before the script runs and if it never does. */
    background: repeating-linear-gradient(
        to bottom,
        rgba(255, 255, 255, var(--scan)) 0 var(--scan-line, 1px),
        transparent var(--scan-line, 1px) var(--scan-period, 3px)
    );
}

.crt::after {
    /* Gentle vignette, so the corners fall off like a real tube. */
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(ellipse at center, transparent 55%, rgba(0, 0, 0, 0.45) 100%);
}

/* ------------------------------------------------------ boot animation --- */

.boot {
    position: fixed;
    inset: 0;
    z-index: 50;
    background: var(--bg);
    padding: 6vh 6vw;
    overflow: hidden;
    transition: opacity 260ms ease;
}

.boot.done { opacity: 0; pointer-events: none; }
.boot.hidden, html.no-boot .boot { display: none; }

.boot-out { margin: 0; font: inherit; color: var(--fg); white-space: pre-wrap; }
.boot-out::after {
    content: "\2588";
    animation: blink 1s steps(1) infinite;
}

@keyframes blink { 50% { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
    .boot { display: none; }
    .boot-out::after { animation: none; }
}

/* ------------------------------------------------------------ statusbar -- */

/* Full-bleed rule and background... */
.statusbar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--bg);
    border-bottom: 1px solid var(--rule);
    font-size: 12px;
}

/* ...but the readouts sit in the same column as the page, so on a wide monitor
   they stay next to the content instead of hugging the screen edges. */
.statusbar-inner {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    max-width: var(--column);
    margin: 0 auto;
    padding: 6px var(--gutter);
}

.sb-group { display: flex; align-items: center; gap: 10px; }
.sb-brand { color: var(--accent); letter-spacing: 0.22em; text-transform: uppercase; }
.sb-sep, .sb-dim { color: var(--dim); }
.sb-clock { font-variant-numeric: tabular-nums; }

.sb-btn {
    cursor: pointer;
    padding: 2px 7px;
    border: 1px solid var(--rule);
    color: var(--dim);
    text-transform: lowercase;
}

.sb-btn span { color: var(--fg); margin-left: 4px; }
.sb-btn:hover { border-color: var(--accent2); color: var(--fg); }

.sb-btn-edit { text-transform: uppercase; letter-spacing: 0.18em; }
.sb-btn-edit[aria-pressed="true"] {
    background: var(--accent2);
    border-color: var(--accent2);
    color: var(--bg);
    text-shadow: none;
}

.sb-save { color: var(--dim); }
.sb-save[data-state="saving"] { color: var(--accent2); }
.sb-save[data-state="saved"]  { color: var(--fg); }
.sb-save[data-state="error"]  { color: var(--danger); }

/* ----------------------------------------------------------- page frame -- */

/*
 * `margin: auto` on a flex item soaks up the free space on all four sides, so
 * the page sits centred when it fits and simply scrolls when it doesn't.
 * `justify-content: center` would centre too, but makes the overflowing top
 * unreachable; auto margins collapse to zero instead.
 */
.wrap {
    width: 100%;
    max-width: var(--column);
    margin: auto;
    padding: 28px var(--gutter);
}

/* --------------------------------------------------------------- search -- */

.search {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    margin-bottom: 30px;
    border: 1px solid var(--rule);
    background: var(--panel);
}

.search:focus-within { border-color: var(--accent2); box-shadow: 0 0 12px -4px var(--accent2); }

.search-caret { color: var(--accent); font-size: 18px; line-height: 1; }

.search-input {
    flex: 1;
    min-width: 0;
    font-size: 17px;
    padding: 2px 0;
}

.search-input:focus { outline: none; }

.search-hint {
    flex: none;
    color: var(--dim);
    font-size: 12px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    white-space: nowrap;
}

.search-hint.is-engine { color: var(--accent2); }
.search-hint.is-url    { color: var(--accent); }

/* ----------------------------------------------------------- link grid --- */

.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 30px 20px;
    align-items: start;
}

.panel {
    position: relative;
    padding: 16px 14px 14px;
    border: 1px solid var(--rule);
    background: var(--panel);
}

/*
 * Headings are labels, not content: nothing here is worth selecting, and a
 * caret over them suggests otherwise. Dragging a section by its title in
 * edit mode also used to smear a text selection across the page, since the
 * grab handle lives inside the chip.
 */
.panel-title,
.group-title,
.engines-title {
    cursor: default;
    user-select: none;
    -webkit-user-select: none;      /* Safari still wants the prefix */
}

.panel-title {
    position: absolute;
    top: -0.72em;
    left: 10px;
    margin: 0;
    padding: 0 7px;
    background: var(--bg);
    color: var(--accent);
    font-size: 12px;
    font-weight: normal;
    letter-spacing: 0.24em;
    text-transform: uppercase;
}

.items { display: flex; flex-direction: column; gap: 2px; }

.link {
    display: block;
    padding: 2px 4px;
    color: var(--fg);
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.link::before { content: "> "; color: var(--dim); }
.link:hover, .link:focus-visible {
    color: var(--accent);
    background: var(--select);
    text-shadow: var(--glow);
}
.link:hover::before { color: var(--accent); }

/*
 * The branch is drawn, not typed. A "\251C\2500" glyph never lines up with a
 * CSS border-left: the stem sits mid-cell (~4px in) while the border sits at
 * the box edge, and the title's letter-spacing pushes the arm off the stem as
 * well. Rule and arm are both borders here, anchored to the same box, so they
 * meet exactly whatever the font does.
 */
.group {
    position: relative;
    margin: 10px 0 4px;
    padding-left: 11px;
    border-left: 1px solid var(--rule);
}

.group-title {
    margin: 0 0 2px;
    color: var(--accent2);
    font-size: 12px;
    font-weight: normal;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    opacity: 0.9;
}

/* The horizontal arm, centred on the title's 18px line box. */
.group-title::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.75em;
    width: 7px;
    height: 1px;
    background: var(--rule);
}

.group-links {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-left: 6px;
}

.empty-hint { color: var(--dim); font-size: 12px; padding: 2px 4px; }

/* --------------------------------------------------------------- notepad -- */

.notepad { margin-top: 40px; }

.np-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 6px;
}

.np-toggle {
    cursor: pointer;
    padding: 0;
    color: var(--accent);
    font-size: 12px;
    letter-spacing: 0.24em;
}

.np-chevron { display: inline-block; transition: transform 140ms ease; }
html.np-closed .np-chevron { transform: rotate(-90deg); }
html.np-closed .np-text { display: none; }

/* Transient messages fade out; FADE_MS in notepad.js must match this duration. */
.np-status {
    color: var(--dim);
    font-size: 12px;
    opacity: 1;
    transition: opacity 450ms ease;
}

.np-status.fading { opacity: 0; }

.np-status[data-state="error"]  { color: var(--danger); }
.np-status[data-state="remote"] { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
    .np-status { transition: none; }
}

.np-text {
    display: block;
    width: 100%;
    min-height: 220px;
    resize: none;
    padding: 12px 14px;
    background: var(--panel);
    border: 1px solid var(--rule);
    color: var(--fg);
    line-height: 1.6;
    tab-size: 4;
}

.np-text:focus { outline: none; border-color: var(--accent2); box-shadow: 0 0 12px -4px var(--accent2); }

/* ----------------------------------------------------------------- toast -- */

.toast {
    position: fixed;
    left: 50%;
    bottom: 22px;
    z-index: 60;
    transform: translateX(-50%);
    max-width: min(680px, 90vw);
    padding: 8px 16px;
    background: var(--panel);
    border: 1px solid var(--danger);
    color: var(--danger);
    font-size: 13px;
}

.toast[data-kind="info"] { border-color: var(--accent2); color: var(--accent2); }

/* =========================================================== EDIT MODE === */

.edit-only { display: none; }
body.editing .edit-only { display: block; }

/*
 * Edit mode is a settings screen, not the page with knobs bolted on.
 *
 * The search bar, the notepad and the scene simulator are all things you
 * use rather than configure, and with settings panels stacked above them
 * the settings themselves ended up scrolled off. Hiding them keeps what you
 * came here to change on one screen.
 *
 * The link sections stay, because editing them is the point, and the
 * weather card stays with them -- it is a grid item, and pulling it out
 * while the cards around it remain would reflow the whole grid the moment
 * you started editing.
 *
 * Nothing is destroyed by this: the notepad textarea keeps its value and
 * its autosave timer while hidden, so leaving edit mode brings back exactly
 * what was there.
 */
body.editing .search,
body.editing .notepad,
body.editing .devbar { display: none; }
/*
 * + SECTION takes the first free cell after the cards.
 *
 * order is the whole trick: the button has to come first in the DOM --
 * renderGrid() inserts sections before the weather card and would otherwise
 * need to know about the button as well -- but it should be the last thing
 * placed, so auto-placement drops it into whatever slot the last row has
 * left over, or opens a new row when that row is full. No dedicated track,
 * so the cards keep their normal width in edit mode, and nothing has to be
 * conditional on whether any sections exist.
 *
 * justify-self keeps the button its own width rather than stretching it
 * across the cell, and align-self tops it out level with the cards beside
 * it. Outside edit mode it is display:none, so it is not a grid item at all
 * and the layout is untouched.
 */
.grid-actions {
    order: 1;
    align-self: start;
    justify-self: start;
}

/* ----------------------------------------------------------- account -- */

/*
 * The grid carries no bottom margin -- the air under it has always come
 * from .notepad's margin-top, and the notepad is precisely what edit mode
 * hides. ACCOUNT took over that slot and inherited none of the spacing, so
 * it butted straight against the last row of cards. Same 40px, so the
 * rhythm below the grid is the same whichever of the two is showing.
 */
#account-panel { margin-top: 40px; }

.acct-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
}

.acct-form label {
    color: var(--dim);
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

/*
 * Both fields and the button on one row.
 *
 * A specificity fix, not a width one. The width here has always said
 * 150px, but .edit-field sets width:100% and is declared further down
 * this file; at equal specificity the later rule won, so every field
 * stretched to the full row and the form ran to three lines -- with
 * nothing to suggest the declaration above it had been discarded. The
 * descendant selector settles it.
 *
 * Below about 700px the button wraps to a second line, then the confirm
 * field follows it. Flex wraps rather than shrinks here, which is the
 * better failure: two readable fields over two lines beats four squeezed
 * boxes on one.
 */
.acct-form .acct-field { width: 150px; }
.acct-status { color: var(--dim); font-size: 11px; }
.acct-status[data-kind="ok"]  { color: var(--accent2); }
.acct-status[data-kind="bad"] { color: var(--danger); }

.acct-sub {
    margin: 18px 0 4px;
    color: var(--accent2);
    font-size: 12px;
    font-weight: normal;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    cursor: default;
    user-select: none;
}

/* The one moment the raw token exists, so it gets room to be copied. */
.inv-fresh { margin-top: 10px; }
.inv-fresh label {
    display: block;
    margin-bottom: 4px;
    color: var(--danger);
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}
.inv-copy { display: flex; gap: 8px; }
.inv-copy .edit-field { flex: 1 1 auto; }

.inv-list { display: flex; flex-direction: column; gap: 2px; margin-top: 12px; }
.inv-list:empty { margin-top: 0; }

.inv-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 0;
    border-top: 1px solid var(--rule);
    font-size: 11.5px;
    color: var(--dim);
}
.inv-row .inv-state { min-width: 62px; letter-spacing: 0.1em; text-transform: uppercase; }
.inv-row[data-state="open"]    .inv-state { color: var(--accent2); }
.inv-row[data-state="used"]    .inv-state { color: var(--dim); }
.inv-row[data-state="expired"] .inv-state { color: var(--danger); }
.inv-row .inv-when { margin-left: auto; }

/* Armed state for a status-bar button awaiting its second click. */
.sb-btn.confirm { color: var(--danger); border-color: var(--danger); }

body.editing .panel { border-style: dashed; }
body.editing .link { pointer-events: none; }

.row-tools { display: none; }
body.editing .row-tools { display: inline-flex; gap: 4px; margin-left: 6px; }

.icon-btn {
    cursor: pointer;
    padding: 0 5px;
    border: 1px solid var(--rule);
    color: var(--dim);
    font-size: 11px;
    line-height: 1.5;
}

.icon-btn:hover { color: var(--fg); border-color: var(--accent2); }
.icon-btn.danger:hover { color: var(--danger); border-color: var(--danger); }
.icon-btn.confirm { color: var(--danger); border-color: var(--danger); }

.ghost-btn {
    cursor: pointer;
    padding: 3px 10px;
    border: 1px dashed var(--rule);
    color: var(--dim);
    font-size: 12px;
    letter-spacing: 0.14em;
}

.ghost-btn:hover { color: var(--accent2); border-color: var(--accent2); }

.edit-field {
    width: 100%;
    padding: 2px 5px;
    background: var(--bg);
    border: 1px solid var(--rule);
    color: var(--fg);
}

.edit-field:focus { outline: none; border-color: var(--accent2); }
.edit-field.invalid { border-color: var(--danger); color: var(--danger); }

.edit-row {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 3px 0;
}

.edit-row .edit-label { flex: 0 0 38%; }
.edit-row .edit-url   { flex: 1 1 auto; min-width: 0; color: var(--dim); }

.panel-actions { display: flex; gap: 6px; margin-top: 10px; }

.drag-handle {
    cursor: grab;
    flex: none;
    padding: 0 3px;
    color: var(--dim);
    user-select: none;
}

.drag-handle:active { cursor: grabbing; }

.dragging { opacity: 0.35; }

/* Insertion caret drawn on the element the pointer is nearest. */
.drop-before { box-shadow: 0 -2px 0 0 var(--accent2); }
.drop-after  { box-shadow: 0  2px 0 0 var(--accent2); }
.drop-into   { outline: 1px dashed var(--accent2); outline-offset: 2px; }

body.editing .group-links,
body.editing .items { min-height: 14px; }

/* ------------------------------------------------------- engines panel --- */

.engines {
    position: relative;
    margin-bottom: 30px;
    padding: 18px 14px 14px;
    border: 1px dashed var(--rule);
    background: var(--panel);
}

.engines-title {
    position: absolute;
    top: -0.72em;
    left: 10px;
    margin: 0;
    padding: 0 7px;
    background: var(--bg);
    color: var(--accent);
    font-size: 12px;
    font-weight: normal;
    letter-spacing: 0.24em;
}

.engines-help { margin: 0 0 12px; color: var(--dim); font-size: 12px; }
.engines-help code { color: var(--accent2); }

.engines-head,
.engine-row {
    display: grid;
    grid-template-columns: 34px 62px minmax(80px, 1fr) minmax(130px, 1.5fr) minmax(170px, 2.4fr) 48px;
    gap: 8px;
    align-items: center;
}

.engines-head {
    margin-bottom: 4px;
    color: var(--dim);
    font-size: 11px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

.engine-row { padding: 3px 0; }

/*
 * The delete button matches the fields beside it.
 *
 * By height rather than by number: the row is a grid, its track is sized
 * by the tallest item -- the inputs -- so stretching the button into that
 * track makes it exactly their height and keeps it there if the field
 * padding or the body font ever changes. A hard height would have to be
 * re-derived every time either moved.
 *
 * Stretching means the glyph is no longer centred by line-height, so the
 * button centres its own content instead. It matters most in the armed
 * state, where the label becomes 'sure?'.
 */
.engine-row .icon-btn {
    align-self: stretch;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}
/*
 * Radios and checkboxes, drawn from scratch.
 *
 * `accent-color` only tints the *checked* state -- an unchecked native control
 * keeps the operating system's grey ring, which is the one thing on the page
 * that ignores the palette entirely. `appearance: none` hands the whole
 * control over, empty state included.
 */
.engine-row input[type="radio"],
.wx-settings input[type="radio"],
.wx-settings input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    flex: none;
    width: 12px;
    height: 12px;
    margin: 0;
    border: 1px solid var(--rule);
    background: var(--bg);
    cursor: pointer;
}

.engine-row input[type="radio"],
.wx-settings input[type="radio"] { border-radius: 50%; }

.engine-row input[type="radio"]:hover,
.wx-settings input[type="radio"]:hover,
.wx-settings input[type="checkbox"]:hover { border-color: var(--accent2); }

.engine-row input[type="radio"]:checked,
.wx-settings input[type="radio"]:checked {
    border-color: var(--accent2);
    background: radial-gradient(circle, var(--accent2) 0 45%, var(--bg) 47%);
    box-shadow: 0 0 6px -1px var(--accent2);
}

/* A filled square with a gap reads as "ticked" without needing a glyph. */
.wx-settings input[type="checkbox"]:checked {
    border-color: var(--accent2);
    background: var(--accent2);
    box-shadow: inset 0 0 0 2px var(--bg), 0 0 6px -1px var(--accent2);
}

#add-engine { margin-top: 10px; }

@media (max-width: 640px) {
    .engines-head { display: none; }
    /* The url and the delete button each wrap onto their own line here. Without
       an explicit placement the button lands in the 34px radio gutter, which is
       narrower than "sure?" and clips it. */
    .engine-row { grid-template-columns: 34px 70px 1fr; }
    .engine-row .engine-url,
    .engine-row .engine-home { grid-column: 2 / -1; }
    .engine-row .icon-btn { grid-column: 2 / -1; justify-self: start; }
    .search-hint { display: none; }
}

/* ==================================================== weather card ======= */

/*
 * Negative line numbers resolve against the column count auto-fill produced,
 * so this pins the card to the last cell of the first row.
 */
.wx-card {
    grid-column: -2 / -1;
    grid-row: 1;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/*
 * One or two columns is too narrow to reserve a whole cell for weather -- it
 * would push every link below the fold. Let it flow to the end instead.
 */
@media (max-width: 900px) {
    .wx-card {
        grid-column: auto;
        grid-row: auto;
    }
}

.wx-place {
    color: var(--dim);
    font-size: 11px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.wx-note { margin: 4px 0; color: var(--dim); font-size: 12px; }

.wx-now { display: flex; align-items: center; gap: 12px; }

.wx-art {
    flex: none;
    margin: 0;
    color: var(--accent);
    font: inherit;
    font-size: 11px;
    line-height: 1.15;
    white-space: pre;
}

.wx-read { min-width: 0; }

.wx-temp { display: flex; align-items: baseline; gap: 2px; }
.wx-degrees { font-size: 30px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.wx-unit { color: var(--dim); font-size: 13px; }

/* Wraps rather than truncates: the exact condition is the point, and
   "Heavy snow showers" or "Thunderstorm, hail" would otherwise be clipped. */
.wx-cond {
    color: var(--accent2);
    font-size: 12px;
    line-height: 1.3;
}

.wx-hilo { font-size: 12px; font-variant-numeric: tabular-nums; }
.wx-dim { color: var(--dim); }

.wx-stats {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 1px 10px;
    margin: 0;
    font-size: 11px;
}

.wx-stats dt { color: var(--dim); text-transform: uppercase; letter-spacing: 0.1em; }
.wx-stats dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; }

.wx-days {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 6px;
    padding-top: 9px;
    border-top: 1px solid var(--rule);
}

.wx-day { text-align: center; }

.wx-day-name {
    color: var(--accent2);
    font-size: 10px;
    letter-spacing: 0.12em;
}

.wx-day-art {
    margin: 2px 0;
    color: var(--accent);
    font: inherit;
    font-size: 14px;
    line-height: 1.15;
    white-space: pre;
}

/*
 * The drawn icons, for vaporwave only.
 *
 * Both sets are in the card and the theme decides which is painted, so a
 * theme switch is instant and weather.js never has to know a theme exists.
 * The sizes match what the <pre> they replace occupied -- 14x5 characters
 * at 11px, and 8x3 at 14px -- so the card does not resize with the theme.
 *
 * One drop-shadow on the element is the whole neon treatment: an SVG filter
 * per icon would be eleven filter regions to rasterise for the same look.
 */
.wx-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.wx-svg, .wx-day-svg { display: none; }

[data-theme="vaporwave"] .wx-art,
[data-theme="vaporwave"] .wx-day-art { display: none; }

[data-theme="vaporwave"] .wx-svg {
    display: block;
    flex: none;
    width: 92px;
    height: 69px;
    filter: drop-shadow(0 0 5px rgba(255, 113, 206, 0.45));
}

[data-theme="vaporwave"] .wx-day-svg {
    display: block;
    width: 46px;
    height: 35px;
    margin: 2px auto;
    filter: drop-shadow(0 0 4px rgba(255, 113, 206, 0.4));
}

.wx-day-range { font-size: 11px; font-variant-numeric: tabular-nums; }

.wx-foot {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    color: var(--dim);
    font-size: 10px;
    letter-spacing: 0.08em;
}

.wx-stale { color: var(--accent); }

/* --------------------------------------------------- weather settings --- */

.wx-settings {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 22px;
    margin-bottom: 10px;
    font-size: 12px;
}

.wx-toggle, .wx-units label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.wx-units { display: inline-flex; gap: 14px; }
.wx-settings input { accent-color: var(--accent2); cursor: pointer; }

.wx-search { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.wx-search .edit-field { flex: 0 1 240px; }
.wx-current { color: var(--dim); font-size: 11px; }

.wx-results { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.wx-results:empty { margin-top: 0; }

.wx-hit {
    cursor: pointer;
    padding: 3px 9px;
    border: 1px solid var(--rule);
    color: var(--fg);
    font-size: 12px;
}

.wx-hit:hover { border-color: var(--accent2); color: var(--accent2); }

/* The section grab handle sits in the title chip, so the chip becomes a row. */
body.editing .panel-title {
    display: flex;
    align-items: center;
    gap: 4px;
}

body.editing .panel-title .drag-handle { letter-spacing: 0; }

/* ============================================ DEV: scene simulator ======
 * Delete this block along with the #dev-scene section in index.php, the
 * devscene.js import in app.js, and api/scene.php to revert the live
 * backdrop. Nothing else references these rules.
 */
.devbar {
    margin-top: 18px;
    border: 1px dashed var(--rule);
    padding: 6px 12px 8px;
    background: var(--panel);
}

.dev-head { display: flex; align-items: baseline; gap: 12px; }
.dev-chevron { display: inline-block; transition: transform 140ms ease; }
.devbar:not(.open) .dev-chevron { transform: rotate(-90deg); }

.dev-state { color: var(--dim); font-size: 11px; letter-spacing: 0.12em; }
.dev-state.is-sim { color: var(--accent2); }

.dev-body { padding-top: 8px; }

.dev-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
    margin-bottom: 6px;
    font-size: 11px;
    color: var(--dim);
}

.dev-row label { text-transform: uppercase; letter-spacing: 0.12em; }
.dev-row .edit-field { width: auto; }
.dev-num { width: 92px; }
.dev-speed { width: 92px; }
/* Wide enough for the longest WMO label without truncating it. */
.dev-code  { width: 190px; }
.dev-date  { width: 140px; }

.dev-clock {
    min-width: 46px;
    color: var(--fg);
    font-variant-numeric: tabular-nums;
}

#dev-time { flex: 1 1 220px; min-width: 160px; accent-color: var(--accent2); }
