/* ══════════════════════════════════════════════════════════════════════════
   components.css — the shared component vocabulary.

   Every interactive component ships default, hover, focus, active, disabled,
   and where it applies loading and error. If the save button looks different
   on two screens, one of them is wrong.
   ══════════════════════════════════════════════════════════════════════════ */

/* ═════════════════════════════════════════════════════════ BUTTONS ══ */

.btn {
  --btn-bg: var(--surface);
  --btn-fg: var(--ink);
  --btn-edge: var(--edge);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 2.25rem;
  padding: 0 var(--s-4);
  font-size: var(--t-13);
  font-weight: 600;
  letter-spacing: -0.005em;
  white-space: nowrap;
  text-decoration: none;
  color: var(--btn-fg);
  background: var(--btn-bg);
  border: 1px solid var(--btn-edge);
  border-radius: var(--r-3);
  cursor: pointer;
  /* Named properties only — never `all`. */
  transition: transform var(--t-press) var(--ease-out),
              background-color var(--t-fast) ease,
              border-color var(--t-fast) ease,
              color var(--t-fast) ease,
              opacity var(--t-fast) ease;
}
.btn svg { width: 1rem; height: 1rem; }

/* Instant feedback that the interface heard the press. */
.btn:active:not(:disabled) { transform: scale(0.97); }

@media (hover: hover) and (pointer: fine) {
  .btn:hover:not(:disabled) { background: var(--panel); border-color: var(--ink-3); }
}

.btn:disabled, .btn[aria-disabled='true'] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

/* Primary. A citron fill is only 1.50:1 against white, so the border is not
   decoration — it is what makes the control identifiable. Do not remove it. */
.btn-primary {
  --btn-bg: var(--citron);
  --btn-fg: var(--on-citron);
  --btn-edge: var(--citron-edge);
}
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover:not(:disabled) {
    background: color-mix(in oklch, var(--citron) 86%, var(--on-citron));
    border-color: var(--citron-edge);
  }
}

.btn-dark { --btn-bg: var(--bark); --btn-fg: var(--on-bark); --btn-edge: var(--bark); }
@media (hover: hover) and (pointer: fine) {
  .btn-dark:hover:not(:disabled) { background: var(--bark-2); border-color: var(--bark-2); }
}

.btn-ghost { --btn-bg: transparent; --btn-edge: transparent; --btn-fg: var(--ink-2); }
@media (hover: hover) and (pointer: fine) {
  .btn-ghost:hover:not(:disabled) { background: var(--panel); border-color: transparent; color: var(--ink); }
}

.btn-danger { --btn-fg: var(--danger); --btn-edge: var(--line); }
@media (hover: hover) and (pointer: fine) {
  .btn-danger:hover:not(:disabled) { background: var(--danger-bg); border-color: var(--danger); }
}

.btn-sm { min-height: 1.75rem; padding: 0 var(--s-3); font-size: var(--t-12); }
.btn-sm svg { width: 0.875rem; height: 0.875rem; }
.btn-lg { min-height: 2.75rem; padding: 0 var(--s-6); font-size: var(--t-15); }
.btn-block { width: 100%; }

/* Icon-only buttons must still meet the 44px touch target on coarse
   pointers, and must always carry an accessible name in the markup. */
.btn-icon { padding: 0; width: 2.25rem; min-height: 2.25rem; }
.btn-icon.btn-sm { width: 1.75rem; min-height: 1.75rem; }
@media (pointer: coarse) {
  .btn-icon { width: 2.75rem; min-height: 2.75rem; }
}

/* Loading. The label stays in place so the button does not resize and
   shift the layout around it. */
.btn.is-loading { pointer-events: none; position: relative; color: transparent; }
.btn.is-loading::after {
  content: '';
  position: absolute;
  width: 0.9rem; height: 0.9rem;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--btn-fg);
  animation: spin 620ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.btn-group { display: inline-flex; }
