/* Interactive org chart viewer (techcoop_frappe/public/js/org_chart_viewer.js).
 *
 * Shipped as a static asset via `web_include_css`, so -- unlike the server-rendered static
 * chart in the handbook Markdown -- nothing here passes through `sanitize_html`. That means
 * `gap` is safe to use (the static chart avoids it only because the sanitizer strips it from
 * *inline* `style` attributes on saved wiki content).
 *
 * Colours are on CSS custom properties, so the chart follows the wiki page's own theme:
 *   1. `--tco-*` tokens default to the static chart's own light-mode colours (see
 *      `org_chart_render.py`'s `_STYLE` string -- keep these two in sync by eye).
 *   2. `body.dark .tc-org-viewer` overrides them from the wiki theme's live variables
 *      (`wiki.scss`'s `body.dark` block: `--background-color`, `--text-color`,
 *      `--border-color`, `--gray-800`) when the wiki itself is in dark mode.
 * The browser/OS `prefers-color-scheme` is deliberately ignored: the wiki does not follow it,
 * so honouring it would paint a dark chart onto a light page.
 * Status pill colours (confirmed/tbf/interim/vacant/outsourced) are the one exception: they
 * stay hardcoded, same values as the static chart, because they are a saturated, deliberately
 * theme-independent badge language, not page chrome.
 */

.tc-org-viewer {
    --tco-bg: #ffffff;
    --tco-card-bg: #ffffff;
    --tco-text: #1a1a1a;
    --tco-muted: #616161;
    --tco-border: #bdbdbd;
    --tco-border-soft: #e0e0e0;
    --tco-connector: #9e9e9e;
    --tco-focus: #1a73e8;
    --tco-match-bg: #fff59d;

    font-family: inherit;
    color: var(--tco-text);
    background: var(--tco-bg);
    border: 1px solid var(--tco-border-soft);
    border-radius: 8px;
    box-sizing: border-box;
    padding: 12px;
    max-width: 100%;
    overflow: hidden; /* the canvas itself pans/zooms; the page must never scroll sideways */
}

.tc-org-viewer *,
.tc-org-viewer *::before,
.tc-org-viewer *::after {
    box-sizing: border-box;
}

