/* ============================================================
 * Issuetracker - design system skin (Phase A: foundation)
 * ------------------------------------------------------------
 * Applies the shared portfolio design tokens on top of the
 * existing Bootstrap "Front" theme. This file is loaded AFTER
 * theme.min.css so its rules win where they overlap.
 *
 * Canonical token source (do not diverge silently):
 *   app-design-templates/tokens/tokens.css
 *
 * Phase A only restyles shared primitives (colours, font,
 * borders, buttons, badges, cards, tables, forms, links,
 * modals). It does NOT restructure any page layout - that is
 * Phase B onward. No HTML or JavaScript is changed by this file.
 * ============================================================ */

:root {
  /* ---------- Canonical design tokens ---------- */
  --bg: #faf9f5;
  --surface: #f0eee6;
  --surface-soft: #f3eee5;
  --code-bg: #efece0;

  --border: #e8e6dc;
  --border-strong: #d8d5c7;

  --text: #141413;
  --text-muted: #706f6c;

  --accent: #65b144;
  --accent-strong: #4c8533;
  --accent-soft: rgba(101, 177, 68, 0.12);
  --on-accent: #ffffff;

  --hover: rgba(20, 20, 19, 0.04);

  --paper-bg: #ffffff;
  --paper-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);

  --ok-text: #166534;   --ok-bg: #dcfce7;   --ok-border: #86efac;
  --warn-text: #92400e; --warn-bg: #fef3c7; --warn-border: #fcd34d;
  --danger-text: #991b1b; --danger-bg: #fee2e2; --danger-border: #fca5a5;

  --font-sans: "Open Sans", ui-sans-serif, -apple-system, BlinkMacSystemFont,
    "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;

  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;

  /* ---------- Map Bootstrap variables to tokens ----------
   * Bootstrap 5.2 reads these custom properties, so remapping
   * them here re-skins most components without per-rule work. */
  --bs-body-bg: var(--bg);
  --bs-body-color: var(--text);
  --bs-body-font-family: var(--font-sans);
  --bs-body-font-size: 14px;
  --bs-body-line-height: 1.55;

  --bs-border-color: var(--border);
  --bs-border-radius: var(--radius-md);
  --bs-border-radius-sm: var(--radius-sm);
  --bs-border-radius-lg: var(--radius-lg);

  --bs-link-color: var(--accent-strong);
  --bs-link-hover-color: var(--accent-strong);
  --bs-link-color-rgb: 76, 133, 51;
  --bs-link-hover-color-rgb: 76, 133, 51;

  --bs-primary: var(--accent);
  --bs-primary-rgb: 101, 177, 68;
  --bs-secondary-color: var(--text-muted);
  --bs-tertiary-color: var(--text-muted);
  --bs-emphasis-color: var(--text);
}

/* ---------- Base page ---------- */
body {
  background-color: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--bs-body-font-size);
  line-height: var(--bs-body-line-height);
}

/* ---------- Links (content, not the coloured navbar) ---------- */
a {
  color: var(--accent-strong);
}
a:hover {
  color: var(--accent-strong);
}
/* active dropdown items (view toggle, group-by current) use the accent,
   not Bootstrap's default blue. */
.dropdown-item.active,
.dropdown-item:active {
  background-color: var(--accent-soft);
  color: var(--accent-strong);
}

/* ============================================================
 * App top bar (Phase B) - replaces the old green navbar.
 * A full-width warm surface bar; the brand mark is the only
 * filled green. Keeps the same buttons/hooks, new styling.
 * ============================================================ */
.app-topbar {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 52px;
  padding: 0 12px;
  margin-bottom: 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.app-brand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: #fff;
  flex-shrink: 0;
}
.app-brand:hover {
  background: var(--accent-strong);
  color: #fff;
}
.app-topbar__divider {
  width: 1px;
  height: 22px;
  background: var(--border-strong);
  flex-shrink: 0;
}
.app-crumb {
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.app-crumb small {
  color: var(--text-muted);
  font-weight: 400;
}
.app-topbar__spacer {
  flex: 1;
}
.app-seg {
  display: inline-flex;
  background: var(--paper-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  flex-shrink: 0;
}
.app-seg__btn {
  font-size: 12px;
  padding: 6px 13px;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
}
.app-seg__btn:hover {
  background: var(--hover);
  color: var(--text);
}
.app-seg__btn.is-active {
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-weight: 600;
}
.app-groupby,
.app-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font: inherit;
  font-size: 12px;
  color: var(--text-muted);
  background: var(--paper-bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: 6px 11px;
  cursor: pointer;
  white-space: nowrap;
}
.app-btn {
  color: var(--text);
  font-size: 13px;
}
.app-groupby b {
  color: var(--text);
  font-weight: 600;
}
.app-groupby:hover,
.app-btn:hover {
  background: var(--hover);
}
.app-iconbtn {
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  flex-shrink: 0;
}
.app-iconbtn:hover {
  background: var(--hover);
  color: var(--text);
}
.app-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent-strong);
  color: #fff;
  border: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
}
.app-newissue {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
}