.btn-group .btn { border-radius: 0; margin-left: -1px; }
.btn-group .btn:first-child { border-radius: var(--r-3) 0 0 var(--r-3); margin-left: 0; }
.btn-group .btn:last-child { border-radius: 0 var(--r-3) var(--r-3) 0; }
.btn-group .btn:focus-visible { z-index: var(--z-raised); }

/* ═══════════════════════════════════════════════════ SEGMENTED SET ══ */

/* Wraps rather than pushing the page sideways. A filter group with six
   machines on it is wider than a phone, and the group is the thing that
   should give way, not the layout. */
.segmented {
  display: inline-flex;
  flex-wrap: wrap;
  max-width: 100%;
  padding: 2px;
  gap: 2px;
  background: var(--sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
}
.segmented button {
  padding: 0.3rem var(--s-3);
  white-space: nowrap;
  font-size: var(--t-12);
  font-weight: 600;
  color: var(--ink-3);
  border-radius: var(--r-2);
  transition: background-color var(--t-fast) ease, color var(--t-fast) ease;
}
.segmented button[aria-pressed='true'], .segmented button[aria-selected='true'] {
  background: var(--bg);
  color: var(--ink);
  box-shadow: var(--shadow-1);
}
@media (hover: hover) and (pointer: fine) {
  .segmented button:hover { color: var(--ink); }
}

/* ═══════════════════════════════════════════════════════════ FORMS ══ */

.field { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.field > label, .label {
  font-size: var(--t-12);
  font-weight: 600;
  color: var(--ink-2);
}
.field .hint { font-size: var(--t-12); color: var(--ink-3); }

.input, .select, .textarea {
  width: 100%;
  min-height: 2.25rem;
  padding: 0.35rem var(--s-3);
  font-size: var(--t-13);
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--edge);
  border-radius: var(--r-3);
  transition: border-color var(--t-fast) ease, box-shadow var(--t-fast) ease, background-color var(--t-fast) ease;
}
.textarea { min-height: 5rem; padding: var(--s-2) var(--s-3); resize: vertical; line-height: 1.5; }

/* Placeholders carry the same 4.5:1 requirement as body text. --ink-3 is
   measured at 5.49:1 on bg; the usual pale grey placeholder is a failure. */
.input::placeholder, .textarea::placeholder { color: var(--ink-3); opacity: 1; }

.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--focus);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--focus) 24%, transparent);
}
.input:disabled, .select:disabled, .textarea:disabled {
  background: var(--sunken);
  color: var(--ink-3);
  cursor: not-allowed;
}
.input[readonly] { background: var(--sunken); }

.input[aria-invalid='true'], .select[aria-invalid='true'], .textarea[aria-invalid='true'] {
  border-color: var(--danger);
  background: var(--danger-bg);
}

/* Errors sit next to the problem, never in a banner at the top. */
.field-error {
  display: flex;
  align-items: flex-start;
  gap: 0.3rem;
  font-size: var(--t-12);
  font-weight: 500;
  color: var(--danger);
}
.field-error svg { width: 0.85rem; height: 0.85rem; margin-top: 0.15em; }