body.dark .tc-org-viewer {
    --tco-bg: var(--background-color, #1c2126);
    --tco-card-bg: var(--gray-800, #1c2126);
    --tco-text: var(--text-color, #f4f5f6);
    --tco-muted: var(--sidebar-text-color, #9aa0a6);
    --tco-border: var(--border-color, #4a5158);
    --tco-border-soft: var(--border-color, #363c42);
    --tco-connector: var(--border-color, #6b7278);
}

/* ---------- Toolbar ---------- */

.tco-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 10px;
}

.tco-search {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 200px;
    min-width: 0;
}

.tco-search input[type="search"] {
    flex: 1 1 auto;
    min-width: 0;
    font: inherit;
    font-size: 13px;
    color: var(--tco-text);
    background: var(--tco-card-bg);
    border: 1px solid var(--tco-border);
    border-radius: 6px;
    padding: 5px 8px;
}

.tco-search-status {
    font-size: 11px;
    color: var(--tco-muted);
    white-space: nowrap;
}

.tco-zoom-controls {
    display: flex;
    gap: 6px;
    flex: 0 0 auto;
}

.tco-btn,
.tco-toggle {
    font: inherit;
    font-size: 12px;
    color: var(--tco-text);
    background: var(--tco-card-bg);
    border: 1px solid var(--tco-border);
    border-radius: 5px;
    padding: 4px 9px;
    cursor: pointer;
    line-height: 1.4;
}

.tco-btn:hover,
.tco-toggle:hover {
    background: var(--tco-border-soft);
}

.tc-org-viewer button:focus-visible,
.tc-org-viewer input:focus-visible,
.tco-viewport:focus-visible {
    outline: 2px solid var(--tco-focus);
    outline-offset: 2px;
}

.tco-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---------- Viewport / canvas ---------- */

.tco-viewport {
    /* The single source of truth for the viewport's height ceiling -- org_chart_viewer.js
       reads this back (getComputedStyle) so the box always matches content exactly, up to
       this ceiling. It is deliberately "none" (a value no real chart approaches) at every
       width, not just desktop: the task is explicit that inner vertical scroll on a small
       viewport should only happen "if needed", not be imposed by a deliberately small cap --
       a mobile-only ceiling here would routinely hide most of an expanded chart behind a
       drag-to-pan gesture a first-time reader has no reason to expect, which is a worse
       failure than the page simply growing and being scrolled the normal way. `_heightFitScale`
       (org_chart_viewer.js) still reads this back for the "Fit" button, so if a future need for
       a real per-breakpoint ceiling ever comes back, that wiring does not have to change. */
    --tco-max-vp-height: 100000px;

    position: relative;
    overflow: hidden;
    height: var(--tco-max-vp-height); /* JS sets an explicit px height once it can measure content */
    border: 1px solid var(--tco-border-soft);
    border-radius: 6px;
    background: var(--tco-bg);
    touch-action: pan-y; /* a chart taller than the viewport can still be scrolled by touch */
}

.tco-viewport.tco-pannable {
    cursor: grab;
    touch-action: none; /* only steal touch gestures once panning is actually meaningful */
}

.tco-viewport.tco-dragging {
    cursor: grabbing;
}

.tco-canvas {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0 0;
    padding: 16px;
}

@media (prefers-reduced-motion: no-preference) {
    .tco-canvas {
        transition: transform 180ms ease-out;
    }
    .tco-viewport.tco-dragging .tco-canvas {
        transition: none; /* never fight a live drag with an eased transition */
    }
}

.tco-connectors {
    position: absolute;
    top: 0;
    left: 0;
    overflow: visible;
    pointer-events: none;
}

/* `position: relative` (with no visual effect of its own, since it isn't offset) makes this
   the offsetParent every card underneath it resolves to -- see offsetRelativeTo() in
   org_chart_viewer.js for why connector geometry is measured this way rather than from
   getBoundingClientRect(). */
.tco-tree {
    position: relative;
}

.tco-connectors line {
    stroke: var(--tco-connector);
    stroke-width: 2px;
    vector-effect: non-scaling-stroke;
}

/* ---------- Tree structure ---------- */

.tco-node {
    display: flex;
    flex-direction: column;
    align-items: center;
    position: relative;
}

/* Compact tree: children wrap into as many same-size columns as fit the available width (3 at
   the wiki's ~750px content width, 1-2 on a ~400px phone -- see .tco-card's width below),
   instead of a single unbroken row that only reads at a shrunk-down, illegible scale. Row-gap
   is generous because that gap is where each wrapped row's connector bus+drops are drawn
   (see treeConnectorSegments in org_chart_viewer.js); padding-top is the same gap between the
   parent card and the first row. */
.tco-children {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: center;
    column-gap: 16px;
    row-gap: 32px;
    padding-top: 26px;
}

.tco-child {
    display: flex;
}

/* ---------- Card ---------- */

.tco-card {
    position: relative;
    width: 200px;
    background: var(--tco-card-bg);
    border: 1px solid var(--tco-border);
    border-radius: 6px;
    padding: 8px 10px;
    text-align: left;
    font-size: 12px;
    line-height: 1.4;
    overflow-wrap: anywhere;
}

.tco-card-root {
    width: 220px;
    border-width: 2px;
}

@media (max-width: 480px) {
    /* Narrower cards so a ~400px phone viewport still fits two columns (matching what the
       static chart's own wrapping cards do at the same width) rather than collapsing to a
       single column everywhere. */
    .tco-card {
        width: 152px;
    }

    .tco-card-root {
        width: 168px;
    }

    .tco-children {
        column-gap: 10px;
    }
}

.tco-card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 6px;
}

.tco-role {
    font-weight: bold;
    font-size: 13px;
    color: var(--tco-text);
}

.tco-toggle {
    flex: 0 0 auto;
    padding: 0 6px;
    font-weight: bold;
    line-height: 1.3;
}

.tco-names {
    margin-top: 4px;
}

.tco-name {
    display: inline-block;
    padding: 1px 6px;
    margin: 1px 3px 1px 0;
    border-radius: 3px;
    font-size: 12px;
    background: #e0e0e0;
    color: #1a1a1a;
    border: 1px solid transparent;
}

.tco-status-tbf {
    background: #fff176;
    color: #3a3000;
}

.tco-status-interim {
    background: #ef9a9a;
    color: #3a0000;
}

.tco-status-vacant {
    background: transparent;
    color: var(--tco-muted);
    border: 1px dashed var(--tco-border);
    font-style: italic;
}

.tco-status-outsourced {
    background: transparent;
    color: var(--tco-muted);
    border: 1px solid var(--tco-border);
    font-style: italic;
}

.tco-note {
    margin-top: 2px;
    font-size: 11px;
    font-style: italic;
    color: var(--tco-muted);
}

.tco-stack {
    list-style: none;
    margin: 6px 0 0 0;
    padding: 6px 0 0 0;
    border-top: 1px solid var(--tco-border-soft);
    text-align: left;
}

.tco-stack li {
    font-size: 11px;
    line-height: 1.6;
    margin: 0 0 4px 0;
}

.tco-stack-role {
    font-weight: 600;
}

.tco-stack .tco-name {
    font-size: 11px;
    padding: 0 5px;
}

/* ---------- Search highlight ---------- */

.tco-match {
    outline: 2px solid var(--tco-match-bg);
    outline-offset: 1px;
    background-color: var(--tco-match-bg) !important;
    color: #1a1a1a !important;
}

/* ---------- Legend ---------- */

.tco-legend {
    margin-top: 12px;
    padding-top: 8px;
    border-top: 1px solid var(--tco-border-soft);
    font-size: 11px;
}

.tco-legend-item {
    display: inline-block;
    margin: 0 12px 4px 0;
}
