/* ──────────────────────────────────────────────────────────────────────────
   Components — card, table, chip, button, field, alert, help.
   ────────────────────────────────────────────────────────────────────────── */

/* ── Card ────────────────────────────────────────────────────────────────── */

.card {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* No shadow anywhere. A 1px line and a change of surface is enough to say
     "this is a separate thing", and it stays true in a dark theme, where a
     shadow says nothing at all. */
}

.card__head {
  min-height: 56px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--border);
}
.card__title { display: flex; align-items: center; gap: var(--s-2);
               font-size: 14px; font-weight: 600; }
.card__title .icon { font-size: 20px; color: var(--text-secondary); }
.card__meta  { font-size: 12px; font-weight: 400; color: var(--text-muted); }

.card__body { padding: var(--s-4); }
.card__foot { padding: var(--s-4); border-top: 1px solid var(--border); }

/* ── Table ───────────────────────────────────────────────────────────────── */

/* The scroller lives on the wrapper, so the card keeps its corners and the
   header row scrolls with the body rather than detaching from it. */
.table-wrap { overflow-x: auto; }

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

/* A sortable column heading. It inherits the header's own type so it does not
   announce itself as a link until you are over it — a table where every
   heading is blue and underlined reads as a navigation bar. */
.sortable {
  display: inline-flex; align-items: center; gap: var(--s-1);
  color: inherit; font: inherit; letter-spacing: inherit; text-transform: inherit;
}
.sortable:hover { color: var(--text-primary); text-decoration: none; }
.sortable[aria-current] { color: var(--text-primary); }
.sortable__mark { font-size: 10px; line-height: 1; }

.table th {
  padding: var(--s-3) var(--s-4);
  background: var(--bg-subtle);
  border-bottom: 1px solid var(--border);
  text-align: left;
  font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-muted);
  white-space: nowrap;
}
.table td {
  padding: var(--s-4);
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  vertical-align: middle;
}
/* No zebra striping: the 1px rule already separates the rows, and a stripe
   adds a second thing doing the same job in a different language. */
.table tbody tr:last-child td { border-bottom: 0; }

/* Row hover. Not decoration: these tables run to a dozen columns, and without
   it there is nothing tying the Archive button on the right to the package
   name on the left — you track across with a finger on the screen. Background
   only; nothing moves, nothing resizes. */
.table tbody tr { transition: background var(--t-fast); }
.table tbody tr:hover { background: var(--bg-subtle); }

.table__num   { font-variant-numeric: tabular-nums; white-space: nowrap; }
.table__empty { padding: var(--s-7) var(--s-4); text-align: center;
                color: var(--text-muted); font-size: 13px; font-weight: 400; }

/* The first column of a list: a name you click, and a line of context. */
.table__name { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.table__name:hover { text-decoration: underline; }
/* A preview is a hint that there is more to read, not the thing itself. One
   line, clamped, so twenty-six rows stay a list you can scan rather than a
   wall — and clamped rather than nowrap, because nowrap in a table cell makes
   the COLUMN as wide as the longest body on the board. */
.table__excerpt {
  display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical;
  overflow: hidden;
}

.table__desc { margin-top: 2px; font-size: 12px; font-weight: 400;
               color: var(--text-secondary); }

.table__actions { display: flex; gap: var(--s-2); justify-content: flex-end; }

/* ── Chip ────────────────────────────────────────────────────────────────── */

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  height: 22px;
  padding: 0 var(--s-2);
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.02em;
  white-space: nowrap;
}
.chip--gray   { background: var(--chip-gray-bg);   color: var(--chip-gray-text); }
.chip--green  { background: var(--chip-green-bg);  color: var(--chip-green-text); }
.chip--purple { background: var(--chip-purple-bg); color: var(--chip-purple-text); }
.chip--pink   { background: var(--chip-pink-bg);   color: var(--chip-pink-text); }
/* "Outline" is the quiet one: it says a thing is deliberately not the default
   without colouring it like a warning. */
.chip--warning { background: var(--warning-bg);     color: var(--warning-text); }
/* The selected one in a row of filter chips: a filled chip reads as "this is
   the filter you are looking at" without needing a second control to say so. */