.select {
  appearance: none;
  padding-right: var(--s-8);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 1rem top 55%, right 0.72rem top 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.input-mono { font-family: var(--font-mono); }

/* Prefix/suffix affix group — currency, units, ABN. */
.affix { display: flex; align-items: stretch; }
.affix > .input { border-radius: 0; }
.affix > :first-child { border-top-left-radius: var(--r-3); border-bottom-left-radius: var(--r-3); }
.affix > :last-child { border-top-right-radius: var(--r-3); border-bottom-right-radius: var(--r-3); }
.affix-tag {
  display: flex; align-items: center;
  padding: 0 var(--s-3);
  font-size: var(--t-12);
  font-family: var(--font-mono);
  color: var(--ink-3);
  background: var(--sunken);
  border: 1px solid var(--edge);
}
.affix-tag + .input, .input + .affix-tag { margin-left: -1px; }

/* Checkbox + radio: real controls, restyled, not reinvented. */
.check { display: inline-flex; align-items: flex-start; gap: var(--s-2); cursor: pointer; font-size: var(--t-13); }
.check input[type='checkbox'], .check input[type='radio'] {
  appearance: none;
  flex: none;
  width: 1.05rem; height: 1.05rem;
  margin-top: 0.15em;
  border: 1px solid var(--edge);
  border-radius: var(--r-2);
  background: var(--bg);
  cursor: pointer;
  transition: background-color var(--t-fast) ease, border-color var(--t-fast) ease;
}
.check input[type='radio'] { border-radius: 50%; }
.check input:checked {
  background: var(--bark);
  border-color: var(--bark);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-6'/%3E%3C/svg%3E");
  background-size: 88%;
  background-position: center;
  background-repeat: no-repeat;
}
.check input[type='radio']:checked {
  background-image: none;
  box-shadow: inset 0 0 0 3px var(--bg);
}
.check input:disabled { opacity: 0.5; cursor: not-allowed; }

/* Switch */
.switch { display: inline-flex; align-items: center; gap: var(--s-2); cursor: pointer; }
.switch input {
  appearance: none;
  position: relative;
  width: 2.1rem; height: 1.2rem;
  flex: none;
  border-radius: var(--r-full);
  background: var(--edge);
  cursor: pointer;
  transition: background-color var(--t-base) var(--ease-out);
}
.switch input::after {
  content: '';
  position: absolute;
  top: 2px; left: 2px;
  width: calc(1.2rem - 4px); height: calc(1.2rem - 4px);
  border-radius: 50%;
  background: var(--bg);
  box-shadow: var(--shadow-1);
  transition: transform var(--t-base) var(--ease-out);
}
.switch input:checked { background: var(--bark); }
.switch input:checked::after { transform: translateX(0.9rem); }
.switch input:disabled { cursor: not-allowed; }
/* A locked switch that is ON must still read as on. Dimming it uniformly
   makes an enforced setting look like an unset one. */
.switch input:disabled:not(:checked) { opacity: 0.45; }
.switch input:disabled:checked { background: var(--ink-3); }
.switch input:disabled:checked::after { background: var(--surface); }

/* ══════════════════════════════════════════════════ PANEL / SHEET ══ */
/* Cards are the lazy answer. These are surfaces with a job: a titled region
   of the page. Never nest one inside another. */

.panel {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-4);
}
.panel-head {
  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(--line);
}
.panel-head h2, .panel-head h3 { font-size: var(--t-14); font-weight: 600; }
.panel-body { padding: var(--s-4); }
.panel-body-flush { padding: 0; }
.panel-foot {
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line);
  background: var(--surface);
  border-radius: 0 0 var(--r-4) var(--r-4);
}
.panel-quiet { background: var(--surface); }

/* ═══════════════════════════════════════════════════════════ TABLE ══ */

/* A dense table is allowed to be wider than a narrow viewport — collapsing
   an order book into a phone width destroys the comparison it exists for.
   What is NOT allowed is pushing the page sideways, so it scrolls inside
   its own container instead.

   The :has() rule is deliberate belt-and-braces: forgetting .table-scroll on
   one panel out of twenty-nine is the likeliest way this regresses, and the
   consequence is a whole screen that scrolls horizontally. */
