/* The shared base every app starts from; app-specific looks live in the
   app's own static css, and the tokens in app/theme.css. */

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: var(--font, ui-monospace, "SF Mono", Menlo, monospace);
    font-size: var(--font-size, 14px);
    background: var(--color-bg, #14161a);
    color: var(--color-fg, #d6d8de);
    /* The browser's own widgets — scrollbars, a select's popup, a date
       picker — follow the scheme, not the page colours: without this they
       paint light on the dark palette. Each theme's block in theme.css sets
       it from that theme's bg. */
    color-scheme: var(--color-scheme, dark);
}

.engy-nav {
    display: flex;
    align-items: center;
    gap: var(--space-lg);
    padding: var(--space-md) var(--space-lg);
    background: var(--color-panel, #1c1f26);
    border-bottom: 1px solid var(--color-line, #2a2e37);
}

.engy-brand {
    font-weight: 700;
    color: var(--color-accent, #6fb3ff);
    margin-right: var(--space-lg);
}

.engy-nav a {
    color: var(--color-fg, #d6d8de);
    text-decoration: none;
    padding: var(--space-xs) var(--space-md);
    border-radius: 4px;
}

.engy-nav a.active {
    background: var(--color-accent, #6fb3ff);
    color: var(--color-bg, #14161a);
}

.engy-page {
    padding: var(--space-lg);
}

table.engy-grid {
    border-collapse: collapse;
    width: 100%;
}

.engy-grid th, .engy-grid td {
    border: 1px solid var(--color-line, #2a2e37);
    padding: var(--space-xs) var(--space-md);
    text-align: left;
    font-size: var(--font-size, 14px);
}

.engy-grid th {
    background: var(--color-panel, #1c1f26);
    color: var(--color-accent, #6fb3ff);
    position: sticky;
    top: 0;
}

.engy-grid td[contenteditable="true"]:focus {
    outline: 2px solid var(--color-accent, #6fb3ff);
    background: var(--color-panel, #1c1f26);
}

.engy-errors {
    background: var(--color-error-bg, #3a1d20);
    color: var(--color-error, #ff8f8f);
    border: 1px solid var(--color-error, #ff8f8f);
    border-radius: 4px;
    padding: var(--space-md) var(--space-lg);
    margin: var(--space-lg) 0;
    white-space: pre-wrap;
}

.engy-ok {
    color: var(--color-ok, #7ddf9a);
    margin: var(--space-lg) 0;
}

button {
    background: var(--color-accent, #6fb3ff);
    color: var(--color-bg, #14161a);
    border: none;
    border-radius: 4px;
    padding: var(--space-sm) var(--space-lg);
    font: inherit;
    cursor: pointer;
}

button:disabled {
    opacity: 0.5;
    cursor: default;
}

.engy-auth {
    display: flex;
    justify-content: center;
    padding-top: 12vh;
}

.engy-auth-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-lg);
    width: 320px;
    padding: var(--space-xl);
    background: var(--color-panel, #1c1f26);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 8px;
}

.engy-auth-form h2 {
    margin: 0 0 var(--space-xs);
    color: var(--color-accent, #6fb3ff);
    font-size: var(--text-lg);
}

.engy-auth-form label {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    font-size: var(--text-sm);
    color: var(--color-fg, #d6d8de);
    opacity: 0.85;
}

.engy-auth-form input {
    padding: var(--space-sm) var(--space-md);
    font: inherit;
    color: var(--color-fg, #d6d8de);
    background: var(--color-bg, #14161a);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 4px;
}

.engy-auth-form input:focus {
    outline: none;
    border-color: var(--color-accent, #6fb3ff);
}

.engy-auth-form button {
    padding: var(--space-md);
    font: inherit;
    font-weight: 700;
    color: var(--color-bg, #14161a);
    background: var(--color-accent, #6fb3ff);
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

.engy-auth-form button[disabled] {
    opacity: 0.6;
    cursor: default;
}

.engy-auth-notice {
    margin: 0;
    padding: var(--space-sm) var(--space-md);
    font-size: var(--text-sm);
    color: var(--color-accent, #6fb3ff);
    background: var(--color-bg, #14161a);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 4px;
}

.engy-auth-alt {
    margin: var(--space-xs) 0 0;
    font-size: var(--text-sm);
    text-align: center;
    opacity: 0.85;
}

.engy-auth-alt a {
    color: var(--color-accent, #6fb3ff);
}

.engy-theme {
    padding: var(--space-xs) var(--space-sm);
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-fg, #d6d8de);
    background: var(--color-panel, #1c1f26);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 4px;
    cursor: pointer;
}

.engy-session {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    margin-left: auto;
}

.engy-session-who {
    color: var(--color-fg, #d6d8de);
    font-weight: 700;
}

.engy-session-role {
    color: var(--color-fg, #d6d8de);
    opacity: 0.6;
    font-size: var(--text-sm);
}

/* An account with one preset reads it; one with several picks, so the switch
   is a control rather than a label and carries none of the label's dimming. */
select.engy-session-role {
    opacity: 1;
    padding: var(--space-xs) var(--space-sm);
    font: inherit;
    font-size: var(--text-sm);
    background: var(--color-panel, #1c1f26);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 4px;
    cursor: pointer;
}

.engy-session-out {
    padding: var(--space-xs) var(--space-md);
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-fg, #d6d8de);
    background: transparent;
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 4px;
    cursor: pointer;
}

.engy-session-out:hover {
    border-color: var(--color-accent, #6fb3ff);
    color: var(--color-accent, #6fb3ff);
}

/* ── the app-switcher shell: top bar, launcher, per-app sidebar ── */

.engy-topbar {
    display: flex;
    align-items: center;
    gap: var(--space-lg);
    padding: var(--space-sm) var(--space-lg);
    background: var(--color-panel, #1c1f26);
    border-bottom: 1px solid var(--color-line, #2a2e37);
    /* Pinned: the header stays put while the page scrolls under it. */
    position: sticky;
    top: 0;
    z-index: 30;
}

.engy-logo {
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
    font-weight: 700;
    color: var(--color-accent, #6fb3ff);
    text-decoration: none;
    font-size: var(--text-md);
}

/* The app icon as the mark beside the name: the same file the home screen
   and the tab show, so the three cannot drift. */
.engy-logo-mark {
    height: 1.4rem;
    width: 1.4rem;
    border-radius: 4px;
}

.engy-app-name {
    font-weight: 400;
    color: var(--color-fg, #d6d8de);
    font-size: var(--text-md);
    padding-left: var(--space-lg);
    border-left: 1px solid var(--color-line, #2a2e37);
}

.engy-topbar-spacer {
    flex: 1;
}

.engy-search {
    position: relative;
    margin-left: 1rem;
}

.engy-search-input {
    width: 32rem;
    font: inherit;
    font-size: 0.85em;
    color: var(--color-fg, #d6d8de);
    background: var(--color-bg, #14161a);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 4px;
    padding: 0.3rem 0.6rem;
}

.engy-search-input:focus {
    outline: none;
    border-color: var(--color-accent, #6fb3ff);
}

.engy-search-dropdown {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    width: 100%;
    max-height: 60vh;
    overflow-y: auto;
    background: var(--color-panel, #1c1f26);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 6px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
    z-index: 40;
}

.engy-search-group {
    padding: 0.3rem 0.7rem;
    font-size: 0.75em;
    color: var(--color-fg, #d6d8de);
    opacity: 0.6;
}

/* The row wraps: a key with no break opportunity in it (a user's id is 64 hex
   characters) keeps its line and sends the label to the next one, rather than
   widening the dropdown into a horizontal scroll. */
.engy-search-hit {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.6rem;
    width: 100%;
    padding: 0.35rem 0.7rem;
    font: inherit;
    font-size: 0.85em;
    color: var(--color-fg, #d6d8de);
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
}

.engy-search-hit.active, .engy-search-hit:hover {
    background: var(--color-accent, #6fb3ff);
    color: var(--color-bg, #14161a);
}

.engy-search-hit-key {
    font-weight: 700;
    flex-shrink: 0;
    overflow-wrap: anywhere;
}

.engy-search-hit-label {
    opacity: 0.7;
    word-break: break-word;
}

.engy-search-error {
    padding: var(--space-sm) var(--space-md);
    font-size: var(--text-sm);
    color: var(--color-error, #ff8f8f);
}

/* An app icon rendered from its SVG: the file is a mask so the glyph takes the
   surrounding text color and themes with everything else. --engy-icon carries
   the per-app url(...); sized to 1em so the sidebar and the home tile scale it
   by their own font-size. */
.engy-icon-mask {
    display: inline-block;
    width: 1em;
    height: 1em;
    background-color: currentColor;
    -webkit-mask: var(--engy-icon) center / contain no-repeat;
            mask: var(--engy-icon) center / contain no-repeat;
}

.engy-body {
    display: flex;
    align-items: stretch;
    min-height: calc(100vh - 3rem);
}

.engy-sidebar {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    width: 11rem;
    flex-shrink: 0;
    padding: var(--space-lg) var(--space-md);
    background: var(--color-panel, #1c1f26);
    border-right: 1px solid var(--color-line, #2a2e37);
    /* Pinned beside the content: sticks below the header (align-self keeps the
       flex row from stretching it, so sticky has room to work) and scrolls
       within itself when the nav outgrows the viewport. */
    position: sticky;
    top: 3rem;
    align-self: flex-start;
    height: calc(100vh - 3rem);
    overflow-y: auto;
}

/* Two lines: the icon down the left of both, the title on the first and the
   description on the second. minmax(0, 1fr), not a bare 1fr, because the text
   column's content is a nowrap line — its min-content would otherwise push the
   track past the sidebar's fixed width instead of being clipped. */
.engy-sidebar-shortcut {
    display: grid;
    grid-template-columns: min-content minmax(0, 1fr);
    grid-template-areas:
        "icon title"
        "icon desc";
    gap: 0 var(--space-sm);
    align-items: center;
    padding: var(--space-md);
    border-radius: 6px;
    text-decoration: none;
    color: var(--color-fg, #d6d8de);
    text-align: left;
}

/* More is the one tile that is a button, and the shared button rule paints an
   accent pill — which beside the shortcuts it must not be. It sits at the foot
   of the nav whatever the nav holds. */
.engy-sidebar-more {
    margin-top: auto;
    background: none;
}

.engy-sidebar-shortcut:hover {
    background: var(--color-bg, #14161a);
}

.engy-sidebar-shortcut.active {
    background: var(--color-accent, #6fb3ff);
    color: var(--color-bg, #14161a);
}

.engy-sidebar-icon {
    grid-area: icon;
    font-size: var(--text-xl);
    line-height: 1;
}

.engy-sidebar-label {
    grid-area: title;
    font-size: var(--text-sm);
}

/* The tile is two lines by design, so a description longer than the sidebar is
   clipped rather than wrapped — the tile's title attribute carries the whole
   of it, and the #/ home shows every one in full. */
.engy-sidebar-desc {
    grid-area: desc;
    font-size: var(--text-xs);
    opacity: 0.6;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.engy-body .engy-page {
    flex: 1;
    min-width: 0;
}

/* the launcher home (#/) */

.engy-home-wrap {
    padding: var(--space-xl);
}

.engy-home {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
    gap: var(--space-lg);
    max-width: 48rem;
}

.engy-home-tile {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
    padding: var(--space-xl);
    text-decoration: none;
    color: var(--color-fg, #d6d8de);
    background: var(--color-panel, #1c1f26);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 8px;
}

.engy-home-tile:hover {
    border-color: var(--color-accent, #6fb3ff);
}

.engy-home-icon {
    font-size: var(--text-xl);
    line-height: 1;
}

.engy-home-name {
    font-weight: 700;
    color: var(--color-accent, #6fb3ff);
}

.engy-home-desc {
    font-size: var(--text-sm);
    opacity: 0.75;
}

.engy-empty {
    padding: var(--space-xl);
    opacity: 0.7;
}

/* chrome pages (login/register/verify) render full-screen, no shell */
.engy-chrome {
    min-height: 100vh;
}

.engy-session-in {
    padding: var(--space-xs) var(--space-md);
    font-size: var(--text-sm);
    color: var(--color-bg, #14161a);
    background: var(--color-accent, #6fb3ff);
    border-radius: 4px;
    text-decoration: none;
    font-weight: 700;
}

/* dialogs — the layer lets pointer events through, so a non-modal dialog
   leaves the page usable behind it; only the frames and backdrops catch them */
#engy-dialogs {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 100;
}

.engy-dialog-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    pointer-events: auto;
}

/* The frame is a column no taller than the viewport: header and footer keep
   their height, the body alone scrolls — both ways, so a wide table scrolls
   inside the dialog and never moves the page. */
.engy-dialog {
    position: fixed;
    display: flex;
    flex-direction: column;
    min-width: 16rem;
    min-height: 6rem;
    max-width: 95vw;
    max-height: 90vh;
    background: var(--color-panel, #1c1f26);
    color: var(--color-fg, #d6d8de);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 6px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
    overflow: hidden;
    pointer-events: auto;
    outline: none;
}

.engy-dialog-resizable {
    resize: both;
}

/* Until the first drag: centred by transform, stepped per stacked dialog. */
.engy-dialog-centred {
    left: 50%;
    top: 50%;
    transform: translate(calc(-50% + var(--engy-dialog-step, 0px)), calc(-50% + var(--engy-dialog-step, 0px)));
}

.engy-dialog-head {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-shrink: 0;
    padding: var(--space-sm) var(--space-lg);
    border-bottom: 1px solid var(--color-line, #2a2e37);
    user-select: none;
}

.engy-dialog-movable {
    cursor: move;
}

.engy-dialog-icon {
    display: inline-block;
    width: 1.1em;
    height: 1.1em;
    line-height: 1;
    background-color: var(--color-accent, #6fb3ff);
}

.engy-dialog-title {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 700;
    color: var(--color-accent, #6fb3ff);
}

.engy-dialog-head-controls {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    flex-shrink: 0;
}

.engy-dialog-x, .engy-dialog-collapse {
    background: none;
    color: var(--color-fg, #d6d8de);
    padding: 0 var(--space-sm);
    font-size: var(--text-lg);
    line-height: 1;
    opacity: 0.6;
}

.engy-dialog-x:hover, .engy-dialog-collapse:hover {
    opacity: 1;
}

.engy-dialog-body {
    flex: 1;
    min-height: 0;
    overflow: auto;
    padding: var(--space-lg);
}

.engy-dialog-errors {
    flex-shrink: 0;
    margin: 0 var(--space-lg) var(--space-md);
}

.engy-dialog-foot {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-md);
    flex-shrink: 0;
    padding: var(--space-md) var(--space-lg);
    border-top: 1px solid var(--color-line, #2a2e37);
}

/* The dynamic and row halves are each their own flex run, so the buttons
   within one keep the same gap as the two halves keep between them. */
.engy-dialog-dyn-buttons,
.engy-dialog-row-buttons {
    display: flex;
    gap: var(--space-md);
}

.engy-dialog-foot button:not(.engy-dialog-default):not(.engy-dialog-move):not(.engy-dialog-danger) {
    background: none;
    color: var(--color-fg, #d6d8de);
    border: 1px solid var(--color-line, #2a2e37);
}

.engy-dialog-foot button.engy-dialog-danger {
    background: var(--color-error, #e5484d);
    color: var(--color-bg, #14161a);
}

body.engy-modal-open {
    overflow: hidden;
}

/* An inline objref, [[type: key]], rendered as a link: a comment, a log
   line, a task's changelog or an agent card's message. */
.engy-objref {
    display: inline;
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--color-accent, #6fb3ff);
    text-decoration: underline dotted;
    cursor: pointer;
}

.engy-objref:hover {
    text-decoration-style: solid;
}

/* A path that did not resolve: the start key behind a red #, the reason in
   the title. */
.engy-objref-broken {
    color: var(--color-error, #ff8f8f);
    font-weight: 700;
}

/* The users table: chips wrap inside their cell, and the control that hands
   one out sits under them rather than in a column of its own. A permission a
   preset carries is quieter than one granted by hand, because only the second
   can be taken back. */
.engy-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
    margin-bottom: var(--space-xs);
}

.engy-chip-quiet {
    opacity: 0.55;
}

.engy-users-add {
    display: flex;
    gap: var(--space-xs);
    align-items: center;
}

.engy-users-add select {
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-fg, #d6d8de);
    background: var(--color-panel, #1c1f26);
    border: 1px solid var(--color-line, #2a2e37);
    border-radius: 4px;
    padding: 1px var(--space-xs);
}

.engy-users-note {
    font-size: var(--text-sm);
    margin-left: var(--space-xs);
}
