/* cards.css — part 5 of 6 of the app stylesheet.
   Holds: card rows and pills, small buttons, tags, the card-detail modal, comment
   history, create-card, toasts.

   ORDER MATTERS. These files were cut out of a single app.css at line boundaries with the
   sequence preserved, so concatenating them in the order index.html links them reproduces the
   original byte for byte — which is what makes the cascade provably unchanged. Equal-specificity
   rules still resolve by source order, so reordering the <link> tags can change rendering.

   The names describe what ended up in each part, not a design: the original file interleaved
   the top bar, layout and per-feature rules, and moving rules to where they belong is a separate
   pass that this split exists to make possible. Palette is in theme.css, linked first. */
    /* The card itself is `.ui-card--raised` (7.488.0); this is only the space between cards on the board. */
    .card { margin-bottom: 16px; }
    .card .top { display: flex; justify-content: space-between; gap: 12px; }
    /* A flex row (7.488.0), so the rename button stays beside the title when the title wraps: at 390 it fell onto a
       line of its own. The title shrinks and wraps inside its own box; the button and an environment badge do not.
       The 4px gap is the space the inline layout put between them. */
    .card .title { font-weight: 600; display: flex; align-items: baseline; gap: 4px; }
    .card .title > .title-link { min-width: 0; }
    .card .title > :not(.title-link) { flex: none; }
    /* The trail is `.ui-crumbs` (7.488.0); this is only its space over the board card's title. */
    .crumbs { margin-bottom: 3px; }
    /* The crumb's menu is a `.ui-menu` (7.423.0); this is only where it sits and what it sits above. */
    .crumb-menu { position: absolute; z-index: var(--z-crumb); }
    /* The card's Description and Comment history are `.ui-disclosure--compact` over a `.ui-textbox` (7.492.0); these are
       only where each sits on the card and how tall its box may grow before it scrolls. */
    .card-desc-wrap { margin: 6px 0 10px; --ui-textbox-max: 220px; }
    .card-comments-wrap { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 10px; --ui-textbox-max: 220px; }
    .title-link.copyable { cursor: pointer; border-radius: 4px; }
    .title-link.copyable:hover { text-decoration: underline; text-decoration-style: dotted; }
    .title-link.copied { color: var(--accent); }
    .copied-flag { font-size: 11px; font-weight: 600; color: var(--accent); margin-left: 8px;
      vertical-align: middle; }
    .meta { color: var(--muted); font-size: 13px; margin-top: 2px; }
    .pill {
      font-size: 11px; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--line);
      color: var(--muted); margin-left: 6px;
      /* A pill is an atom: inline-block + nowrap keeps a two-word label ("In Progress") from
         breaking across lines, which splits the border/background down the middle. */
      display: inline-block; white-space: nowrap;
    }
    /* The Detections screen's client link. The board's client, "you" and state pills are `.ui-pill` (7.479.0). */
    .pill.cust { color: var(--accent-text); border-color: var(--accent-text); }
    .hours-now { font-variant-numeric: tabular-nums; color: var(--muted); font-size: 13px; }
    /* The app's original default button (3.20.1), at zero specificity since 7.457.0. It still applies to every button
       wherever nothing else sets a property, which is what the classless ones and many classed ones rely on (weight,
       radius, padding). At (0,0,1) for the rest and (0,1,1) for `:hover`, it used to outrank a one-class rule's
       background on hover, so any button whose own `:hover` set only a color or border showed this accent-press fill:
       nine of them, the header's bell and gear among them, at 1.00-1.32:1 in the light theme. */
    :where(button) {
      background: var(--accent); color: white; border: 0; border-radius: 8px;
      padding: 10px 20px; font: inherit; font-weight: 600; cursor: pointer;
    }
    :where(button:hover) { background: var(--accent-press); }
    :where(button:disabled) { opacity: .5; cursor: default; }
    .result {
      margin-top: 10px; padding: 10px 12px; border-left: 3px solid var(--ok);
      background: var(--panel-2); border-radius: 6px; font-size: 14px; display: none;
    }
    .result.show { display: block; }
    .result .lbl { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }
    .empty, .err { color: var(--muted); padding: 8px 0; }
    .err { color: var(--err); }
    /* The scope pills' `button.scope` rule went in 7.473.0. It had not drawn a pill since the rail arrived: every
       property it set lost to the rail's own item rule. The rail's buttons are `.ui-nav__item`, and `.scope` is the
       hook the router and the gating sweeps find them by. */
    /* small secondary buttons (save tags / mark active) */
    .btn-sm {
      display: inline-flex; align-items: center; justify-content: center;
      /* The SAME gap `.ui-btn` has had since 7.70.0, and its absence here is the whole bug. A flex
         item's trailing whitespace is trimmed, so `Label <span>79</span>` — which has a space in the
         markup and reads correctly in review — rendered as `All79` on the audits filter bar. It took
         until 7.105.0 to surface because that bar is the first `.btn-sm` in the app with two
         children: measured across every screen, 46 render and exactly 5 have more than one child,
         and all 5 are the jammed ones. A gap on a single-item flex container does nothing, so this
         reaches precisely the buttons that have the defect and no others. */
      gap: var(--ui-space-2);
      padding: 7px 13px; font-size: 13px; font-weight: 600; border-radius: 8px;
      background: transparent; color: var(--accent-text); border: 1px solid var(--accent-text);
      text-decoration: none; cursor: pointer; box-sizing: border-box;
    }
    /* --ui-wash, not an rgba() literal (7.70.0). These five hover/press tints were
       `rgba(90,45,138,.10/.16/.18/.28)` — the DEFAULT palette's accent, frozen — so a branded instance
       whose --accent is a blue still hovered that plum, and over the dark --panel a 10% plum is nearly
       nothing. Both existing guards missed it: the hardcoded-color sweep only matches `#rrggbb`, and
       the frozen-tint sweep only matches the four rgba() values the STATUS colors used to have.
       `test_no_tint_freezes_a_copy_of_a_theme_color` is the one that covers this class now. */
    .btn-sm:hover { background: var(--ui-wash); }
    /* The default's disabled cursor no longer outranks `.btn-sm`'s pointer (7.457.0), so it is stated here. */
    .btn-sm:disabled { opacity: .5; cursor: default; }
    .btn-sm.primary { background: var(--accent); color: #fff; }
    .btn-sm.primary:hover { filter: brightness(1.08); background: var(--accent); }
    /* card head-right + active pill */
    .headl { min-width: 0; }
    .head-right { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; white-space: nowrap; }
    .card-menu { position: relative; }
    /* The card's ⋮ menu is a `.ui-menu` (7.423.0) and its button a `.ui-icon-btn` (7.488.0); this is only where the
       menu opens and what it sits above: 6px under the 36px button. */
    .card-menu > .ui-menu { position: absolute; right: 0; top: 42px; z-index: var(--z-menu); }
    /* The confirm state of the card's Complete: a warning tone, so it reads from --warn rather than three
       separately-chosen ambers that no longer match each other or the theme. Keyed on the id and `data-armed`
       (7.428.0), the attribute the card menu's Complete arms with too. `.ui-btn` has no armed state, and this
       one stays the card's: the id outranks the component's hover, so the tint holds under the pointer as it
       did before. */
    #cd-complete[data-armed] {
      color: var(--text);
      border-color: color-mix(in srgb, var(--warn) 55%, transparent);
      background: color-mix(in srgb, var(--warn) 14%, transparent);
    }
    /* editable tags */
    /* The tags editor moved into the shared details grid's Tags cell in 6.146.0, so it is laid out by the
       grid rather than by its own row. The cell keeps the input, the Save button and the status together. */
    .tags-cell { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
    .tags-cell .tags { min-width: 180px; flex: 1 1 180px; }
    input.tags {
      flex: 1; min-width: 160px; font: inherit;
      background: var(--panel-2); border: 1px solid var(--line); color: var(--text);
      border-radius: 8px; padding: 8px 10px;
    }
    input.tags:focus { outline: none; border-color: var(--accent); }

    /* comment-history modal */
    .overlay {
      position: fixed; inset: 0; background: rgba(0,0,0,0.45);
      display: flex; align-items: center; justify-content: center; z-index: var(--z-overlay); padding: 20px;
    }
    .overlay[hidden] { display: none; }
    .modal {
      background: var(--panel); border: 1px solid var(--line); border-radius: 14px;
      box-shadow: var(--shadow); width: min(680px, 100%); max-height: 80vh;
      display: flex; flex-direction: column; overflow: hidden;
    }
    /* Header text column: breadcrumb over title. min-width lets long titles ellipsize instead of
       pushing the close button out of the header. */
    .cd-head-text { min-width: 0; flex: 1; }
    /* The rename button is `ui-btn ui-btn--ghost ui-btn--sm ui-btn--icon` (7.488.0): 24x24, the WCAG 2.5.8 floor, reached
       with the box. `cd-rename` is its hook and carries no rule. */
    .cd-title-input { font-family: 'Quicksand', system-ui, sans-serif; font-size: 16px;
      font-weight: 600; color: var(--text); background: var(--panel-2);
      border: 1px solid var(--accent); border-radius: 7px; padding: 3px 7px; width: min(38ch, 60vw); }
    /* Same as input.hours: the ring was removed with no replacement, and this field looks like
       static text until it has focus — the state being hidden was the only thing marking it editable. */
    .cd-title-input:focus { outline: none; border-color: var(--accent); }
    /* In the card detail the field takes the dialog title's type (7.444.0), so renaming does not change its size;
       on a board card it keeps the rule above. */
    #cd-title .cd-title-input { font-family: inherit; font-size: inherit; font-weight: inherit; }
    .cd-crumbs { margin-bottom: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .cmt-text { line-height: 1.55; }
    /* Slice 2: added-environment board sections */
    .env-board { margin-top: 22px; padding-top: 14px; border-top: 2px solid var(--line); }
    .env-board-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
    .env-board-name { font-size: 15px; font-weight: 600; }
    .env-board-ro { font-size: 11px; color: var(--muted); border: 1px solid var(--line); border-radius: 10px; padding: 1px 8px; }
    /* The Completed view's per-environment tally (7.16.1) — same quiet weight as the native summary. */
    .env-board-count { font-size: 12px; color: var(--muted); margin-left: auto; }
    .env-board-err { font-size: 13px; color: var(--err); }
    .tnode.ro { cursor: default; opacity: 0.92; }
    .tnode.ro:hover { background: transparent; }
    /* No fill of its own (7.494.0): the textarea default's `--panel-2`, which the board's Work notes box already has,
       so a card has one textarea fill. It was the white `--panel`. */
    .cd-note { min-height: 60px; padding: 6px 8px; border: 1px solid var(--line); border-radius: 6px;
      font: inherit; resize: vertical; color: var(--text); }
    .cd-note::placeholder { color: var(--muted); }
    /* Which contract the hours bill to, above the controls that submit them (7.136.0). It sits on its
       own row rather than inside .cd-log-controls because it is the one field somebody reads before
       typing, and wrapping it in with the buttons puts it wherever the line happens to break. */
    .cd-contract-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; }
    .cd-contract-lbl { color: var(--muted); }
    .cd-contract { flex: 1 1 260px; min-width: 0; max-width: 460px; padding: 6px 8px;
      border: 1px solid var(--line); border-radius: 6px; font: inherit;
      background: var(--panel); color: var(--text); }
    /* A card naming two contracts. Warn-tinted rather than error: nothing is broken yet, and it stops
       being true the moment somebody picks one. Takes the full row so it is not read as part of the
       control's label. */
    .cd-contract-warn { flex: 1 0 100%; color: var(--warn); font-size: 12px; }
    /* "Use for this card from now on" (7.217.0). Hidden until the choice differs from what the card
       would pick by itself; the [hidden] reassert is required because the label sets a display. */
    .cd-contract-remember { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); font-size: 12px; }
    .cd-contract-remember[hidden] { display: none; }
    /* On a board card the row sits between the notes and the controls, which have their own spacing. */
    .card .cd-contract-row { margin: 8px 0 4px; }
    .cmt-move-ui { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; padding: 10px; background: var(--panel-2, rgba(127,127,127,0.06)); border-radius: 8px; }
    .cmt-move-ui[hidden] { display: none; }   /* re-assert so `hidden` wins over the class display */
    .cmt-move-ui select { flex: 1; min-width: 180px; padding: 6px 8px; font-family: inherit; }
    /* The status line is `.ui-status` since 7.460.0; this only gives it the row to itself. */
    .cmt-move-ui .cmt-move-status { flex-basis: 100%; }
    .cmt-text h4 { margin: 10px 0 4px; color: var(--brand); font-size: 14px; }
    .cmt-text a, .cmt-text a:visited { color: var(--link); text-decoration: underline; text-underline-offset: 2px; }
    .cmt-text a:hover { text-decoration: none; }

    .form-row { margin: 14px 0; }
    .form-row > label { display: block; font-size: 13px; font-weight: 600; color: var(--muted); margin-bottom: 5px; }
    .form-row input[type="text"], .form-row select, .form-row textarea {
      width: 100%; box-sizing: border-box; padding: 9px 11px; border-radius: 8px; font-size: 14px;
      background: var(--panel-2); border: 1px solid var(--line); color: var(--text); font-family: inherit;
    }
    .form-row textarea { resize: vertical; min-height: 68px; }
    .form-row input[type="text"]:focus, .form-row select:focus, .form-row textarea:focus { outline: none; border-color: var(--accent); }
    .form-grid { display: flex; gap: 12px; }
    .form-grid .form-row { flex: 1; }
    .form-hint { font-size: 12px; color: var(--muted); margin-top: 4px; }
    .form-check { display: flex; align-items: center; gap: 8px; margin: 16px 0 4px; }
    .form-check input { width: 16px; height: 16px; accent-color: var(--accent); }
    .form-check label { font-size: 14px; color: var(--text); }
    .btn-sm[hidden] { display: none; }
    .nc-error { color: var(--err); font-size: 13px; margin-top: 12px; min-height: 1em; }

    /* The toasts are `.ui-toast` in components.css since 7.459.0. This keyframe stays here: the busy
       indicator below animates with it. */
    @keyframes toastin { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

    /* --- "Working on it" (7.12.0) --- */
    /* Bottom CENTER, not bottom-right: the toasts live on the right, and a spinner stacking under an
       integration's error would hide the thing worth reading. Center also reads as "the app is busy" rather
       than "here is another notification". */
    /* Named `.busy-bar`, not `.busy`. This is a page-level singleton, and a bare single-word class in a
       global stylesheet collides with any component that uses the same word as a modifier — which is
       exactly what happened: the calendar tags an event with its show-as status, and every "busy" event
       was pulled out of its row and pinned down here by these rules. Siblings below already read
       `busy-msg` / `busy-wheel`; this one was the odd one out.

       Centered with `margin-inline: auto` between a left/right 0, NOT with `translateX(-50%)`. The
       shared `toastin` keyframe ends on `transform: none`, so a centering transform is silently wiped
       the moment the entry animation completes and the popover sits off to one side. Measured in a
       browser: it landed left of center with every string test still passing. */
    .busy-bar { position: fixed; left: 0; right: 0; bottom: 18px;
      width: fit-content; margin-inline: auto;
      display: flex; align-items: center; gap: 10px; z-index: var(--z-overlay);
      background: var(--panel); border: 1px solid var(--line); border-radius: 999px;
      padding: 9px 16px 9px 13px; box-shadow: 0 8px 24px rgba(0,0,0,0.28);
      font-size: 13px; color: var(--text); animation: toastin .18s ease-out;
      /* Never intercepts a click: it sits over the screen someone may be trying to use, and a wait
         indicator that swallows a button press would be worse than no indicator. */
      pointer-events: none; }
    .busy-bar[hidden] { display: none; }    /* the recurring §10 gotcha: `hidden` needs the reassert */
    .busy-msg { white-space: nowrap; }
    .busy-wheel { width: 14px; height: 14px; flex: none; border-radius: 50%;
      border: 2px solid color-mix(in srgb, var(--text) 22%, transparent);
      border-top-color: var(--brand-bright); animation: busyspin .7s linear infinite; }
    @keyframes busyspin { to { transform: rotate(360deg); } }
    /* A spinning wheel is exactly what "reduce motion" is about, so it becomes a pulse instead of
       vanishing: the message alone with a static ring reads as a stalled indicator. */
    @media (prefers-reduced-motion: reduce) {
      .busy-wheel { animation: busypulse 1.4s ease-in-out infinite; border-top-color: var(--brand-bright); }
      .busy-bar { animation: none; }
      @keyframes busypulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
    }
    .update-banner { position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-top); display: flex; align-items: center; justify-content: center; gap: 12px; padding: 8px 14px; background: var(--brand); color: #fff; font-size: 14px; box-shadow: 0 2px 10px rgba(0,0,0,0.25); }
    .update-banner[hidden] { display: none; }   /* class rule sets display:flex, which beats the UA [hidden] rule — re-assert it here so `hidden` actually hides */
    .update-reload { background: #fff; color: var(--brand-deep); border: none; border-radius: 6px; padding: 5px 12px; font-weight: 650; cursor: pointer; }
    .update-reload:hover { opacity: .92; }
    .update-dismiss { position: absolute; right: 12px; background: none; border: none; color: #fff; font-size: 20px; line-height: 1; cursor: pointer; opacity: .8; }
    .update-dismiss:hover { opacity: 1; }
/* ---- Management priority badge (7.5.0) ---------------------------------------------------------
   One rule set, four surfaces: the board card, the card modal, the planning/kanban card and the Gantt
   row. The band class comes from the server (`card_priority.band`); nothing here knows where 1-3 ends.

   Every placement below is a FLEX ITEM, never `position: absolute`. That is the lesson of 6.106.1: an
   absolutely positioned child dropped into a flex row that already right-aligns something landed on top
   of it, 12-13px of overlap on every column, and the string-matching frontend tests could not see it. */
.prio-badge {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
  min-width: 22px; height: 22px; padding: 0 6px;
  border-radius: 999px; border: 2px solid var(--prio-ring);
  /* The edge in the light theme, where the white border sits on a white card. Kept very light so the
     badge reads as a ring rather than a shadowed chip. */
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .18);
  font-size: 12px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;   /* a 10 must not be wider than a 9 in a column of badges */
  white-space: nowrap;
}
.prio-band-none { background: var(--prio-none); color: var(--prio-none-ink); }
.prio-band-low { background: var(--prio-low); color: var(--prio-low-ink); }
.prio-band-medium { background: var(--prio-medium); color: var(--prio-medium-ink); }
.prio-band-high { background: var(--prio-high); color: var(--prio-high-ink); }

/* Board card: `.head-right` is already the card's top-right column, so the badge is simply its first
   item. On narrow screens layout.css turns that column into a wrapping row and the badge travels with it. */
.head-right .prio-badge { align-self: flex-end; }

/* Card modal: its own slot in the header, before the close button. */
#carddetail-overlay .cd-prio { display: flex; align-items: center; flex: none; }

/* The Priority row inside the details grid — the badge sits beside the control, so the color is visible
   while you are choosing the number. */
.cd-prio-cell { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