.chip--on      { background: var(--border-strong); color: #FFFFFF; }
.chip--outline { background: transparent; border-color: var(--border);
                 color: var(--chip-gray-text); }

.chip__dot {
  width: 6px; height: 6px; border-radius: var(--radius-pill);
  background: currentColor; flex: none;
}

/* ── Button ──────────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  height: 36px;
  padding: 0 var(--s-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text-primary);
  font-size: 13px; font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--t-fast), border-color var(--t-fast),
              color var(--t-fast);
}
.btn:hover { background: var(--bg-subtle); text-decoration: none; }
.btn .icon { font-size: 18px; }

.btn--primary {
  background: var(--border-strong);
  border-color: var(--border-strong);
  color: #FFFFFF;
  font-weight: 600;
}
.btn--primary:hover { background: #222222; border-color: #222222; }

.btn--outline { background: var(--bg-surface); border-color: var(--border-strong);
                font-weight: 600; }
.btn--outline:hover { background: var(--bg-subtle); }

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

.btn--sm { height: 32px; padding: 0 var(--s-2); font-size: 12px; }

/* Destructive by hover only: the row action should read as ordinary until the
   pointer is on it, or every list looks alarming. */
.btn--danger:hover { color: var(--chip-pink-text); border-color: var(--chip-pink-text); }

.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ── Field ───────────────────────────────────────────────────────────────── */

/* Stacked fields need space between them — `form-group`, which this replaced,
   carried a bottom margin and a column of fields relied on it. Inside a
   form-grid the grid's own gap does that job, so it is taken back off there,
   or the rows of a two-column form drift apart. */
.field { display: flex; flex-direction: column; gap: var(--s-1); min-width: 0;
         margin-bottom: var(--s-4); }
.form-grid > .field { margin-bottom: 0; }
/* ...which leaves the GRID carrying no space below it, so a field after one
   butted straight against it. A grid is a block in the same stack as a field
   and spaces itself like one. */
.form-grid { margin-bottom: var(--s-4); }

/* The row of buttons that closes a form. */
.form-actions { display: flex; align-items: center; gap: var(--s-2);
                flex-wrap: wrap; padding-top: var(--s-3); }
/* A field has exactly one <label>; requiring a class on every one of them
   buys nothing that this does not already give. */
.field > label,
.field__label { display: flex; align-items: center; gap: var(--s-1);
                font-size: 12px; font-weight: 600; }
.field__help  { font-size: 11px; font-weight: 400; color: var(--text-muted);
                line-height: 1.5; }

.input, .select, .textarea {
  width: 100%;
  height: 40px;
  padding: 0 var(--s-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
  transition: border-color var(--t-fast);
}
.textarea { height: auto; padding: var(--s-3); line-height: 1.6; resize: vertical; }
.input::placeholder, .textarea::placeholder { color: var(--text-muted); font-weight: 400; }
.input:hover, .select:hover, .textarea:hover { border-color: var(--text-muted); }

.input--num { text-align: right; font-variant-numeric: tabular-nums; }

/* The caret is drawn rather than left to the platform, so the control looks
   the same on every operating system. */
.select {
  appearance: none;
  padding-right: var(--s-6);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B6B6B' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-3) center;
  background-size: 16px;
}

.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-5); }
.form-grid--full { grid-column: 1 / -1; }
@media (max-width: 768px) { .form-grid { grid-template-columns: 1fr; gap: var(--s-4); } }

.check {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: 13px; font-weight: 500; cursor: pointer;
}
.check input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--border-strong);
                                margin: 0; cursor: pointer; }
.check input[type="checkbox"]:disabled { cursor: not-allowed; opacity: .55; }

/* ── Alert ───────────────────────────────────────────────────────────────── */

.alert {
  display: flex; align-items: flex-start; gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  font-size: 13px; font-weight: 400;
  line-height: 1.55;
}
.alert .icon { font-size: 20px; flex: none; }
.alert--warning { background: var(--warning-bg); border-color: var(--warning-border);
                  color: var(--warning-text); }
.alert--success { background: var(--chip-green-bg); border-color: var(--chip-green-bg);
                  color: var(--chip-green-text); }
.alert--error   { background: var(--chip-pink-bg); border-color: var(--chip-pink-bg);
                  color: var(--chip-pink-text); }
.alert--info    { background: var(--bg-subtle); }
.alert strong   { font-weight: 600; }

/* ── Help ────────────────────────────────────────────────────────────────── */

/* A <details>, not a button: the panel runs no JavaScript, so a control that
   needed a script would be one nobody can operate. The panel is positioned so
   opening it does not push the rest of the page down. */
/* (?) explains what a setting IS — authored prose, the same for everybody.
   (i) shows what THIS RECORD is — its code, what it belongs to, when it
   changed. They look and behave identically on purpose: one affordance to
   learn. They differ in the icon, the cursor, and in how their content is
   handled — see slHelp()/slInfo() in includes/ui.php. */