.table-scroll { overflow-x: auto; }
.panel-body:has(> .table), .panel-body:has(> table) { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: var(--t-13); }
.table caption { text-align: left; padding: 0 var(--s-4) var(--s-2); font-size: var(--t-12); color: var(--ink-3); }
.table th {
  position: sticky;
  top: 0;
  z-index: var(--z-raised);
  padding: var(--s-2) var(--s-3);
  text-align: left;
  font-size: var(--t-11);
  font-weight: 700;
  letter-spacing: 0.045em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
.table td {
  padding: var(--s-3);
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table .num, .table .right { text-align: right; }
.table .num, .table .nowrap, .table th.right { white-space: nowrap; }
.table th.right { text-align: right; }

@media (hover: hover) and (pointer: fine) {
  .table tbody tr:hover td { background: var(--surface); }
}
.table tbody tr[aria-selected='true'] td { background: var(--citron-dim); }

/* A row that needs a human. Colour alone never carries this — the row also
   gets a glyph and a text label in its status cell. */
.table tr.is-exception td { background: var(--danger-bg); }
.table tr.is-caution td { background: var(--caution-bg); }

.table-compact td { padding: var(--s-2) var(--s-3); }
.row-link { color: var(--ink); font-weight: 600; text-decoration: none; }
.row-link:hover { text-decoration: underline; color: var(--ink); }

/* ═══════════════════════════════════════════════════════════ BADGE ══ */
/* Status is never colour alone. Every badge carries a shape-differentiated
   glyph and a text label, so it survives greyscale and colour blindness. */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.12rem 0.45rem;
  font-size: var(--t-11);
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-2);
}
.badge svg { width: 0.75rem; height: 0.75rem; }
.badge-danger  { color: var(--danger);  background: var(--danger-bg);  border-color: color-mix(in oklch, var(--danger) 30%, transparent); }
.badge-caution { color: var(--caution); background: var(--caution-bg); border-color: color-mix(in oklch, var(--caution) 30%, transparent); }
.badge-info    { color: var(--info);    background: var(--info-bg);    border-color: color-mix(in oklch, var(--info) 30%, transparent); }
/* The normal case. Deliberately uncoloured — see the governing rule. */
.badge-ok      { color: var(--ink-2);   background: var(--surface); }
.badge-strong  { color: var(--on-bark); background: var(--bark); border-color: var(--bark); }

.dot { width: 0.45rem; height: 0.45rem; border-radius: 50%; background: currentColor; flex: none; }

/* Chip — a removable filter or selection. */
.chip {
  display: inline-flex; align-items: center; gap: 0.3rem;
  padding: 0.15rem 0.3rem 0.15rem 0.55rem;
  font-size: var(--t-12);
  background: var(--sunken);
  border: 1px solid var(--line);
  border-radius: var(--r-full);
}
.chip button { display: flex; padding: 0.1rem; border-radius: 50%; color: var(--ink-3); }
.chip button:hover { color: var(--ink); background: var(--line); }

/* ═════════════════════════════════════════════════════════ CALLOUT ══ */
/* Full border and a tinted ground. Never a thick side-stripe. */

.callout {
  display: flex;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  font-size: var(--t-13);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  background: var(--surface);
}
.callout svg { width: 1rem; height: 1rem; flex: none; margin-top: 0.15em; }
.callout-danger  { background: var(--danger-bg);  border-color: color-mix(in oklch, var(--danger) 32%, transparent); }
.callout-caution { background: var(--caution-bg); border-color: color-mix(in oklch, var(--caution) 32%, transparent); }
.callout-info    { background: var(--info-bg);    border-color: color-mix(in oklch, var(--info) 32%, transparent); }
.callout h4 { font-size: var(--t-13); margin-bottom: 0.15rem; }

/* ══════════════════════════════════════════════════════════════ KEY ══ */
/* A definition list of facts. The workhorse of every detail screen. */

.keys { display: grid; gap: var(--s-3) var(--s-5); }
.keys-2 { grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); }
.key dt { font-size: var(--t-11); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-3); }
.key dd { font-size: var(--t-14); margin-top: 0.1rem; }
.key dd.num { font-family: var(--font-mono); }

/* ══════════════════════════════════════════════════════════ EMPTY ══ */
/* Empty states teach the interface. "Nothing here" teaches nothing. */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s-2);
  padding: var(--s-12) var(--s-6);
  color: var(--ink-3);
}
.empty svg { width: 1.75rem; height: 1.75rem; color: var(--ink-3); }
.empty h3 { font-size: var(--t-15); color: var(--ink); }
.empty p { max-width: 34ch; font-size: var(--t-13); }
.empty .btn { margin-top: var(--s-2); }