/* show/hide by screen size */
.app-mobile {
  display: none;
}
@media (max-width: 700px) {
  .app-desk {
    display: none !important;
  }
  .app-mobile {
    display: inline-flex !important;
  }
  .app-hide-phone {
    display: none;
  }
  .app-topbar {
    gap: 8px;
    padding: 0 10px;
  }
}

/* ---------- Buttons ---------- */
.btn-primary,
.btn-success {
  --bs-btn-bg: var(--accent);
  --bs-btn-border-color: var(--accent);
  --bs-btn-color: var(--on-accent);
  --bs-btn-hover-bg: var(--accent-strong);
  --bs-btn-hover-border-color: var(--accent-strong);
  --bs-btn-hover-color: var(--on-accent);
  --bs-btn-active-bg: var(--accent-strong);
  --bs-btn-active-border-color: var(--accent-strong);
  --bs-btn-active-color: var(--on-accent);
  --bs-btn-disabled-bg: var(--accent);
  --bs-btn-disabled-border-color: var(--accent);
  --bs-btn-disabled-color: var(--on-accent);
}
.btn-outline-success {
  --bs-btn-color: var(--accent-strong);
  --bs-btn-border-color: var(--accent);
  --bs-btn-hover-bg: var(--accent);
  --bs-btn-hover-border-color: var(--accent);
  --bs-btn-hover-color: var(--on-accent);
  --bs-btn-active-bg: var(--accent-strong);
  --bs-btn-active-border-color: var(--accent-strong);
}
.btn-outline-secondary {
  --bs-btn-color: var(--text-muted);
  --bs-btn-border-color: var(--border-strong);
  --bs-btn-hover-bg: var(--hover);
  --bs-btn-hover-color: var(--text);
  --bs-btn-hover-border-color: var(--border-strong);
  --bs-btn-active-bg: var(--hover);
  --bs-btn-active-color: var(--text);
}

/* ---------- Badges / status pills ---------- */
.badge {
  font-weight: 600;
}
.text-bg-success {
  background-color: var(--accent) !important;
  color: var(--on-accent) !important;
}
.bg-success {
  background-color: var(--accent) !important;
}

/* ---------- Cards ---------- */
.card {
  --bs-card-bg: var(--paper-bg);
  --bs-card-border-color: var(--border);
  --bs-card-border-radius: var(--radius-lg);
  --bs-card-cap-bg: var(--surface-soft);
  --bs-card-color: var(--text);
}
/* board/list cards use .bg-light; make it a warm surface, not grey */
.bg-light {
  background-color: var(--surface-soft) !important;
}

/* ---------- Tables ---------- */
.table {
  --bs-table-bg: transparent;
  --bs-table-color: var(--text);
  --bs-table-border-color: var(--border);
  --bs-table-striped-bg: var(--surface-soft);
  --bs-table-striped-color: var(--text);
  --bs-table-hover-bg: var(--hover);
  --bs-table-hover-color: var(--text);
}
.table > thead th,
table thead th {
  color: var(--text-muted);
  font-weight: 600;
}

/* ---------- Forms ---------- */
.form-control,
.form-select {
  background-color: var(--paper-bg);
  border-color: var(--border-strong);
  color: var(--text);
}
.form-control:focus,
.form-select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.form-control::-moz-placeholder {
  color: var(--text-muted);
  opacity: 1;
}
.form-control::placeholder {
  color: var(--text-muted);
  opacity: 1;
}
.form-check-input:checked {
  background-color: var(--accent);
  border-color: var(--accent);
}
.form-check-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.form-switch .form-check-input:checked {
  background-color: var(--accent);
  border-color: var(--accent);
}

/* ---------- Dropdowns ---------- */
.dropdown-menu {
  --bs-dropdown-bg: var(--paper-bg);
  --bs-dropdown-border-color: var(--border);
  --bs-dropdown-border-radius: var(--radius-md);
  --bs-dropdown-link-color: var(--text);
  --bs-dropdown-link-hover-bg: var(--hover);
  --bs-dropdown-link-hover-color: var(--text);
  --bs-dropdown-link-active-bg: var(--accent-soft);
  --bs-dropdown-link-active-color: var(--accent-strong);
}

/* ---------- Modals ---------- */
.modal-content {
  background-color: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
}
.modal-header,
.modal-footer {
  border-color: var(--border);
}

/* ---------- Offcanvas (filter panel) ---------- */
.offcanvas {
  --bs-offcanvas-bg: var(--surface);
  --bs-offcanvas-color: var(--text);
  --bs-offcanvas-border-color: var(--border);
}

/* ---------- Pagination ---------- */
.pagination {
  --bs-pagination-color: var(--text-muted);
  --bs-pagination-hover-color: var(--accent-strong);
  --bs-pagination-hover-bg: var(--hover);
  --bs-pagination-active-bg: var(--accent);
  --bs-pagination-active-border-color: var(--accent);
  --bs-pagination-border-color: var(--border);
}

/* ---------- Code / monospace ---------- */
code, pre, kbd, samp {
  font-family: var(--font-mono);
}
code {
  background: var(--code-bg);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
}

/* ============================================================
 * Utilities for later phases (defined now, used in Phase C).
 * These do not affect any current markup.
 * ============================================================ */