.help, .info { display: inline-block; position: relative; vertical-align: middle; }
.help > summary,
.info > summary {
  list-style: none; cursor: help;
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; vertical-align: middle;
  color: var(--text-muted);
  transition: color var(--t-fast);
}
.help > summary::-webkit-details-marker,
.info > summary::-webkit-details-marker { display: none; }
.help > summary .icon,
.info > summary .icon { font-size: 16px; vertical-align: middle; }
.help > summary:hover, .help[open] > summary,
.info > summary:hover, .info[open] > summary { color: var(--text-primary); }

.help__body,
.info__body {
  position: absolute; z-index: 60; top: 22px; left: 0;
  width: 22rem; max-width: 70vw;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--s-3);
  font-size: 12px; font-weight: 400; line-height: 1.6;
  color: var(--text-secondary);
  text-transform: none; letter-spacing: normal; text-align: left;
  white-space: normal;
}
.help--right .help__body,
.info--right .info__body { left: auto; right: 0; }
.help__body p { margin: 0 0 var(--s-2); }
.help__body :last-child { margin-bottom: 0; }
.help__body strong { color: var(--text-primary); font-weight: 600; }
.help__body code {
  font-family: inherit; font-weight: 600;
  background: var(--bg-input); padding: 1px 4px; border-radius: 3px;
}

/* (i) is a pointer, not a question: it reveals a fact rather than explaining
   one, so it gets the ordinary cursor. */
.info > summary { cursor: pointer; }

/* Label and value in two columns, so several facts read as a list rather than
   a paragraph. The label column takes only what it needs. */
.info__body dl { display: grid; grid-template-columns: auto 1fr;
                 gap: var(--s-1) var(--s-3); margin: 0; }
.info__body dt { color: var(--text-muted); white-space: nowrap; }
.info__body dd { margin: 0; color: var(--text-primary); overflow-wrap: anywhere; }

/* ── Thread entries ──────────────────────────────────────────────────────── */

/* A system line in a conversation — "moved to Ready to check". Muted and
   small; it used to carry a 2px bar down its left edge, which was decoration
   doing the job that a change of weight already does. */
.note--change { font-size: 12px; font-weight: 400; color: var(--text-muted);
                padding: var(--s-1) 0; }

/* An internal note the customer never sees. The tint and the chip beside it
   carry that; it does not need an edge marker as well. */
.note--internal { background: var(--warning-bg); color: var(--warning-text);
                  border: 1px solid var(--warning-border);
                  border-radius: var(--radius-sm); padding: var(--s-2) var(--s-3); }

/* ── Legacy, during the migration ────────────────────────────────────────
   Four utilities that lived in the old inline <style> block. The screens still
   on the old markup use them, so they are mapped onto their new equivalents
   rather than left undefined — an undefined class is a layout that quietly
   falls apart. This block is deleted with the last unmigrated screen. */
/* The three meaning colours, which the old inline block also provided. */
.text-danger  { color: var(--chip-pink-text); }
.text-success { color: var(--chip-green-text); }
.text-warning { color: var(--warning-text); }
.text-muted   { color: var(--text-muted); }

.ctl-row     { display: flex; align-items: center; gap: var(--s-2); }
.ctl-row-end { display: flex; align-items: flex-end; gap: var(--s-3); flex-wrap: wrap; }
.ctl-full    { width: 100%; }
.ctl-num     { width: 100%; text-align: right; font-variant-numeric: tabular-nums; }
.ctl-eyebrow { font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.ctl-figure  { font-size: 1.5rem; font-weight: 700; line-height: 1.2; }
.ctl-secret  { margin: var(--s-2) 0; font-weight: 600; font-size: 1.05rem;
               letter-spacing: 0.04em; word-break: break-all; }


/* ── Sign-in ─────────────────────────────────────────────────────────────── */

/* These lived in a <style> block inside login.php, which is why the sign-in
   screen was the one place a class could be invented without any check
   noticing — the class sweep reads stylesheets, and this was not one. */
/* The only page in the panel that centres a single card. Flat, like the rest —
   the old one had a gradient and a drop shadow, which is two decorations doing
   a job the 1px border already does. */
.signin      { min-height: 100vh; display: flex; align-items: center;
               justify-content: center; padding: var(--s-4); }
.signin__card { width: 100%; max-width: 23rem; background: var(--bg-surface);
                border: 1px solid var(--border); border-radius: var(--radius-md);
                padding: var(--s-6); }
.signin__wordmark { display: block; height: 22px; width: auto;
                    margin-inline: auto; }
.signin__sub  { margin-top: var(--s-2); margin-bottom: var(--s-5); text-align: center;
                font-size: 10px; font-weight: 600; text-transform: uppercase;
                letter-spacing: 0.12em; color: var(--text-muted); }
.signin__note { margin-top: var(--s-4); text-align: center; font-size: 11px;
                font-weight: 400; line-height: 1.5; color: var(--text-muted); }
.signin .field + .field { margin-top: var(--s-4); }
.signin .btn  { width: 100%; height: 40px; margin-top: var(--s-5); }


/* ── Form controls without a class ───────────────────────────────────────── */

/* `.input`, `.select` and `.textarea` are the named versions. Almost every
   control in this panel is a BARE <input> inside a table cell or a field, and
   these rules are what dress them. They came from legacy.css — which is why
   deleting that file looked safe when it was not: the bridge maps old CLASS
   names, and I had checked only class names. Nothing in the markup said
   "legacy", and every form control in the panel depended on it. */

input[type="text"], input[type="email"], input[type="password"],
input[type="date"], input[type="number"], select, textarea {
  height: 40px;
  padding: 0 var(--s-3);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-primary);
  transition: border-color var(--t-fast);
  max-width: 100%;
}
textarea { height: auto; padding: var(--s-3); line-height: 1.6; resize: vertical; }
input:hover, select:hover, textarea:hover { border-color: var(--text-muted); }
input[type="checkbox"] { height: auto; accent-color: var(--border-strong); }

