/* ==========================================================================
 * dtmcp-app.css — DEVONthink MCP Apps shared design system
 *
 * Inlined into each app's index.html at +load time by
 * MCPSharedAppAssets.  Apps must use these tokens and components instead
 * of redefining their own chrome.  No per-app overrides.
 *
 * Layout grid (apps pick which slots they use):
 *
 *   .dtm-app
 *   ├── .dtm-toolbar  (toolbar with .dtm-toolbar__params + __actions)
 *   ├── .dtm-meta     (status caption strip)
 *   └── .dtm-body     (3-column grid: aux | main | inspector)
 *       ├── .dtm-aux         (optional left list pane)
 *       ├── .dtm-main        (canvas / table / cards)
 *       └── .dtm-inspector   (optional right inspector)
 *
 * Canonical container-query breakpoints (CSS `@container` cannot read
 * custom properties, so the literal values below must be used inline —
 * keep them in lockstep with this list).  Stick to these three tiers
 * unless the threshold is an intra-component layout switch (e.g. a
 * panel-local donut/legend collapse) that genuinely doesn't align with
 * the page-stack thresholds:
 *
 *   460px   — toolbar / chrome ultra-narrow (chat sidebar).  Hides the
 *             toolbar title; collapses statgrids to a single column.
 *   720px   — page stack.  2-pane bodies (`.dtm-body`) collapse to a
 *             single column; KPI strips step from 4 → 2 columns;
 *             3-pane / multi-row bodies use this when their content
 *             still fits stacked.
 *   940px   — wide stack.  3-pane bodies (catalyst, replicant-atlas)
 *             stack at this higher threshold because their middle pane
 *             needs ~600 px to remain useful.
 *
 * ========================================================================== */

:root
{
    /* --- Surfaces ----------------------------------------------------- */
    --dtm-bg:                 #0e1116;
    --dtm-bg-rgb:             14, 17, 22;
    --dtm-surface:            #11161d;
    --dtm-surface-2:          #161b24;
    --dtm-surface-hover:      #1a2030;
    --dtm-surface-selected:   rgba(31, 111, 235, 0.18);
    --dtm-border:             #1f2630;
    --dtm-border-strong:      #2d3743;

    /* --- Text -------------------------------------------------------- */
    --dtm-text:               #e6edf3;
    --dtm-text-muted:         #8b949e;
    --dtm-text-muted-rgb:     139, 148, 158;
    --dtm-text-faint:         #6b7480;
    --dtm-text-on-accent:     #ffffff;

    /* --- Brand ------------------------------------------------------- */
    --dtm-accent:             #1f6feb;
    --dtm-accent-rgb:         31, 111, 235;
    --dtm-accent-hover:       #388bfd;
    --dtm-accent-soft:        rgba(31, 111, 235, 0.14);
    --dtm-accent-fg:          #8ab4ff;
    --dtm-accent-fg-rgb:      138, 180, 255;
    --dtm-accent-fg-bright:   #c9e0ff;

    /* --- Status colours --------------------------------------------- */
    --dtm-success:            #2ea043;
    --dtm-success-soft:       rgba(46, 160, 67, 0.06);
    --dtm-success-soft-border:rgba(46, 160, 67, 0.25);
    --dtm-success-text:       #d6efd9;
    --dtm-warning:            #f0883e;
    --dtm-warning-rgb:        240, 136, 62;
    --dtm-warning-soft:       rgba(240, 136, 62, 0.10);
    --dtm-warning-border:     rgba(240, 136, 62, 0.45);
    --dtm-error:              #fca5a5;
    --dtm-error-bg:           rgba(252, 165, 165, 0.12);

    /* --- Destructive-action palette (deeper, more saturated reds for
     * "you are about to delete this" affordances — Duplicate Doctor,
     * future trash/merge confirmations).  Distinct from --dtm-error
     * which is a soft pink used for error-state messaging. */
    --dtm-danger:                  #b03b2e;
    --dtm-danger-soft:             rgba(176, 59, 46, 0.08);
    --dtm-danger-border:           rgba(176, 59, 46, 0.45);
    --dtm-danger-strong-border:    rgba(176, 59, 46, 0.55);

    /* --- Deep / muted variants of accent and warning, sized for use as
     * row borders and chip borders where the saturated accent/warning
     * would visually dominate.  Currently used by Duplicate Doctor. */
    --dtm-accent-deep-border:      rgba(43, 108, 176, 0.35);
    --dtm-accent-deep-strong-border:rgba(43, 108, 176, 0.45);
    --dtm-warning-deep-soft:       rgba(180, 106, 42, 0.08);
    --dtm-warning-deep-border:     rgba(180, 106, 42, 0.35);

    /* --- Accent-fg alpha variants (cluster around #8ab4ff) ----------
     * Used as flash backgrounds, edge tints, focus glows, and hover
     * fills throughout the apps.  Spelled out as tokens so per-app CSS
     * never has to inline the literal triplet. */
    --dtm-accent-fg-soft:     rgba(138, 180, 255, 0.18);
    --dtm-accent-fg-edge:     rgba(138, 180, 255, 0.25);
    --dtm-accent-fg-border:   rgba(138, 180, 255, 0.35);
    --dtm-accent-fg-glow:     rgba(138, 180, 255, 0.45);
    --dtm-accent-fg-hover:    rgba(138, 180, 255, 0.22);

    /* --- Overlays / tooltips / spinners ------------------------------ */
    --dtm-overlay-bg:         rgba(14, 17, 22, 0.55);
    --dtm-overlay-bg-strong:  rgba(14, 17, 22, 0.78);
    --dtm-modal-backdrop:     rgba(0, 0, 0, 0.5);
    --dtm-tooltip-bg:         rgba(14, 17, 22, 0.94);
    --dtm-spinner-track:      rgba(96, 165, 250, 0.25);

    /* --- Shadows / decorative blacks --------------------------------- */
    --dtm-shadow-soft:        rgba(0, 0, 0, 0.35);
    --dtm-shadow-medium:      rgba(0, 0, 0, 0.50);
    --dtm-shadow-card:        rgba(0, 0, 0, 0.55);
    --dtm-shadow-strong:      rgba(0, 0, 0, 0.60);
    --dtm-thumb-frame-light:  rgba(255, 255, 255, 0.18);
    --dtm-overlay-row-hover:  rgba(255, 255, 255, 0.08);
    --dtm-thumb-bg:           #000000;

    /* --- Anomaly / row highlight ------------------------------------ */
    --dtm-highlight:          #ffd166;
    --dtm-highlight-glow:     rgba(255, 209, 102, 0.85);

    /* --- Hop palette (root → distant)
     * Used by Catalyst, Knowledge Atlas, and Sunburst legends so all
     * three speak the same colour language.  Index 0 is the centre /
     * root; higher indices are further away. */
    --dtm-hop-0:              #f0b429;  /* root / centre — saturated yellow */
    --dtm-hop-1:              #5b9eff;  /* 1st hop      — blue              */
    --dtm-hop-2:              #4cc38a;  /* 2nd hop      — green             */
    --dtm-hop-3:              #b888ff;  /* 3rd hop      — purple            */
    --dtm-hop-4:              #ff8aa8;  /* 4th hop      — pink              */

    /* --- Spacing ----------------------------------------------------- */
    --dtm-space-1:            4px;
    --dtm-space-2:            8px;
    --dtm-space-3:            12px;
    --dtm-space-4:            16px;
    --dtm-space-5:            24px;
    --dtm-space-6:            32px;

    /* --- Radius ------------------------------------------------------ */
    --dtm-radius-sm:          4px;
    --dtm-radius-md:          6px;
    --dtm-radius-pill:        999px;

    /* --- Type scale -------------------------------------------------- */
    --dtm-font-ui:            -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    --dtm-font-mono:          ui-monospace, SFMono-Regular, Menlo, monospace;
    --dtm-text-xs:            11px;
    --dtm-text-sm:            12px;
    --dtm-text-md:            13px;
    --dtm-text-lg:            15px;
    --dtm-text-xl:            18px;
    --dtm-text-2xl:           24px;

    /* --- Layout ----------------------------------------------------- */
    --dtm-toolbar-height:     56px;
    --dtm-meta-height:        28px;
    --dtm-aux-width:          260px;
    --dtm-inspector-width:    320px;

    /* --- Focus ring ------------------------------------------------- *
     * Inset so the ring paints inside the focused element's bounds and
     * is never clipped by a tight parent or overlap a neighbour's
     * border (a non-inset ring leaks 2px across the seam between, say,
     * a toolbar and the scrollable list it's adjacent to, showing as a
     * stray top-edge stripe). */
    --dtm-focus-ring:         inset 0 0 0 2px rgba(138, 180, 255, 0.55);
}