/* Signature "paper" surface: white sheet floating on cream bg. */
.ds-paper {
  background: var(--paper-bg);
  box-shadow: var(--paper-shadow);
  border-radius: var(--radius-lg);
}

/* Small uppercase muted section header. */
.ds-panel-header {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 600;
  color: var(--text-muted);
}

/* Soft status pills. */
.ds-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  border: 1px solid transparent;
}
.ds-pill--ok { color: var(--ok-text); background: var(--ok-bg); border-color: var(--ok-border); }
.ds-pill--warn { color: var(--warn-text); background: var(--warn-bg); border-color: var(--warn-border); }
.ds-pill--danger { color: var(--danger-text); background: var(--danger-bg); border-color: var(--danger-border); }
.ds-pill--muted { color: var(--text-muted); background: var(--surface-soft); border-color: var(--border); }
.ds-pill--info { color: var(--info-text, #1e40af); background: var(--info-bg, #dbeafe); border-color: var(--info-border, #93c5fd); }

/* ============================================================
 * Page container + generic bits (Phase C, reused across pages)
 * ============================================================ */
.ds-page { max-width: 1120px; margin: 0 auto; padding: 20px; }
.ds-page__head { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; }
.ds-page__title { font-size: 20px; font-weight: 600; margin: 0; }
.flex-spacer { flex: 1; }

.ds-empty {
  text-align: center;
  padding: 56px 20px;
  background: var(--paper-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.ds-empty__icon {
  width: 46px; height: 46px; margin: 0 auto 12px;
  border-radius: 50%; background: var(--accent-soft); color: var(--accent-strong);
  display: flex; align-items: center; justify-content: center; font-size: 24px;
}
.ds-empty__title { font-size: 16px; font-weight: 600; margin: 0 0 6px; }
.ds-empty__text { color: var(--text-muted); margin: 0 0 16px; }

/* ============================================================
 * Projects index cards (Phase C)
 * ============================================================ */
.proj-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
}
.proj-card {
  background: var(--paper-bg);
  box-shadow: var(--paper-shadow);
  border-radius: var(--radius-lg);
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.proj-card__title { margin: 0; font-size: 15px; font-weight: 600; }
.proj-card__title a { color: var(--text); text-decoration: none; }
.proj-card__title a:hover { color: var(--accent-strong); }
.proj-card__meta { display: flex; flex-wrap: wrap; gap: 14px; font-size: 12px; color: var(--text-muted); }
.proj-card__meta b { color: var(--text); font-weight: 600; }
.proj-card__foot {
  display: flex; align-items: center; gap: 8px;
  margin-top: 2px; padding-top: 10px; border-top: 1px solid var(--border);
}
.ds-del {
  width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-md); color: var(--text-muted); border: 1px solid transparent;
}
.ds-del:hover { background: var(--danger-bg); color: var(--danger-text); }

@media (max-width: 640px) {
  .proj-grid { grid-template-columns: 1fr; }
  .ds-page { padding: 12px; }
}

/* ============================================================
 * Kanban board - standard boards (Phase C)
 * Horizontal-scroll row of column panels with informative cards.
 * Drag/sort hooks preserved: .drag-col .sort-col on the body,
 * cards are .card (issue) / .card.no-drug (add button).
 * ============================================================ */
.kb-wrap { padding: 4px 16px 0; }
.kb-board {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  height: calc(100vh - 96px);
  overflow-x: auto;
  overflow-y: hidden;
  padding-bottom: 10px;
}
.kb-col {
  width: 288px;
  flex: 0 0 288px;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}
.kb-col__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
.kb-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.kb-col__title {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kb-col__count {
  margin-left: auto;
  font-size: 11px;
  color: var(--text-muted);
  background: var(--surface-soft);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0 7px;
  flex-shrink: 0;
}
.kb-col__body {
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-y: auto;
  overflow-x: hidden;
}
/* the "+ Add issue" is a link pinned under the column header, OUTSIDE the
   drag/sort body, so a card can never be dropped above it. */
.kb-add {
  display: block;
  margin: 8px 8px 0;
  padding: 7px;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  color: var(--text-muted);
  text-decoration: none;
  font-size: 12px;
  text-align: center;
}
.kb-add:hover { background: var(--hover); color: var(--text); }
/* issue cards */
.card.kb-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--paper-bg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  padding: 10px;
  margin: 0;
  cursor: pointer;
}
.card.kb-card:hover { border-color: var(--border-strong); }
.kb-card__img { display: block; margin-bottom: 8px; }
.kb-card__img img { max-width: 100%; border-radius: var(--radius-sm); }
.kb-card__title { font-size: 13px; font-weight: 600; line-height: 1.35; }
.kb-card__title a { color: var(--text); text-decoration: none; }
.kb-card__title a:hover { color: var(--accent-strong); }
.kb-card__meta { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
.kb-card__foot { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.kb-assignee {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--border-strong);
  color: var(--text-muted); display: inline-flex; align-items: center;
  justify-content: center; font-size: 10px; font-weight: 600;
}
.kb-due { font-size: 11px; color: var(--text-muted); }

/* Tracker #12, moved into the topic panel by #120.

   It used to sit on every board card, sized to be as quiet as the due date.
   It now lives in the panel sidebar alongside Status, Type and Priority, and
   is a plain `form-select` like all six of them - a dropdown and "of N" under
   a `ds-f__label`. The number box that sat beside it was removed as redundant
   in #120: the dropdown already offers every legal place and cannot produce an
   illegal one. */
.kb-pos__row { display: flex; align-items: center; gap: 8px; }
/* Three quarters of the field, leaving room for "of N" beside it. A fixed rem
   width was tried first and came out at about half of its neighbours, which
   made the row look like a different kind of control than the six selects
   above it. A percentage keeps it in proportion at any panel width, including
   the fullscreen mode.

   `flex-basis` rather than `width`, because `.ds-panel .ds-f .form-select`
   sets `width: 100%` and a flex-basis that is not `auto` takes precedence over
   it. */
.kb-pos__select {
  flex: 0 0 75%;
  width: auto;
  /* A place number is one or two characters in a field sized for a label, so
     left-aligned it reads as a stray digit. Centred it reads as a value.

     `text-align-last` as well as `text-align`: Chrome centres the closed
     select's text from the first, Firefox from the second.

     The left padding is raised to match the right, which the base rule sets to
     28px to clear the chevron. Without that the text centres inside an
     asymmetric box and lands visibly left of centre. */
  text-align: center;
  -moz-text-align-last: center;
       text-align-last: center;
  padding-left: 28px;
}
.kb-pos__of { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.kb-pos__error {
  display: block; margin-top: 4px; font-size: 12px; color: var(--danger-text);
}
.kb-pos__error:empty { display: none; }

@media (max-width: 640px) {
  .kb-wrap { padding: 4px 10px 0; }
  .kb-board { height: calc(100vh - 84px); scroll-snap-type: x proximity; }
  .kb-col { width: 86vw; flex-basis: 86vw; scroll-snap-align: start; }
}

/* ============================================================
 * List view / DataTables (Phase C)
 * ============================================================ */
.dataTables_wrapper { padding: 0 12px; }
.dataTables_filter input,
.dataTables_length select {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: 4px 8px;
  background: var(--paper-bg);
  color: var(--text);
  font: inherit;
  font-size: 13px;
}
.dataTables_filter input:focus,
.dataTables_length select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
#issues thead th { white-space: nowrap; }
#issues td, #issues th { vertical-align: middle; }
#issues td img { max-height: 40px; border-radius: var(--radius-sm); }
/* mobile: let the table scroll sideways instead of squishing */
@media (max-width: 640px) {
  .dataTables_wrapper { padding: 0 4px; }
  .ds-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  table#issues.dataTable { min-width: 620px; }
}

/* ============================================================
 * Forms (Phase C4) - project new/edit, collaborators, profile
 * ============================================================ */
.ds-page--narrow { max-width: 936px; }
.ds-card {
  background: var(--paper-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  padding: 18px;
  margin-bottom: 14px;
}
.ds-card > .table { margin-bottom: 0; }
.ds-section__title { font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.ds-help { font-size: 12px; color: var(--text-muted); margin: 0 0 14px; }
.ds-field { margin-bottom: 14px; }
.ds-field:last-child { margin-bottom: 0; }
.ds-label { display: block; font-size: 12px; font-weight: 600; color: var(--text); margin-bottom: 5px; }
.ds-actions { display: flex; align-items: center; gap: 8px; padding-top: 2px; }
.ds-invite { display: flex; gap: 8px; align-items: center; }
.ds-invite .form-control { max-width: 340px; }
@media (max-width: 640px) {
  .ds-invite { flex-direction: column; align-items: stretch; }
  .ds-invite .form-control { max-width: none; }
}

/* ============================================================
 * Issue detail slide-over panel (Phase D, option 2)
 * The iframe is kept; the container becomes a right-side panel
 * with fixed height, a control bar, and a full-screen toggle.
 * ============================================================ */
.ds-slideover .modal-dialog.ds-slideover__dialog {
  margin: 0 0 0 auto;
  max-width: 780px;
  width: 100%;
  height: 100%;
}
.ds-slideover .modal-content.ds-slideover__content {
  height: 100vh;
  height: 100dvh;
  border: 0;
  border-left: 1px solid var(--border-strong);
  border-radius: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.ds-slideover__bar {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 44px;
  padding: 0 10px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.ds-slideover__body { flex: 1; min-height: 0; background: var(--bg); }
.ds-slideover__body iframe { width: 100%; height: 100%; border: 0; display: block; }
.ds-slideover.is-fullscreen .modal-dialog.ds-slideover__dialog { max-width: 100%; }
/* gentle slide-in from the right */
.ds-slideover.fade .modal-dialog.ds-slideover__dialog {
  transform: translateX(30px);
  transition: transform 0.2s ease-out;
}
.ds-slideover.show .modal-dialog.ds-slideover__dialog { transform: none; }
@media (max-width: 640px) {
  .ds-slideover .modal-dialog.ds-slideover__dialog { max-width: 100%; }
}

/* "Saved" chip shown briefly after an autosave inside the issue form */
.ds-saved {
  display: inline-block;
  vertical-align: middle;
  margin-left: 8px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ok-text);
  background: var(--ok-bg);
  border: 1px solid var(--ok-border);
  border-radius: 999px;
  padding: 1px 9px;
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}
.ds-saved.show { opacity: 1; }

/* ============================================================
 * Issue detail panel body (Task #5 restyle - keeps the iframe)
 * The edit form inside the slide-over is laid out as
 * head / body(main + side) / foot to match the approved mockup.
 * Pure layout + skin: every JS hook (.title-header, #quill-editor,
 * #dropzone, .form-select, #issue_labels, .refresh) is preserved
 * by the view, so autosave/editor/upload behaviour is unchanged.
 * ============================================================ */
.ds-panel {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  background: var(--paper-bg);
  color: var(--text);
}
.ds-panel__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}
.ds-panel__heading { flex: 1; min-width: 0; }
.ds-panel__id { font-size: 12px; color: var(--text-muted); font-weight: 600; }
.ds-panel__title {
  margin: 2px 0 0;
  font-size: 18px;
  font-weight: 600;
  line-height: 1.3;
  word-wrap: break-word;
}
.ds-panel__title .title-label { cursor: text; }
.ds-panel__title .edit-title { margin-left: 6px; vertical-align: middle; }
.ds-panel__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: grid;
  grid-template-columns: 1fr 250px;
  align-items: start;
}
.ds-panel__main {
  padding: 16px;
  border-right: 1px solid var(--border);
  min-width: 0;
}
.ds-panel__side { padding: 16px; }
.ds-panel__foot {
  flex: 0 0 auto;
  border-top: 1px solid var(--border);
  padding: 12px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.ds-foot-meta { font-size: 12px; color: var(--text-muted); }

/* fields inside the panel: stacked uppercase label + full-width control */
.ds-panel .ds-f { margin-bottom: 14px; }
.ds-panel .ds-f:last-child { margin-bottom: 0; }
.ds-panel .ds-f__label {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  font-weight: 700;
  margin-bottom: 5px;
}
.ds-panel .ds-f .form-select,
.ds-panel .ds-f .form-control { width: 100%; font-size: 13px; }
.ds-panel .ds-ro { font-size: 13px; color: var(--text); }

/* dropzone: warm, thin dashed to match the mockup (overrides the
   grey inline style in the form's own <style> block via higher
   specificity: .ds-panel #dropzone beats #dropzone). */
.ds-panel #dropzone {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  padding: 16px;
  color: var(--text-muted);
  font-size: 13px;
  background: transparent;
}
.ds-panel #select-files { color: var(--accent-strong); }

@media (max-width: 640px) {
  .ds-panel__body { grid-template-columns: 1fr; }
  .ds-panel__main { border-right: 0; border-bottom: 1px solid var(--border); }
}

/* ============================================================
 * Task #5 - tighter density pass to match the mockup, and move
 * the panel controls (full screen + close) to float top-right so
 * the header is one compact row (no separate grey bar above it).
 * Appended last so these win over the base .ds-panel rules.
 * ============================================================ */
.ds-slideover .modal-content.ds-slideover__content { position: relative; }
.ds-slideover__fs,
.ds-slideover__close {
  position: absolute;
  top: 8px;
  z-index: 6;
  width: 30px;
  height: 30px;
}
.ds-slideover__close { right: 8px; }
.ds-slideover__fs { right: 42px; }

/* single-row header: reserve room on the right for the floating buttons */
.ds-panel__head { padding: 12px 88px 11px 16px; }
.ds-panel__id { line-height: 1.2; }
.ds-panel__title { margin-top: 1px; font-size: 17px; }

/* tighter body + fields */
.ds-panel__main,
.ds-panel__side { padding: 14px 16px; }
.ds-panel .ds-f { margin-bottom: 10px; }
.ds-panel .ds-f__label { margin-bottom: 3px; }
.ds-panel .ds-f .form-select {
  padding: 5px 28px 5px 9px;
  min-height: 0;
  background-position: right 9px center;
}
.ds-panel .ds-f .form-control {
  padding: 5px 9px;
  min-height: 0;
}
/* shorter description editor (overrides the inline min-height:200px) */
.ds-panel #quill-editor { min-height: 150px !important; }
.ds-panel .ql-container { min-height: 0; }
.ds-panel .ql-editor { min-height: 96px; }
/* comment box: a touch tighter */
.ds-panel__main textarea.form-control { min-height: 70px; }

/* ============================================================
 * Task #5 - toolbar polish + a little breathing room (per Tore):
 * editor toolbar cream + compact to match the comment headers;
 * comment headers compact; more gap id->title and around fields.
 * ============================================================ */
/* Quill editor toolbar: cream like the comment headers, tighter */
.ds-panel .ql-toolbar.ql-snow {
  background: var(--surface-soft);
  border-color: var(--border);
  padding: 4px 6px;
}
.ds-panel .ql-container.ql-snow { border-color: var(--border); }
/* comment card headers ("Now" / author): same cream, compact */
.ds-panel .card { --bs-card-cap-bg: var(--surface-soft); }
.ds-panel .card-header {
  padding: 7px 12px !important;
  background: var(--surface-soft);
  font-size: 13px;
}
/* breathing room: a bit more under the id, and around each field */
.ds-panel__title { margin-top: 5px; }
.ds-panel .ds-f { margin-bottom: 14px; }

/* Task #5 - shorter action buttons (Save / Comment / Delete / Cancel)
   to ~80% height, and align the comment file input to match. */
.ds-panel .btn-sm {
  --bs-btn-padding-y: 2px;
  line-height: 1.3;
}
.ds-panel #new-comment .form-control-sm {
  padding-top: 2px;
  padding-bottom: 2px;
}

/* Task #5 - comment file picker: match the Comment button height + align */
.ds-panel #new-comment .form-control-sm {
  padding-top: 2px;
  padding-bottom: 2px;
  font-size: 13px;
  line-height: 1.4;
}
.ds-panel #new-comment .form-control-sm::file-selector-button {
  padding-top: 2px;
  padding-bottom: 2px;
}

/* Task #5 - comment file picker + Comment button: pin identical compact
   height so they line up exactly (native file inputs size from their
   internal browse button, so padding alone is not enough). */
.ds-panel #new-comment .d-flex { align-items: center; }
.ds-panel #new-comment .btn-sm {
  height: 26px;
  padding-top: 0;
  padding-bottom: 0;
  line-height: 1;
  display: inline-flex;
  align-items: center;
}
.ds-panel #new-comment .form-control-sm {
  height: 26px;
  padding-top: 0;
  padding-bottom: 0;
  font-size: 13px;
  line-height: 24px;
}
.ds-panel #new-comment .form-control-sm::file-selector-button {
  height: 24px;
  padding-top: 0;
  padding-bottom: 0;
  margin-top: 0;
  margin-bottom: 0;
  line-height: 24px;
}
.ds-panel #new-comment .form-control-sm::-webkit-file-upload-button {
  height: 24px;
  padding-top: 0;
  padding-bottom: 0;
  margin-top: 0;
  margin-bottom: 0;
  line-height: 24px;
}