select {
  appearance: none;
  padding-right: var(--s-6);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B6B6B' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--s-3) center;
  background-size: 16px;
}

/* ── Fields inside a table ───────────────────────────────────────────────── */

/* A table of editable rows is still a TABLE: you read down a column. A 40px
   control in every cell turns each row into a stack of form furniture and
   pushes the row past 100px tall, so four bundles fill the screen.
 *
   Inside a table the control is sized to the line it sits on — the cell's
   padding supplies the breathing room, so the input does not have to. */
.table td input[type="text"],
.table td input[type="email"],
.table td input[type="number"],
.table td select {
  height: 32px;
  width: 100%;
  padding: 0 var(--s-2);
  font-size: 13px;
}
.table td textarea { font-size: 13px; padding: var(--s-2); }

/* A cell holding a control needs less padding than one holding text, in both
   directions. Vertically, because the row would otherwise be the control's
   height plus two lots of 16px. Horizontally, because the control has its own
   border to mark its edge — the cell does not need to leave room for one, and
   16px a side was enough to clip "18.000.000" out of a column wide enough to
   hold it. */
.table td:has(> input, > select, > textarea) {
  padding: var(--s-2);
}

/* A table whose rows hold controls aligns every cell to the TOP.
 *
   With `vertical-align: middle` a cell holding a STACK — the bundle name, its
   code, and the list of packages it is in — fills the row's height, while
   every neighbour centres a single 32px control against that height. The
   Bundle field ended up 29px above every other field on its own row. Nothing
   was wrong with either cell; they were being aligned by different rules.
 *
   Top alignment makes the first line of each cell the thing that agrees,
   which is what you read across. The stack then grows downwards, where it
   belongs — it is detail about the name, not about the row. */
.table:has(td input, td select, td textarea) td {
  vertical-align: top;
  padding-top: var(--s-2);
  padding-bottom: var(--s-2);
}

/* Text sharing a row with controls gets the control's line box, so a label
   and the text inside a 32px input sit on the same line rather than 5px
   apart.
 *
   Only cells holding a single run of INLINE content. A cell with a block
   child — a title with an excerpt under it — would otherwise give that child
   a 32px line box too, which quietly added 14px to every row on the task
   board while looking like a rule about alignment. */
.table:has(td input, td select, td textarea)
  td:not(:has(input, select, textarea)):not(:has(div, p, ul, ol, form, table, label, br)) {
  line-height: 32px;
}

/* ── the design gallery ───────────────────────────────────────────────────
   Only used by the Design screen, which renders the token source. */
.ds-swatches { display:grid; gap:var(--s-3);
               grid-template-columns:repeat(auto-fill, minmax(190px, 1fr));
               margin-bottom:var(--s-4); }
.ds-swatch { display:flex; align-items:center; gap:var(--s-2); min-width:0; }
.ds-swatch code { font-size:12px; }
.ds-swatch > span:last-child { margin-left:auto; }
.ds-chipcolor { width:28px; height:28px; border-radius:var(--radius-sm);
                border:1px solid var(--border); flex-shrink:0; }
.ds-scale { display:flex; flex-direction:column; gap:var(--s-2);
            margin-bottom:var(--s-4); }
.ds-step { display:flex; align-items:center; gap:var(--s-3); }
.ds-bar { height:12px; background:var(--text-primary);
          border-radius:var(--radius-sm); flex-shrink:0; }
.ds-radius { width:40px; height:28px; background:var(--bg-input);
             border:1px solid var(--border-strong); flex-shrink:0; }