/* ==========================================================================
 * Base
 * ========================================================================== */

html, body
{
    margin: 0;
    padding: 0;
    height: 100%;
    background: var(--dtm-bg);
    color:      var(--dtm-text);
    font:       var(--dtm-text-md)/1.45 var(--dtm-font-ui);
}

/* Iframe-aware layout: `html` is the query container so descendants
 * (`body.dtm-app`, `.dtm-statgrid`, `.dtm-body`, per-app grids) can
 * use `@container (max-width: …)` to respond to the actual iframe
 * width.  Hosts that embed us inline-chat give a narrow iframe
 * regardless of viewport width, so media queries against the viewport
 * are unreliable.
 *
 * `container-type` must live on `html`, NOT on `body.dtm-app`, because
 * container queries only match descendants of the container — a rule
 * like `@container { body.dtm-app { … } }` against a body-typed
 * container would never apply to body itself, which is the bug that
 * silently broke the narrow body-overflow override. */
html
{
    container-type: inline-size;
}

body.dtm-app
{
    display:        flex;
    flex-direction: column;
    /* `100vh` makes the layout fill the iframe in the common case.
     * The `400px` floor protects us when the host ignores
     * `ui/notifications/size-changed` and gives us a tiny frame —
     * content scrolls internally instead of getting cropped. */
    min-height:     max(400px, 100vh);
    overflow:       hidden;
}

*, *::before, *::after { box-sizing: border-box; }

/* Focus ring (keyboard-only). */
:focus-visible { outline: none; box-shadow: var(--dtm-focus-ring); }

/* vis-network injects a wrapper div + a focusable <canvas> whenever
 * keyboard navigation is enabled.  The default focus ring lands on the
 * entire graph area as a thick accent box — visually heavy and the
 * built-in arrow-key panning is rarely useful for our apps anyway, so
 * suppress the ring on both the wrapper and the canvas.  Apps that want
 * their own keyboard handling install it via DTMCP.hotkeys. */
.vis-network,
.vis-network:focus,
.vis-network:focus-visible,
.vis-network canvas,
.vis-network canvas:focus,
.vis-network canvas:focus-visible
{
    outline:     none;
    box-shadow:  none;
}