/* Task #5 - force comment file picker + button to equal height (!important
   to defeat any cascade/stale issue; box-sizing+overflow to clip native
   browse-button overflow). */
.ds-panel #new-comment .form-control-sm {
  height: 28px !important;
  min-height: 28px !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  line-height: 26px;
  box-sizing: border-box;
  overflow: hidden;
}
.ds-panel #new-comment .form-control-sm::file-selector-button {
  height: 26px !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  line-height: 26px !important;
}
.ds-panel #new-comment .form-control-sm::-webkit-file-upload-button {
  height: 26px !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  line-height: 26px !important;
}
.ds-panel #new-comment .btn-sm {
  height: 28px !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  display: inline-flex;
  align-items: center;
  line-height: 1;
}

/* #19 - full-screen issue panel = 90% x 90%, centred, so a border of the
   board shows behind it (instead of covering the whole window). */
.ds-slideover.is-fullscreen .modal-dialog.ds-slideover__dialog {
  max-width: 90%;
  width: 90%;
  height: 90dvh;
  margin: 5dvh auto;
}
.ds-slideover.is-fullscreen .modal-content.ds-slideover__content {
  height: 90dvh;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
}

/* #16 - top navbar touch-ups: "New issue" (green) and "Project" get the same
   height so the green one is no longer taller; smaller avatar initials. */