/* Skeletons, not spinners, for content that is arriving. */
.skeleton {
  background: linear-gradient(90deg, var(--sunken) 25%, var(--panel) 37%, var(--sunken) 63%);
  background-size: 400% 100%;
  border-radius: var(--r-2);
  animation: shimmer 1.4s ease infinite;
}
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* ═══════════════════════════════════════════════════════════ TABS ══ */

.tabs { display: flex; gap: var(--s-1); border-bottom: 1px solid var(--line); overflow-x: auto; }
.tabs button {
  position: relative;
  padding: var(--s-2) var(--s-3);
  font-size: var(--t-13);
  font-weight: 600;
  color: var(--ink-3);
  white-space: nowrap;
  transition: color var(--t-fast) ease;
}
.tabs button::after {
  content: '';
  position: absolute;
  left: var(--s-2); right: var(--s-2); bottom: -1px;
  height: 2px;
  background: var(--bark);
  border-radius: 2px 2px 0 0;
  transform: scaleX(0);
  transition: transform var(--t-base) var(--ease-out);
}
.tabs button[aria-selected='true'] { color: var(--ink); }
.tabs button[aria-selected='true']::after { transform: scaleX(1); }
@media (hover: hover) and (pointer: fine) { .tabs button:hover { color: var(--ink); } }

/* ═══════════════════════════════════════════════════════ PROGRESS ══ */

.meter { height: 0.4rem; background: var(--sunken); border-radius: var(--r-full); overflow: hidden; }
.meter > span { display: block; height: 100%; background: var(--bark); border-radius: inherit;
  transition: width var(--t-slow) var(--ease-out); }
.meter.is-over > span { background: var(--danger); }
.meter.is-near > span { background: var(--caution); }

/* ═══════════════════════════════════════════════════════ TIMELINE ══ */

.timeline { list-style: none; padding: 0; display: flex; flex-direction: column; }
.timeline li { position: relative; padding: 0 0 var(--s-5) var(--s-6); }
.timeline li::before {
  content: '';
  position: absolute; left: 0.31rem; top: 0.42rem;
  width: 0.5rem; height: 0.5rem;
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--edge);
}
.timeline li::after {
  content: '';
  position: absolute; left: 0.56rem; top: 1.1rem; bottom: 0;
  width: 1px; background: var(--line);
}
.timeline li:last-child { padding-bottom: 0; }
.timeline li:last-child::after { display: none; }
.timeline li.is-done::before { background: var(--bark); border-color: var(--bark); }
.timeline li.is-now::before { border-color: var(--citron-edge); background: var(--citron); }
.timeline .when { font-size: var(--t-11); color: var(--ink-3); font-family: var(--font-mono); }

/* ═════════════════════════════════════════════════ MODAL / DRAWER ══ */
/* Native <dialog>: correct focus trapping, correct Esc handling, and it
   escapes every overflow:hidden ancestor without a portal. */

dialog { padding: 0; border: 0; color: var(--ink); background: transparent; max-width: none; max-height: none; }
dialog::backdrop { background: oklch(0.18 0.01 115 / 0.42); backdrop-filter: blur(2px); }

.modal {
  width: min(34rem, calc(100vw - 2rem));
  max-height: calc(100vh - 4rem);
  display: flex;
  flex-direction: column;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-4);
  box-shadow: var(--shadow-3);
}
.modal-wide { width: min(56rem, calc(100vw - 2rem)); }
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-4); border-bottom: 1px solid var(--line); }
.modal-body { padding: var(--s-4); overflow-y: auto; }
.modal-foot { display: flex; justify-content: flex-end; gap: var(--s-2);
  padding: var(--s-3) var(--s-4); border-top: 1px solid var(--line); background: var(--surface);
  border-radius: 0 0 var(--r-4) var(--r-4); }