/* ==========================================================================
 * Scrollbars
 * ==========================================================================
 * macOS WKWebView defaults to overlay scrollbars that only fade in
 * during interaction — when a side panel or doc list overflows, users
 * can't tell more content exists.  Style a thin always-visible
 * scrollbar across every `body.dtm-app` descendant so the affordance
 * is consistent everywhere (find-similar lists, panel bodies, doc
 * lists, table viewports …), AND on `body.dtm-app` itself (no-space
 * selector) so the body's own scrollbar that appears at narrow widths
 * is the same thin styled affordance instead of the wide macOS
 * standard scrollbar.
 *
 * Thumb colour is a translucent white that picks up the macOS overlay
 * scrollbar look — readable against any dark surface (the dark slate
 * `--dtm-border-strong` we had before was nearly invisible against
 * `--dtm-bg`).  `background-clip: padding-box` + transparent border
 * gives the thumb breathing room without coupling it to any one
 * surface colour. */
body.dtm-app::-webkit-scrollbar,
body.dtm-app ::-webkit-scrollbar          { width: 10px; height: 10px; }
body.dtm-app::-webkit-scrollbar-track,
body.dtm-app ::-webkit-scrollbar-track    { background: transparent; }
body.dtm-app::-webkit-scrollbar-thumb,
body.dtm-app ::-webkit-scrollbar-thumb
{
    background:      rgba(255, 255, 255, 0.22);
    border:          2px solid transparent;
    background-clip: padding-box;
    border-radius:   999px;
    min-height:      24px;
}
body.dtm-app::-webkit-scrollbar-thumb:hover,
body.dtm-app ::-webkit-scrollbar-thumb:hover    { background-color: rgba(255, 255, 255, 0.38); }
body.dtm-app::-webkit-scrollbar-corner,
body.dtm-app ::-webkit-scrollbar-corner         { background: transparent; }

/* ==========================================================================
 * Toolbar
 * ========================================================================== */

.dtm-toolbar
{
    flex:         0 0 auto;
    min-height:   var(--dtm-toolbar-height);
    padding:      var(--dtm-space-3) var(--dtm-space-4);
    border-bottom: 1px solid var(--dtm-border);
    background:   var(--dtm-surface);
    display:      flex;
    align-items:  center;
    gap:          var(--dtm-space-3);
    flex-wrap:    wrap;
}

.dtm-toolbar__title
{
    font-size:       var(--dtm-text-xs);
    text-transform:  uppercase;
    letter-spacing:  0.06em;
    color:           var(--dtm-accent-fg);
    font-weight:     600;
    margin-right:    var(--dtm-space-2);
    flex:            0 0 auto;
}

.dtm-toolbar__params
{
    display:     flex;
    align-items: center;
    gap:         var(--dtm-space-3);
    flex:        1 1 auto;
    flex-wrap:   wrap;
}

.dtm-toolbar__actions
{
    display:     flex;
    align-items: center;
    gap:         var(--dtm-space-2);
    flex:        0 0 auto;
    margin-left: auto;
}

/* Narrow iframes (chat side panels, mobile) often can't fit
 * title + params + actions on one row, and `flex-wrap` then pushes
 * the actions onto their own line — leaving an awkward strip of
 * empty space.  The title is redundant in those contexts (the chat
 * already shows the tool name), so hide it instead. */
@container (max-width: 460px)
{
    .dtm-toolbar__title { display: none; }
    .dtm-toolbar
    {
        padding: var(--dtm-space-2) var(--dtm-space-3);
        gap:     var(--dtm-space-2);
    }
}

/* Two-row toolbar — opt-in modifier for apps with a rich filter strip
 * (chip-row scope selectors, multi-criteria filters, etc.) where the
 * default `flex-wrap` toolbar would produce an unpredictably tall bar
 * depending on chip count and viewport width.  `--two-row` swaps the
 * flex container for a 3-column / 2-row grid: title + actions stay on
 * the top row at fixed slots, and `.dtm-toolbar__params` occupies the
 * full second row.  The two rows are deterministic, so the bar always
 * reserves the same vertical space regardless of param content. */
.dtm-toolbar--two-row
{
    display:               grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-rows:    auto auto;
    column-gap:            var(--dtm-space-3);
    row-gap:               var(--dtm-space-2);
    align-items:           center;
    flex-wrap:             nowrap;
}
.dtm-toolbar--two-row .dtm-toolbar__title   { grid-column: 1; grid-row: 1; }
.dtm-toolbar--two-row .dtm-toolbar__actions { grid-column: 3; grid-row: 1; margin-left: 0; }
.dtm-toolbar--two-row .dtm-toolbar__params  { grid-column: 1 / -1; grid-row: 2; }
@container (max-width: 460px)
{
    .dtm-toolbar--two-row { grid-template-columns: minmax(0, 1fr) auto; }
    .dtm-toolbar--two-row .dtm-toolbar__actions { grid-column: 2; }
}

.dtm-field
{
    display:     inline-flex;
    align-items: center;
    gap:         var(--dtm-space-2);
}

.dtm-field__label
{
    font-size:       var(--dtm-text-xs);
    color:           var(--dtm-text-muted);
    text-transform:  uppercase;
    letter-spacing:  0.04em;
}

.dtm-input,
.dtm-select
{
    background:    var(--dtm-bg);
    border:        1px solid var(--dtm-border-strong);
    border-radius: var(--dtm-radius-sm);
    color:         var(--dtm-text);
    font:          inherit;
    padding:       var(--dtm-space-1) var(--dtm-space-2);
}
.dtm-input--num     { width: 64px; }
.dtm-input--search  { flex: 1 1 auto; min-width: 180px; }
.dtm-input::placeholder { color: var(--dtm-text-faint); }

/* ==========================================================================
 * Buttons
 * ========================================================================== */

.dtm-button
{
    background:    transparent;
    border:        1px solid var(--dtm-border-strong);
    border-radius: var(--dtm-radius-sm);
    color:         var(--dtm-text);
    font:          inherit;
    padding:       var(--dtm-space-1) var(--dtm-space-3);
    cursor:        pointer;
    user-select:   none;
    line-height:   1.45;
}
.dtm-button:hover:not(:disabled)  { background: var(--dtm-surface-hover); }
.dtm-button:disabled              { opacity: 0.45; cursor: not-allowed; }