.app-topbar .app-newissue,
.app-topbar .app-btn,
.app-topbar .app-groupby {
  height: 32px;
  padding-top: 0;
  padding-bottom: 0;
  display: inline-flex;
  align-items: center;
}
.app-avatar {
  font-size: 10px;
  line-height: 1;
  white-space: nowrap;
}

/* #16 follow-up: the green "New issue" is a Bootstrap .btn whose theme
   min-height kept it taller than the "Project" (.app-btn) button. Pin it to
   the same box (height, min-height, padding, border) so they match exactly. */
.app-topbar a.app-newissue.btn.btn-sm {
  height: 32px;
  min-height: 32px;
  padding: 0 11px;
  line-height: 1;
}

/* #19 - darker backdrop when the issue panel is in full-screen (90%) mode,
   so the board behind it recedes and the panel stands out. Scoped with :has()
   so the normal right-side slide-over keeps the lighter default backdrop. */
body:has(.ds-slideover.is-fullscreen.show) .modal-backdrop.show {
  opacity: 0.72;
}

/* ============================================================
 * Tracker #29 - flash messages
 *
 * They used to sit in the document flow inside `.container.mt-3`, so every
 * message shoved the whole page down by its own height and the layout jumped
 * as it appeared. Now they float in the top-right corner and the page below
 * never moves.
 *
 * z-index 1085 is deliberate. Bootstrap 5 uses 1045 for .offcanvas (the
 * filter panel), 1050 for .modal-backdrop and 1055 for .modal (the issue
 * slide-over). Anything lower would put a message BEHIND the filter panel,
 * where nobody would ever see it. 1085 stays below .tooltip (1080 is
 * Bootstrap's, so tooltips lose - accepted: a tooltip is transient and the
 * user can move the pointer).
 *
 * The topbar is 52px and is NOT sticky, so 68px clears it on first paint and
 * the messages simply float over the content once the page is scrolled.
 * ============================================================ */