/* Modals keep transform-origin: center — they are not anchored to a
   trigger. Nothing animates from scale(0); it would appear from nowhere. */
dialog[open] .modal, dialog[open] .drawer { animation: modal-in var(--t-slow) var(--ease-out); }
@keyframes modal-in { from { opacity: 0; transform: scale(0.96) translateY(6px); } }

.drawer {
  width: min(30rem, 100vw);
  height: 100vh;
  margin-left: auto;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  border-left: 1px solid var(--line);
  box-shadow: var(--shadow-3);
}
dialog.drawer-host { width: 100vw; height: 100vh; max-width: none; }
dialog[open] .drawer { animation: drawer-in var(--t-slow) var(--ease-drawer); }
@keyframes drawer-in { from { transform: translateX(100%); } }

/* ═════════════════════════════════════════════════════════ POPOVER ══ */
/* Origin-aware: a popover scales out of its trigger, not out of nowhere. */

.popover {
  position: fixed;
  z-index: var(--z-dropdown);
  min-width: 12rem;
  padding: var(--s-1);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-3);
  box-shadow: var(--shadow-2);
  transform-origin: var(--pop-origin, top left);
  animation: pop-in var(--t-base) var(--ease-out);
}
@keyframes pop-in { from { opacity: 0; transform: scale(0.96); } }

.menu-item {
  display: flex; align-items: center; gap: var(--s-2);
  width: 100%;
  padding: var(--s-2) var(--s-3);
  font-size: var(--t-13);
  text-align: left;
  color: var(--ink-2);
  border-radius: var(--r-2);
  text-decoration: none;
  transition: background-color var(--t-fast) ease, color var(--t-fast) ease;
}
.menu-item svg { width: 0.95rem; height: 0.95rem; }
.menu-item:hover, .menu-item:focus-visible { background: var(--panel); color: var(--ink); }
.menu-item[aria-disabled='true'] { opacity: 0.45; pointer-events: none; }
.menu-sep { height: 1px; margin: var(--s-1) 0; background: var(--line); }
.menu-label { padding: var(--s-2) var(--s-3) var(--s-1); font-size: var(--t-11);
  font-weight: 700; letter-spacing: 0.045em; text-transform: uppercase; color: var(--ink-3); }

/* ═════════════════════════════════════════════════════════ TOOLTIP ══ */

.tooltip {
  position: fixed;
  z-index: var(--z-tooltip);
  max-width: 20rem;
  padding: 0.28rem var(--s-2);
  font-size: var(--t-12);
  color: var(--on-bark);
  background: var(--bark-deep);
  border-radius: var(--r-2);
  pointer-events: none;
  transform-origin: var(--pop-origin, center bottom);
  animation: pop-in var(--t-fast) var(--ease-out);
}
/* Once one tooltip is open, its neighbours open instantly. Feels faster
   without defeating the point of the initial delay. */
.tooltip[data-instant] { animation-duration: 0ms; }

/* ═══════════════════════════════════════════════════════════ TOAST ══ */
/* Transitions, not keyframes — toasts are added rapidly and keyframes
   restart from zero on interruption. */

.toast-stack {
  position: fixed;
  z-index: var(--z-toast);
  right: var(--s-4); bottom: var(--s-4);
  display: flex; flex-direction: column-reverse; gap: var(--s-2);
  width: min(23rem, calc(100vw - 2rem));
}
.toast {
  display: flex; align-items: flex-start; gap: var(--s-3);
  padding: var(--s-3);
  font-size: var(--t-13);
  background: var(--bark-deep);
  color: var(--on-bark);
  border-radius: var(--r-3);
  box-shadow: var(--shadow-3);
  opacity: 1;
  transform: translateY(0);
  transition: opacity var(--t-slow) var(--ease-out), transform var(--t-slow) var(--ease-out);
}
.toast[data-enter] { opacity: 0; transform: translateY(100%); }
.toast[data-leave] { opacity: 0; transform: translateY(30%); }
.toast svg { width: 1rem; height: 1rem; flex: none; margin-top: 0.1em; }
.toast .toast-x { margin-left: auto; color: inherit; opacity: 0.65; }
.toast .toast-x:hover { opacity: 1; }