.dtm-button--primary
{
    background:    var(--dtm-accent);
    border-color:  var(--dtm-accent);
    color:         var(--dtm-text-on-accent);
}
.dtm-button--primary:hover:not(:disabled)
{
    background:    var(--dtm-accent-hover);
    border-color:  var(--dtm-accent-hover);
}

.dtm-button--ghost
{
    border-color: transparent;
    color:        var(--dtm-text-muted);
}
.dtm-button--ghost:hover:not(:disabled)
{
    color:        var(--dtm-text);
    background:   var(--dtm-surface-hover);
}

.dtm-button--icon
{
    padding:    var(--dtm-space-1);
    min-width:  28px;
    height:     28px;
    display:    inline-flex;
    align-items: center;
    justify-content: center;
}

/* Compact text button — for dense action rows where the default
 * button feels heavy (Research Canvas card actions, eventual list-
 * row Open buttons). */
.dtm-button--sm
{
    padding:    var(--dtm-space-1) var(--dtm-space-2) calc(var(--dtm-space-1) + 1px);
    font-size:  var(--dtm-text-xs);
}

.dtm-button--danger
{
    background:    #b62324;
    border-color:  #b62324;
    color:         var(--dtm-text-on-accent);
}
.dtm-button--danger:hover:not(:disabled)
{
    background:    #d2393b;
    border-color:  #d2393b;
}

/* ==========================================================================
 * Segmented picker (database scope, etc.)
 * ========================================================================== */

.dtm-segmented
{
    display:     inline-flex;
    flex-wrap:   wrap;
    gap:         var(--dtm-space-1);
}

.dtm-segmented__item
{
    background:    transparent;
    border:        1px solid var(--dtm-border-strong);
    border-radius: var(--dtm-radius-pill);
    color:         var(--dtm-text);
    font:          inherit;
    padding:       3px 12px;
    cursor:        pointer;
    user-select:   none;
}
.dtm-segmented__item:hover:not(.is-active):not(:disabled) { background: var(--dtm-surface-hover); }
.dtm-segmented__item.is-active
{
    border-color: var(--dtm-accent-fg);
    color:        var(--dtm-accent-fg);
    background:   var(--dtm-accent-soft);
}

/* ==========================================================================
 * Meta strip (under the toolbar)
 * ========================================================================== */

.dtm-meta
{
    flex:           0 0 auto;
    min-height:     var(--dtm-meta-height);
    padding:        var(--dtm-space-2) var(--dtm-space-4);
    color:          var(--dtm-text-muted);
    font-size:      var(--dtm-text-sm);
    border-bottom:  1px solid var(--dtm-border);
    background:     var(--dtm-surface);
    display:        flex;
    align-items:    center;
    gap:            var(--dtm-space-2);
    flex-wrap:      wrap;
}
.dtm-meta strong       { color: var(--dtm-text); font-weight: 600; }
.dtm-meta .dtm-meta__warn  { color: var(--dtm-warning); }
.dtm-meta .dtm-meta__error { color: var(--dtm-error); }
.dtm-meta--placeholder { color: var(--dtm-text-faint); font-style: italic; }
/* `.dtm-meta`'s own `display:flex` outranks the UA `[hidden]` rule, so
 * apps that hide their meta strip in the healthy state need this. */
.dtm-meta[hidden]      { display: none; }
.dtm-meta__sep         { color: var(--dtm-text-faint); }

/* ==========================================================================
 * Body grid
 * ========================================================================== */

.dtm-body
{
    flex:    1 1 auto;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    min-height: 0;
}
.dtm-body--no-aux       { grid-template-columns: minmax(0, 1fr) auto; }
.dtm-body--no-inspector { grid-template-columns: auto minmax(0, 1fr); }
.dtm-body--main-only    { grid-template-columns: minmax(0, 1fr); }

.dtm-aux,
.dtm-main,
.dtm-inspector
{
    min-height: 0;
    overflow:   auto;
}

.dtm-aux
{
    width:        var(--dtm-aux-width);
    border-right: 1px solid var(--dtm-border);
    background:   var(--dtm-surface);
}

.dtm-main
{
    background: var(--dtm-bg);
    position:   relative;
}

.dtm-inspector
{
    width:        var(--dtm-inspector-width);
    border-left:  1px solid var(--dtm-border);
    background:   var(--dtm-surface);
    padding:      var(--dtm-space-4);
}

/* Narrow-iframe stacking: when the chat-side iframe is narrow there's
 * no space for three side-by-side columns, so stack aux → main →
 * inspector vertically.  Each pane keeps its own scroll context but
 * gets a bounded height so the main canvas still has room.  Borders
 * flip from vertical seams to horizontal ones.
 *
 * The body itself becomes the scroll container in narrow mode because
 * stacked content routinely exceeds 100vh on hosts that ignore
 * `size-changed` (our standalone AppHost, embedded DEVONthink window,
 * any chat client that doesn't grow the iframe).  `fitToContent` still
 * reports body.scrollHeight so hosts that *do* grow the iframe get
 * the full content height and the scrollbar stays idle. */
@container (max-width: 720px)
{
    body.dtm-app
    {
        overflow: auto;
    }
    .dtm-body,
    .dtm-body--no-aux,
    .dtm-body--no-inspector,
    .dtm-body--main-only
    {
        grid-template-columns: minmax(0, 1fr);
        grid-auto-rows:        auto;
    }
    .dtm-aux
    {
        width:         auto;
        max-height:    220px;
        border-right:  0;
        border-bottom: 1px solid var(--dtm-border);
    }
    .dtm-inspector
    {
        /* No max-height: the inspector now lives inside a scrollable
         * body so it can grow naturally (image previews, long detail
         * lists, tag chips, etc. all read better at full height than
         * inside a 320 px clipped frame). */
        width:        auto;
        border-left:  0;
        border-top:   1px solid var(--dtm-border);
    }
}