.flash-stack {
  position: fixed;
  top: 68px;
  right: 16px;
  z-index: 1085;
  width: min(420px, calc(100vw - 32px));
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;
}

/* The container ignores clicks so it never blocks the page underneath; each
   message takes them back, otherwise the X would not be clickable. */
.flash-stack__item {
  pointer-events: auto;
  margin-bottom: 0;
  box-shadow: var(--paper-shadow);
  border-radius: var(--radius-lg);
  position: relative;
  isolation: isolate;
}

/* Found in staging QA: the message was see-through, and the button underneath
   showed through it.
 *
 * The theme's alert colours are deliberately faint - `.alert-soft-success` is
 * `rgba(7, 124, 118, 0.075)`, i.e. 7.5% opacity. That is fine for the box's old
 * life sitting IN the page on a plain background. Floating over content it is
 * near enough transparent.
 *
 * Setting `background-color` here would fix the transparency and throw away
 * the colour with it, because this file loads after theme.min.css and would
 * win the cascade - and then every alert variant's tint would have to be
 * restated by hand. Instead an opaque layer is painted BEHIND the element, and
 * the theme's tint keeps painting on top of it exactly as it was written.
 */
.flash-stack__item::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--paper-bg);
  border-radius: inherit;
}

@media (max-width: 575.98px) {
  .flash-stack {
    top: 60px;
    left: 16px;
    right: 16px;
    width: auto;
  }
}