/* ══════════════════════════════════════════════════ COMMAND PALETTE ══ */

.cmdk { width: min(36rem, calc(100vw - 2rem)); margin-top: 12vh; }
.cmdk .modal { max-height: 60vh; }
.cmdk-input {
  width: 100%;
  padding: var(--s-4);
  font-size: var(--t-16);
  border: 0;
  border-bottom: 1px solid var(--line);
  background: transparent;
}
.cmdk-input:focus { outline: none; }
.cmdk-list { overflow-y: auto; padding: var(--s-2); }
.cmdk-list .menu-item[data-active='true'] { background: var(--panel); color: var(--ink); }
.cmdk-list .menu-item .where { margin-left: auto; font-size: var(--t-11); color: var(--ink-3); }

/* ════════════════════════════════════════════════════════ SWATCHES ══ */

.swatch-set { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.swatch {
  position: relative;
  width: 2.1rem; height: 2.1rem;
  border-radius: var(--r-3);
  border: 1px solid var(--edge);
  cursor: pointer;
  transition: box-shadow var(--t-fast) ease, transform var(--t-press) var(--ease-out);
}
.swatch:active { transform: scale(0.94); }
.swatch[aria-pressed='true'] { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--bark); }
.swatch[aria-disabled='true'] { opacity: 0.35; cursor: not-allowed; }
/* A locked swatch is a brand rule, not a styling choice. The scrim is what
   makes the glyph legible on a pale colour — a white lock on a white swatch
   communicates nothing. */
.swatch[data-locked]::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2.2'%3E%3Crect x='5' y='11' width='14' height='10' rx='2'/%3E%3Cpath d='M8 11V7a4 4 0 0 1 8 0v4'/%3E%3C/svg%3E") center/0.85rem no-repeat,
    oklch(0.22 0.014 115 / 0.52);
}

/* ══════════════════════════════════════════════════════════ MISC ══ */

.divider-h { height: 1px; background: var(--line); }
.stack-4 > * + * { margin-top: var(--s-4); }
.stack-3 > * + * { margin-top: var(--s-3); }
.stack-2 > * + * { margin-top: var(--s-2); }
.stack-6 > * + * { margin-top: var(--s-6); }

.grid-auto { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--s-4); }

/* A quantity that changed since the last render. Draws the eye once, then
   releases it — this is state, not decoration. */
.flash { animation: flash 900ms var(--ease-out); }
@keyframes flash {
  from { background: var(--citron-dim); }
  to { background: transparent; }
}

/* ══════════════════════════════════════════════════ ROSTER PASTE ══ */
/* Pasted spreadsheet data must keep its columns lined up, or a buyer
   cannot see what they pasted. Wrapping it destroys the only structure
   the text has. */
.roster-paste {
  white-space: pre;
  overflow-x: auto;
  overflow-y: auto;
  line-height: 1.65;
  font-size: var(--t-12);
  tab-size: 12;
}

/* ═══════════════════════════════════════════════════ FILE PREVIEW ══ */
/* The exact bytes, before anything downloads. Control characters are shown
   rather than hidden, because a planner debugging a rejected import needs
   to see whether the line ending is CRLF or LF. */
.file-preview {
  max-height: 22rem;
  overflow: auto;
  padding: var(--s-4);
  font-family: var(--font-mono);
  font-size: var(--t-11);
  line-height: 1.85;
  white-space: pre;
  background: var(--sunken);
  color: var(--ink-2);
  border-radius: 0 0 var(--r-4) var(--r-4);
}
.file-preview .ctrl { color: var(--ink-3); opacity: 0.7; }
.file-preview .ctrl + br { display: none; }