/* ==========================================================================
 * Inspector sections + statgrid
 * ========================================================================== */

.dtm-inspector__title
{
    font-size:    var(--dtm-text-lg);
    font-weight:  600;
    margin:       0 0 var(--dtm-space-3);
    color:        var(--dtm-text);
}

.dtm-inspector__section
{
    margin-top: var(--dtm-space-4);
}
.dtm-inspector__section:first-of-type { margin-top: var(--dtm-space-2); }

.dtm-inspector__section-header
{
    display:         flex;
    justify-content: space-between;
    align-items:     center;
    font-size:       var(--dtm-text-xs);
    text-transform:  uppercase;
    letter-spacing:  0.06em;
    color:           var(--dtm-text-muted);
    margin-bottom:   var(--dtm-space-2);
}

.dtm-inspector__section-header > a,
.dtm-inspector__section-header > button.dtm-button--ghost
{
    text-transform: none;
    letter-spacing: normal;
    font-size:      var(--dtm-text-xs);
    color:          var(--dtm-accent-fg);
    background:     transparent;
    border:         0;
    cursor:         pointer;
    padding:        0;
    text-decoration: none;
}
.dtm-inspector__section-header > a:hover,
.dtm-inspector__section-header > button.dtm-button--ghost:hover
{
    color: var(--dtm-accent-fg-bright);
}

.dtm-statgrid
{
    display:               grid;
    grid-template-columns: 1fr 1fr;
    gap:                   var(--dtm-space-2);
}

/* Four-column variant for apps with four KPI tiles (Recent Activity,
 * Duplicate Doctor, Replicant Atlas).  Steps 4 → 2 → 1 by container
 * width via the queries below — apps must not redefine
 * `grid-template-columns` per-instance. */