/* Someone who has asked the system to reduce motion should not get the
   0.15s Bootstrap cross-fade on a message they need to read. */
@media (prefers-reduced-motion: reduce) {
  .flash-stack__item.fade {
    transition: none;
  }
}

/* ============================================================
 * Tracker #7 - filter chip bar. Closes #75.
 *
 * Filters were invisible: saved in localStorage, applied on every visit, with
 * nothing on the page to say so. People saw fewer tasks than they expected and
 * could not tell a filter from missing data. This bar sits above the list and
 * above the board and says what is on and what that costs them.
 *
 * In the flow, not floating. The badge it replaces (common/_counter.erb) was
 * position:absolute in the bottom-right corner, where it sat on top of the
 * last card. This pushes content down by one row and covers nothing.
 * ============================================================ */
.filter-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 16px 12px;
  padding: 8px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  font-size: 13px;
}

/* `hidden` is set by the JS when no filter is on. Bootstrap's reboot sets
   display on some elements, so this makes sure the attribute wins. */
.filter-chips[hidden] {
  display: none;
}

.filter-chips__list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.filter-chips__count {
  color: var(--text-muted);
  margin-left: auto;
  white-space: nowrap;
}

.filter-chips__clear {
  padding: 0;
  font-size: 13px;
  text-decoration: none;
  white-space: nowrap;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 4px 2px 10px;
  background: var(--paper-bg);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  line-height: 1.6;
}

.filter-chip__text {
  white-space: nowrap;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A real <button>, so it is reachable by keyboard and announced. Styled down
   to look like the x it is. */
.filter-chip__remove {
  border: 0;
  background: transparent;
  color: var(--text-muted);
  font-size: 16px;
  line-height: 1;
  padding: 0 6px;
  cursor: pointer;
  border-radius: 999px;
}

.filter-chip__remove:hover,
.filter-chip__remove:focus-visible {
  background: var(--hover);
  color: var(--text);
}

@media (max-width: 575.98px) {
  .filter-chips {
    margin: 0 8px 8px;
  }
  /* On a phone the count would be squeezed onto the same line as the chips
     and truncated; give it its own row. */
  .filter-chips__count {
    margin-left: 0;
    width: 100%;
  }
}

/* ============================================================
 * Tracker #32 - Activity.
 *
 * Activity used to be a 400px card with an inline style attribute, holding
 * three entries at a time, opened from a dropdown menu. It is a page now, with
 * that panel kept as a five-entry preview. Both render the same row markup
 * (projects/_activity_entry.erb); `.ds-act--peek` is the only thing that
 * differs, and it only makes long comment text clamp.
 *
 * `.ds-paper` is used here for the first time. It was defined in Phase A under
 * a heading saying it was for a later phase and had never been put on any
 * markup - which is also what tracker #14 is about. If #14 lands on a different
 * surface, this page should follow it rather than the other way round.
 * ============================================================ */

.ds-act {
  overflow: hidden;
}

/* ---------- one entry ---------- */
.ds-act__row {
  display: flex;
  gap: 12px;
  padding: 12px 16px;
  border-top: 1px solid var(--border);
}
.ds-act__row:first-of-type { border-top: 0; }
.ds-act__row:hover { background: var(--hover); }

.ds-act__avatar {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
}

.ds-act__body { min-width: 0; flex: 1; }
.ds-act__line { margin: 0; }
.ds-act__title { color: var(--text-muted); }

/* The comment as it was written. `pre-wrap` because comments keep their line
   breaks, the same as common/_comment.erb renders them. */
.ds-act__quote {
  margin: 6px 0 0;
  padding: 8px 10px;
  background: var(--surface-soft);
  border-left: 3px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 13px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.ds-act__change {
  margin-top: 4px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  font-size: 13px;
}
/* Struck through rather than merely grey: at a glance the eye should find the
   value that is true now, not read both and work it out. */
.ds-act__was { color: var(--text-muted); text-decoration: line-through; }
.ds-act__arrow { color: var(--text-muted); }

.ds-act__meta {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 2px;
}

/* ---------- filters ---------- */
.ds-act__filters { padding: 14px 16px; margin-bottom: 16px; }
.ds-act__filters-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-end;
}
.ds-act__field { display: flex; flex-direction: column; gap: 4px; }
.ds-act__field .form-select,
.ds-act__field .form-control { min-width: 170px; }

/* Segmented control. The items are links, not buttons - see the note in
   projects/activity.erb - so everything a button gives for free (the reset,
   the pointer, not being underlined) has to be said here. */
.ds-seg {
  display: flex;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--paper-bg);
}
.ds-seg__btn {
  font-size: 12.5px;
  padding: 6px 10px;
  border: 0;
  border-right: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
}
.ds-seg__btn:last-child { border-right: 0; }
.ds-seg__btn:hover { background: var(--hover); color: var(--text); text-decoration: none; }
.ds-seg__btn.is-on {
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-weight: 600;
}

.ds-act__count { margin: 0 2px 10px; }

/* ---------- paging ---------- */
.ds-act__paging {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  justify-content: center;
  padding: 14px;
  border-top: 1px solid var(--border);
}
.ds-act__page {
  min-width: 32px;
  padding: 5px 9px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  background: var(--paper-bg);
  color: var(--text);
  text-align: center;
  text-decoration: none;
}
.ds-act__page:hover { background: var(--hover); text-decoration: none; }
.ds-act__page.is-on {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
}
.ds-act__page.is-off { opacity: 0.45; }

/* ---------- the preview panel ---------- */
.ds-act__panel {
  position: absolute;
  right: 12px;
  z-index: 1080;
  width: min(400px, calc(100vw - 24px));
}
/* An opaque surface, because this floats over the page. The theme's soft
   backgrounds are 7.5% opaque, which is the trap that made the #29 alerts
   see-through on staging. */
.ds-act__panel-inner {
  background: var(--paper-bg);
  box-shadow: var(--paper-shadow);
  border-radius: var(--radius-lg);
  max-height: 70vh;
  overflow-y: auto;
}

.ds-act__peek-head {
  font-weight: 600;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  background: var(--paper-bg);
  z-index: 1;
}
.ds-act__peek-foot {
  padding: 10px 14px;
  border-top: 1px solid var(--border);
  text-align: center;
  position: sticky;
  bottom: 0;
  background: var(--paper-bg);
}
.ds-act__peek-empty { padding: 20px 14px; margin: 0; color: var(--text-muted); }

.ds-act--peek .ds-act__row { padding: 10px 14px; }
/* Two lines of a comment is enough to recognise it in a preview; the page
   shows the whole thing. */
.ds-act--peek .ds-act__quote {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

@media (max-width: 575.98px) {
  .ds-act__filters-row { gap: 10px; }
  .ds-act__field { width: 100%; }
  .ds-act__field .form-select,
  .ds-act__field .form-control { min-width: 0; width: 100%; }
  .ds-seg { flex-wrap: wrap; }
}

/* ============================================================
 * Tracker #14 - the list page on the paper surface.
 *
 * The canonical Paper component in app-design-templates is 820px wide and
 * centred, described as "a white sheet you write on". That is a reading
 * surface; this page is a thirteen-column table. So what is borrowed is the
 * SURFACE - white, soft shadow, rounded corners - and not the width.
 * The Activity page (#32) uses it the same way.
 * ============================================================ */

.ds-page--full { max-width: none; }

/* The 10px at the sides is not the whole horizontal inset: `.dataTables_wrapper`
   carries another 12px, so the content sits 22px in from the sheet's edge. The
   wrapper has to keep carrying its share, because DataTables' `dom` string wraps
   everything in Bootstrap `.row`/`.col-12` pairs whose -12px gutter margin and
   +12px column padding cancel out against exactly that value. Move the whole
   inset onto this element and the controls hang 12px outside the sheet. */
.ds-table-paper { padding: 30px 10px; }

/* Chips sit above the sheet, so they line up with its edges rather than with
   the page container's own padding. */
.ds-page .filter-chips { margin-left: 0; margin-right: 0; }

/* The table has no outer border any more (the sheet is the edge), so the
   header rule is what separates the headings from the rows. */
.ds-table-paper #issues thead th { border-bottom: 1px solid var(--border-strong); }

@media (max-width: 640px) {
  /* Full-bleed on a phone: a shadow and 8px of cream on each side is a waste
     of a narrow screen, and the table is already scrolling sideways in there. */
  .ds-page--full { padding: 12px 0; }
  /* Same vertical breathing room as the desktop sheet; no horizontal padding,
     because the sheet is edge to edge here and the wrapper's 12px is enough. */
  .ds-table-paper { border-radius: 0; padding: 30px 0; }
  .ds-page .filter-chips { margin-left: 8px; margin-right: 8px; }
}