.dtm-statgrid--4
{
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

@container (max-width: 720px)
{
    .dtm-statgrid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@container (max-width: 460px)
{
    .dtm-statgrid,
    .dtm-statgrid--4 { grid-template-columns: minmax(0, 1fr); }
}

.dtm-statgrid__cell
{
    background:    var(--dtm-surface-2);
    border:        1px solid var(--dtm-border);
    border-radius: var(--dtm-radius-md);
    padding:       var(--dtm-space-3);
    text-align:    left;
}

.dtm-statgrid__value
{
    font-size:    var(--dtm-text-2xl);
    font-weight:  600;
    color:        var(--dtm-accent-fg);
    line-height:  1.1;
    font-variant-numeric: tabular-nums;
}

.dtm-statgrid__label
{
    font-size:       var(--dtm-text-xs);
    text-transform:  uppercase;
    letter-spacing:  0.06em;
    color:           var(--dtm-text-muted);
    margin-top:      var(--dtm-space-1);
}

.dtm-keyval
{
    display:               grid;
    grid-template-columns: 84px 1fr;
    gap:                   var(--dtm-space-1) var(--dtm-space-2);
}
.dtm-keyval__key
{
    font-size:       var(--dtm-text-xs);
    text-transform:  uppercase;
    letter-spacing:  0.04em;
    color:           var(--dtm-text-muted);
    align-self:      center;
}
.dtm-keyval__val
{
    color: var(--dtm-text);
    word-break: break-word;
}

/* ==========================================================================
 * Lists / tables (keyboard-navigable)
 * ========================================================================== */

.dtm-list
{
    list-style: none;
    margin:     0;
    padding:    0;
}

.dtm-list__row
{
    padding:       var(--dtm-space-3) var(--dtm-space-4);
    border-bottom: 1px solid var(--dtm-border);
    cursor:        pointer;
    user-select:   none;
}
.dtm-list__row:hover:not(.is-selected)  { background: var(--dtm-surface-hover); }
.dtm-list__row.is-selected               { background: var(--dtm-surface-selected); }
.dtm-list__row:focus-visible             { box-shadow: inset 0 0 0 2px var(--dtm-accent-fg); outline: none; }

.dtm-list__title
{
    color:        var(--dtm-text);
    font-weight:  500;
    word-break:   break-word;
    margin:       0 0 var(--dtm-space-1);
}
.dtm-list__sub
{
    font-size: var(--dtm-text-sm);
    color:     var(--dtm-text-muted);
    display:   flex;
    gap:       var(--dtm-space-2);
    align-items: center;
    flex-wrap: wrap;
}

/* Record timestamp — the string from `DTMCP.formatTimestamp(iso)`.
 * Convention: a record's date is rendered with this class as a
 * right-aligned trailing element in its row, kept separate from the
 * location / type meta (which stays left).  Pair the text with
 * `title={DTMCP.formatDateTime(iso)}` so the exact value is one hover
 * away.  Row-level alignment (grid cell / `margin-left:auto`) is the
 * caller's job; this class only standardises the appearance. */
.dtm-when
{
    flex:                 0 0 auto;
    font-size:            var(--dtm-text-xs);
    color:                var(--dtm-text-faint);
    font-variant-numeric: tabular-nums;
    white-space:          nowrap;
}

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

.dtm-table th, .dtm-table td
{
    text-align:    left;
    padding:       var(--dtm-space-2) var(--dtm-space-3);
    border-bottom: 1px solid var(--dtm-border);
    vertical-align: top;
}
.dtm-table th
{
    position:        sticky;
    top:             0;
    background:      var(--dtm-surface);
    z-index:         1;
    font-size:       var(--dtm-text-xs);
    text-transform:  uppercase;
    letter-spacing:  0.04em;
    color:           var(--dtm-text-muted);
    font-weight:     600;
    user-select:     none;
}
.dtm-table th[data-sort]              { cursor: pointer; }
.dtm-table th .dtm-sort-arrow         { display: inline-block; width: 10px; color: var(--dtm-text-faint); }
.dtm-table th.is-sorted               { color: var(--dtm-text); }
.dtm-table th.is-sorted .dtm-sort-arrow { color: var(--dtm-accent-fg); }

.dtm-table tbody tr               { cursor: pointer; }
.dtm-table tbody tr:hover td:not(.is-selected) { background: var(--dtm-surface-2); }
.dtm-table tbody tr.is-selected td             { background: var(--dtm-surface-selected); }
.dtm-table tbody tr:focus-visible              { box-shadow: inset 0 0 0 2px var(--dtm-accent-fg); outline: none; }
.dtm-table tbody tr.is-done td                 { color: var(--dtm-text-faint); }
.dtm-table td.is-num                            { font-variant-numeric: tabular-nums; }

/* ==========================================================================
 * Chips / badges
 * ========================================================================== */

.dtm-chip
{
    display:        inline-flex;
    align-items:    center;
    gap:            var(--dtm-space-1);
    background:     var(--dtm-accent-soft);
    border:         1px solid rgba(138, 180, 255, 0.35);
    color:          var(--dtm-accent-fg);
    border-radius:  var(--dtm-radius-pill);
    padding:        1px var(--dtm-space-2);
    font-size:      var(--dtm-text-xs);
    text-decoration: none;
    cursor:         default;
    user-select:    none;
}
.dtm-chip--clickable        { cursor: pointer; }
.dtm-chip--clickable:hover  { background: rgba(138, 180, 255, 0.22); color: var(--dtm-accent-fg-bright); }

.dtm-chip__count
{
    color:      var(--dtm-text-faint);
    font-variant-numeric: tabular-nums;
}

.dtm-chip--neutral
{
    background:    var(--dtm-surface-2);
    border-color:  var(--dtm-border-strong);
    color:         var(--dtm-text-muted);
}
.dtm-chip--warn
{
    background:    rgba(240, 136, 62, 0.10);
    border-color:  rgba(240, 136, 62, 0.40);
    color:         var(--dtm-warning);
}
.dtm-chip--success
{
    background:    rgba(46, 160, 67, 0.10);
    border-color:  rgba(46, 160, 67, 0.40);
    color:         var(--dtm-success);
}

/* ==========================================================================
 * Tag editor — shared component (DTMCP.tagEditor) used by Knowledge
 * Atlas, Catalyst, Image Atlas, etc. for both display and editing of
 * a record's tag list.  The container itself looks like a soft input
 * box; each tag is rendered as a pill (`dtm-tag`) with an optional
 * coloured left edge (set via `--tag-color`) and an optional × button
 * (suppressed in `.is-readonly` mode).
 * ========================================================================== */

.dtm-tag-editor
{
    display:        flex;
    flex-wrap:      wrap;
    gap:            4px;
    align-items:    center;
    padding:        5px 6px;
    background:     var(--dtm-bg);
    border:         1px solid var(--dtm-border-strong);
    border-radius:  var(--dtm-radius-sm);
    cursor:         text;
    min-height:     28px;
    box-sizing:     border-box;
    transition:     border-color 120ms ease, opacity 120ms ease;
}
.dtm-tag-editor.is-busy     { opacity: 0.7; pointer-events: none; }
.dtm-tag-editor:focus-within
{
    outline:        -webkit-focus-ring-color auto 1px;
    outline-offset: 0;
}
.dtm-tag-editor.is-error    { border-color: var(--dtm-error); }
.dtm-tag-editor.is-readonly
{
    padding: 0; border: 0; background: transparent; cursor: default;
    min-height: 0;
}

.dtm-tag-editor .dtm-tag
{
    display:        inline-flex;
    align-items:    center;
    gap:            4px;
    padding:        2px 4px 2px 8px;
    background:     var(--dtm-surface-2);
    color:          var(--dtm-text);
    border-radius:  var(--dtm-radius-pill);
    font-size:      var(--dtm-text-xs);
    user-select:    none;
}
.dtm-tag-editor .dtm-tag[data-color]
{
    border-left:    3px solid var(--tag-color, currentColor);
    padding-left:   7px;
}
.dtm-tag-editor .dtm-tag button
{
    background:     transparent;
    border:         0;
    color:          var(--dtm-text-muted);
    font-size:      12px;
    line-height:    1;
    cursor:         pointer;
    padding:        0 2px;
    border-radius:  50%;
}
.dtm-tag-editor .dtm-tag button:hover { color: var(--dtm-error); }
.dtm-tag-editor.is-readonly .dtm-tag button { display: none; }
.dtm-tag-editor.is-readonly .dtm-tag        { padding-right: 8px; }

.dtm-tag-editor input
{
    flex:           1 1 80px;
    min-width:      80px;
    background:     transparent;
    color:          var(--dtm-text);
    border:         0;
    outline:        none;
    font-size:      var(--dtm-text-sm);
    font-family:    inherit;
    padding:        2px 0;
}
.dtm-tag-editor input::placeholder { color: var(--dtm-text-faint); font-style: italic; }
.dtm-tag-editor.is-readonly input  { display: none; }

/* ==========================================================================
 * Cards (Research Canvas, etc.)
 * ========================================================================== */

.dtm-card
{
    background:    var(--dtm-surface);
    border:        1px solid var(--dtm-border);
    border-radius: var(--dtm-radius-md);
    padding:       var(--dtm-space-3);
    margin-bottom: var(--dtm-space-3);
}
.dtm-card__title
{
    color:       var(--dtm-text);
    font-weight: 600;
    margin:      0 0 var(--dtm-space-1);
    word-break:  break-word;
}
.dtm-card__snippet
{
    color:      var(--dtm-text-muted);
    font-size:  var(--dtm-text-sm);
    line-height: 1.5;
    margin:     0 0 var(--dtm-space-2);
    word-break: break-word;
    max-height: 6.4em;
    overflow:   hidden;
}
.dtm-card__source
{
    color:      var(--dtm-accent-fg);
    font-size:  var(--dtm-text-xs);
    text-decoration: none;
    word-break: break-all;
}
.dtm-card__source:hover { color: var(--dtm-accent-fg-bright); text-decoration: underline; }
.dtm-card__meta
{
    color:      var(--dtm-text-muted);
    font-size:  var(--dtm-text-xs);
    word-break: break-word;
}

/* ==========================================================================
 * Empty / error / loading states
 * ========================================================================== */

.dtm-empty
{
    padding:    var(--dtm-space-6) var(--dtm-space-4);
    color:      var(--dtm-text-faint);
    font-style: italic;
    text-align: center;
}

.dtm-error
{
    padding:       var(--dtm-space-3) var(--dtm-space-4);
    background:    var(--dtm-error-bg);
    color:         var(--dtm-error);
    border-bottom: 1px solid var(--dtm-border);
    font-size:     var(--dtm-text-sm);
}

@keyframes dtm-shimmer
{
    0%   { background-position: -400px 0; }
    100% { background-position:  400px 0; }
}
.dtm-shimmer
{
    background: linear-gradient(90deg, var(--dtm-surface) 0%, var(--dtm-surface-2) 50%, var(--dtm-surface) 100%);
    background-size: 800px 100%;
    animation:  dtm-shimmer 1.4s linear infinite;
    color:      transparent;
    border-radius: var(--dtm-radius-sm);
    min-height: 1em;
}

/* ==========================================================================
 * Status surfaces — shared loading / error / empty / busy chrome.
 *
 * One spinner, one canvas-overlay message box, one full-canvas busy
 * overlay.  Drive them from JS via DTMCP.status.* — apps must NOT
 * hand-roll their own #status / #busy elements or spinner keyframes.
 *
 * Colour rule: error styling is for genuine failures only; loading and
 * empty / instructional states are neutral (faint).
 * ========================================================================== */

/* The single shared rotating indicator. */
.dtm-spinner
{
    display:          inline-block;
    width:            16px;
    height:           16px;
    border:           2px solid var(--dtm-border-strong);
    border-top-color: var(--dtm-accent);
    border-radius:    50%;
    animation:        dtm-spin 0.7s linear infinite;
    flex:             0 0 auto;
}
@keyframes dtm-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce)
{
    .dtm-spinner { animation-duration: 2.4s; }
}

/* Canvas-overlay status message — a centred caption layered over a
 * visualization host.  Neutral by default; .is-error turns it red.
 * Created / updated / removed by DTMCP.status.overlay(). */
.dtm-statusbox
{
    position:        absolute;
    inset:           0;
    z-index:         5;
    display:         flex;
    align-items:     center;
    justify-content: center;
    gap:             var(--dtm-space-2);
    padding:         var(--dtm-space-4);
    box-sizing:      border-box;
    text-align:      center;
    color:           var(--dtm-text-faint);
    font-size:       var(--dtm-text-sm);
    font-style:      italic;
    pointer-events:  none;
}
.dtm-statusbox.is-error { color: var(--dtm-error); font-style: normal; }

/* Full-canvas busy overlay — dimmed backdrop + spinner + label.
 * Created / toggled by DTMCP.status.busy(). */
.dtm-busy
{
    position:        absolute;
    inset:           0;
    z-index:         6;
    display:         none;
    flex-direction:  column;
    align-items:     center;
    justify-content: center;
    gap:             var(--dtm-space-3);
    background:      var(--dtm-overlay-bg);
    backdrop-filter: blur(2px);
    color:           var(--dtm-text-muted);
    font-size:       var(--dtm-text-sm);
    pointer-events:  none;
}
.dtm-busy.is-active        { display: flex; }
.dtm-busy .dtm-spinner     { width: 22px; height: 22px; border-width: 3px; }

/* ===========================================================================
 * Progress bar — linear progress component for long-running ops with
 * known total (e.g. Duplicate Doctor's similar-scan, Image Atlas's
 * feature-print extraction).  Two modes:
 *   - determinate: set `data-percent="0..100"` (or use `DTMCP.progress.set`)
 *   - indeterminate: add `.is-indeterminate` (sweeping fill, no value)
 *
 * Usage:
 *   <div class="dtm-progress" data-percent="0"><div class="dtm-progress__fill"></div></div>
 *   <div class="dtm-progress is-indeterminate"><div class="dtm-progress__fill"></div></div>
 * ========================================================================== */

.dtm-progress
{
    height:        6px;
    background:    var(--dtm-border-soft, var(--dtm-border));
    border-radius: 3px;
    overflow:      hidden;
    position:      relative;
    width:         100%;
}
.dtm-progress__fill
{
    height:        100%;
    width:         0;
    background:    var(--dtm-accent);
    border-radius: inherit;
    transition:    width 200ms cubic-bezier(0.22, 0.72, 0.28, 1);
}
.dtm-progress.is-indeterminate .dtm-progress__fill
{
    width:        35%;
    animation:    dtm-progress-sweep 1.1s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
@keyframes dtm-progress-sweep
{
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(286%); }   /* 100/35 ≈ 286 → travels just past the right edge */
}

/* Compact label row often paired with a progress bar.
 *   <div class="dtm-progress-row">
 *     <span>Scanning records</span>
 *     <span class="dtm-progress-row__count">123 / 500</span>
 *   </div>
 *   <div class="dtm-progress">…</div> */
.dtm-progress-row
{
    display:       flex;
    align-items:   baseline;
    justify-content: space-between;
    font-size:     var(--dtm-text-xs);
    color:         var(--dtm-text-muted);
    margin-bottom: var(--dtm-space-1);
}
.dtm-progress-row__count
{
    color:                 var(--dtm-text-faint);
    font-variant-numeric:  tabular-nums;
}

@media (prefers-reduced-motion: reduce)
{
    .dtm-progress.is-indeterminate .dtm-progress__fill
    {
        animation: none;
        width:     50%;
    }
}

/* --------------------------------------------------------------------------
 * Relevance score — five vertical "signal bars" lit from the left.  Reads a
 * 0..1 similarity / relevance score at-a-glance without a percentage tag.
 * Build the markup with `DTMCP.scoreBars(score)`; used by any app that
 * surfaces `search_records` / `find_similar_records` scores.
 * -------------------------------------------------------------------------- */
.dtm-score
{
    display:     flex;
    align-items: flex-end;
    gap:         2px;
    flex:        0 0 auto;
    width:       22px;
    height:      18px;
}
.dtm-score__bar
{
    width:         3px;
    border-radius: 1px;
    background:    var(--dtm-surface-2);
    transition:    background 160ms ease;
}
.dtm-score__bar--1 { height: 4px;  }
.dtm-score__bar--2 { height: 8px;  }
.dtm-score__bar--3 { height: 12px; }
.dtm-score__bar--4 { height: 15px; }
.dtm-score__bar--5 { height: 18px; }
.dtm-score__bar.is-on
{
    background: var(--dtm-accent-fg);
    box-shadow: 0 0 4px var(--dtm-accent-fg-glow);
}

/* ==========================================================================
 * Toast + modal
 * ========================================================================== */

.dtm-toast-host
{
    position: fixed;
    right:    var(--dtm-space-4);
    bottom:   var(--dtm-space-4);
    display:  flex;
    flex-direction: column;
    gap:      var(--dtm-space-2);
    z-index:  9999;
    pointer-events: none;
}
.dtm-toast
{
    background:    var(--dtm-surface);
    color:         var(--dtm-text);
    border:        1px solid var(--dtm-border-strong);
    border-radius: var(--dtm-radius-md);
    padding:       var(--dtm-space-2) var(--dtm-space-3);
    box-shadow:    0 4px 12px rgba(0, 0, 0, 0.4);
    font-size:     var(--dtm-text-sm);
    pointer-events: auto;
    max-width:     320px;
}
.dtm-toast--success { border-color: var(--dtm-success); }
.dtm-toast--error   { border-color: var(--dtm-error);   }
.dtm-toast--warn    { border-color: var(--dtm-warning); }

.dtm-modal-host
{
    position: fixed;
    inset:    0;
    z-index:  9998;
    background: rgba(0, 0, 0, 0.55);
    display:  flex;
    align-items: center;
    justify-content: center;
}
.dtm-modal
{
    background:    var(--dtm-surface);
    border:        1px solid var(--dtm-border-strong);
    border-radius: var(--dtm-radius-md);
    padding:       var(--dtm-space-4);
    min-width:     320px;
    max-width:     90vw;
    box-shadow:    0 8px 24px rgba(0, 0, 0, 0.6);
}
.dtm-modal__title  { margin: 0 0 var(--dtm-space-2); color: var(--dtm-text); }
.dtm-modal__body   { color: var(--dtm-text-muted); margin-bottom: var(--dtm-space-3); }
.dtm-modal__footer { display: flex; gap: var(--dtm-space-2); justify-content: flex-end; }

/* ==========================================================================
 * Hotkey help overlay
 * ========================================================================== */

.dtm-hotkeys
{
    position:      fixed;
    inset:         auto var(--dtm-space-4) var(--dtm-space-4) auto;
    background:    var(--dtm-surface);
    border:        1px solid var(--dtm-border-strong);
    border-radius: var(--dtm-radius-md);
    padding:       var(--dtm-space-4);
    z-index:       9999;
    min-width:     260px;
    box-shadow:    0 8px 24px rgba(0, 0, 0, 0.6);
}
.dtm-hotkeys__title
{
    margin: 0 0 var(--dtm-space-3);
    font-size: var(--dtm-text-md);
    font-weight: 600;
    color: var(--dtm-text);
}
.dtm-hotkeys__row
{
    display:         flex;
    justify-content: space-between;
    gap:             var(--dtm-space-3);
    font-size:       var(--dtm-text-sm);
    padding:         var(--dtm-space-1) 0;
}
.dtm-hotkeys__keys
{
    color: var(--dtm-text);
    font-family: var(--dtm-font-mono);
    font-size: var(--dtm-text-xs);
}
.dtm-hotkeys__desc { color: var(--dtm-text-muted); }
.dtm-hotkeys[hidden] { display: none; }

/* ==========================================================================
 * Status text helpers
 * ========================================================================== */

.dtm-mute       { color: var(--dtm-text-faint); }
.dtm-text-warn  { color: var(--dtm-warning); }
.dtm-text-error { color: var(--dtm-error); }
.dtm-text-ok    { color: var(--dtm-success); }
.dtm-num        { font-variant-numeric: tabular-nums; }
.dtm-mono       { font-family: var(--dtm-font-mono); }

input[type="checkbox"], input[type="radio"] { accent-color: var(--dtm-accent); }
