/* ============================================================================
   Tile boards and charts
   --------------------------------------------------------------------------
   A board is a 12-column grid. Each tile spans --tile-cols columns and is
   exactly --tile-height tall, both written by ckwm-tiles.js from the layout
   the user saved. The definite height is the point: charts measure their box
   and draw to fill it, so resizing a tile genuinely resizes its chart.
   ========================================================================== */

.tile-board {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--space-8);
    align-items: start;
    margin-bottom: var(--space-12);
}
.tile-host {
    grid-column: span var(--tile-cols, 6);
    height: var(--tile-height, 300px);
    min-width: 0;
    perspective: 1400px;
}
.tile-board-empty {
    grid-column: 1 / -1;
    background: var(--surface);
    border: var(--border-width) dashed var(--border-strong);
    border-radius: var(--radius-lg);
    padding: var(--space-18) var(--space-10);
}

.tile-card {
    position: relative;
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    transition: transform 0.45s var(--ease-out);
}
.tile-card.flipped { transform: rotateY(180deg); }

.tile-face {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}
.tile-back { transform: rotateY(180deg); }

.tile-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-7);
    border-bottom: var(--border-width) solid var(--border-subtle);
    font-weight: var(--weight-semibold);
    font-size: var(--text-base);
    color: var(--text);
    flex-shrink: 0;
}
.tile-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile-subtitle {
    font-weight: var(--weight-normal);
    font-size: var(--text-xs);
    color: var(--text-muted);
    margin-left: var(--space-3);
}
.tile-actions { display: flex; gap: var(--space-1); flex-shrink: 0; }
.tile-btn {
    background: transparent;
    border: none;
    color: var(--text-subtle);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: var(--text-base);
    line-height: var(--leading-none);
    transition: color var(--transition), background var(--transition);
}
.tile-btn:hover { color: var(--brand); background: var(--surface-hover); }
.tile-btn[disabled] { opacity: 0.3; cursor: default; }
.tile-btn[disabled]:hover { color: var(--text-subtle); background: transparent; }

/* The body is the measured box every chart draws into. min-height:0 is required or
   the flex child refuses to shrink and the chart reports a stale height. */
.tile-body {
    flex: 1;
    min-height: 0;
    padding: var(--space-5) var(--space-7) var(--space-6);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.tile-scroll { overflow-y: auto; overflow-x: hidden; margin: 0 calc(var(--space-7) * -1); padding: 0 var(--space-7); }

.tile-spinner { flex: 1; display: flex; align-items: center; justify-content: center; }
.tile-loading { border: var(--border-width) solid var(--border); border-radius: var(--radius-lg); background: var(--surface); }

/* Chart host: fills whatever the tile body leaves, and is what CkwmCharts measures. */
.ckwm-chart { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.ckwm-plot { flex: 1; min-height: 0; }
.ckwm-plot-scroll { overflow-y: auto; overflow-x: hidden; }
.ckwm-chart .chart-legend,
.chart-card-body .chart-legend {
    display: flex;
    gap: var(--space-5);
    flex-wrap: wrap;
    margin-top: var(--space-3);
    font-size: var(--text-xs);
    color: var(--text-body);
    flex-shrink: 0;
    line-height: var(--leading-normal);
}
.empty-state.chart-empty { flex: 1; display: flex; flex-direction: column; justify-content: center; padding: var(--space-6); }
.empty-state.chart-empty p { margin: var(--space-3) 0 0; }

/* Tile settings (back face) */
.tile-settings-body { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-7) var(--space-8); }
.tile-settings-form { display: flex; flex-direction: column; height: 100%; }
.tile-settings-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5); }
/* What stands where the size controls would be on a tile that owns its size. */
.tile-settings-fixed {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    margin: 0;
    font-size: var(--text-xs);
    color: var(--text-muted);
}
.tile-settings-form .form-group { margin-bottom: var(--space-5); }
.tile-settings-form .form-label {
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    color: var(--text-body);
    margin-bottom: var(--space-1);
    display: block;
}
.tile-settings-form .form-input { font-size: var(--text-base); padding: var(--space-3) var(--space-4); }
.tile-settings-form .form-checkbox { font-size: var(--text-base); }
.tile-settings-own {
    border-top: var(--border-width) solid var(--border-subtle);
    margin-top: var(--space-2);
    padding-top: var(--space-5);
}
.tile-settings-actions {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    margin-top: auto;
    padding-top: var(--space-6);
}

/* Board bar + customize panel */
.tile-board-bar { display: flex; justify-content: flex-end; margin-bottom: var(--space-5); }

/* The board's window: one control, above the grid, for what every governed tile measures over. */
.tile-board-window { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.tile-board-window .form-label { margin-bottom: 0; }
.tile-customize {
    background: var(--surface);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--space-7) var(--space-8);
    margin-bottom: var(--space-8);
}
.tile-customize-head { display: flex; flex-direction: column; gap: var(--space-1); margin-bottom: var(--space-5); }
.tile-customize-list { display: flex; flex-direction: column; }
.tile-customize-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-6);
    padding: var(--space-4) 0;
    border-top: var(--border-width) solid var(--border-subtle);
}
.tile-customize-toggle { display: flex; align-items: flex-start; gap: var(--space-4); margin: 0; cursor: pointer; }
.tile-customize-toggle > span { display: flex; flex-direction: column; font-size: var(--text-base); }
.tile-customize-name { font-weight: var(--weight-semibold); color: var(--text); }
.tile-customize-desc { font-size: var(--text-xs); color: var(--text-muted); font-weight: var(--weight-normal); }
.tile-customize-move { display: flex; gap: var(--space-1); flex-shrink: 0; }
.tile-customize-actions { display: flex; justify-content: space-between; gap: var(--space-4); margin-top: var(--space-6); }

/* Compact KPI strip used inside tiles — the standalone .kpi-grid is capped at 640px,
   which is wrong inside a tile that the user chose the width of.

   --kpi-card-min is the width of the longest label this strip carries on one line
   ("COMMITS / ACTIVE DAY"), because a two-line label pushes its figure down and the figures then
   read at three different heights across one row. Every card on the strip is that wide, so the
   numbers line up whatever the label above them says. */
.tile-kpis {
    --kpi-card-min: 172px;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--kpi-card-min), 1fr));
    gap: var(--space-4);
    align-content: start;
}
.tile-kpis .kpi-card {
    padding: var(--space-6) var(--space-6);
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: var(--space-2);
    text-align: center;
}
.tile-kpis .kpi-value { font-size: var(--text-2xl); margin-top: 0; }
/* One line, always. A tile narrow enough to cut a label loses its tail rather than its shape:
   the title attribute carries the full name and the definition either way. */
.tile-kpis .kpi-label {
    letter-spacing: var(--tracking-wide);
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Runs of figures, each behind the heading that names what the run measures. The heading is beside
   the cards rather than above them: four headings stacked over four rows of cards cost 70px of a
   300px tile. Every heading is the same width, so the cards line up column-for-column down the
   runs.

   A run gets a line to itself. Sharing one means a run of one trails off the end of the run before
   it, reading as a fourth message figure rather than as the one thing this tile says about tests,
   and which runs pair up would change with the tile's width. The tile that carries runs declares a
   FixedSize instead, so there is a definite number of lines and a box that holds them. */
.tile-stat-groups {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: var(--space-6);
    min-height: 100%;
}
.tile-stat-group {
    display: flex;
    align-items: stretch;
    gap: var(--space-5);
    min-width: 0;
}
.tile-stat-heading {
    flex: 0 0 5.5rem;
    align-self: center;
    margin: 0;
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    color: var(--text-muted);
}
.tile-stat-group .tile-kpis { flex: 1 1 auto; min-width: 0; }

/* Scorecard: one measure per row, the headline figure beside the months behind it. */
.tile-scorecard { display: flex; flex-direction: column; gap: var(--space-4); }
.tile-scorecard-measure {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
    align-items: stretch;
    gap: var(--space-4);
}
.tile-scorecard .kpi-card { padding: var(--space-3) var(--space-4); min-width: 0; }
.tile-scorecard .kpi-value { font-size: var(--text-2xl); }
.tile-scorecard .kpi-label { letter-spacing: var(--tracking-wide); overflow-wrap: anywhere; }

/* The strip. Bars sit on a shared baseline and are scaled by --bar-fill, which the server computes
   against the tallest month in that row: the stylesheet cannot see the other months. */
.tile-scorecard-bars {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
    min-height: var(--space-10);
    /* Room under the baseline for the month labels, which are positioned against each bar rather
       than laid out in a second row: a bar is only as tall as its month, so a flow-positioned
       label would sit at a different height in every column. */
    padding-bottom: var(--space-6);
}
.tile-scorecard-bar {
    position: relative;
    flex: 1;
    min-width: 0;
    height: calc(var(--bar-fill, 0) * 100%);
    min-height: var(--border-width);
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: var(--brand);
}
/* The month still running. An outline, never a fill: a part-month is short for a reason that has
   nothing to do with the work, and a solid short bar at the right-hand edge reads as a collapse. */
.tile-scorecard-bar[data-projected="true"] {
    background: transparent;
    border: var(--border-width) dashed var(--brand);
}
.tile-scorecard-bar-label {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    text-align: center;
    font-size: var(--text-2xs);
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Scorecard footnote: what one card was measured over, pinned to the bottom of the body so a
   band of cards lines its footnotes up whatever height the rows above happen to take. */
.tile-scorecard-foot {
    flex-shrink: 0;
    margin-top: auto;
    padding-top: var(--space-3);
    border-top: var(--border-width) solid var(--border-subtle);
}
.tile-scorecard-note {
    display: block;
    font-size: var(--text-xs);
    color: var(--text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-decoration: none;
}
a.tile-scorecard-note:hover { color: var(--brand); }

/* Tile list rows — denser than the page-level .list-item so a tile actually fits several. */
.tile-list { display: flex; flex-direction: column; }
.tile-list-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-5);
    padding: var(--space-3) 0;
    border-top: var(--border-width) solid var(--border-subtle);
    color: inherit;
    text-decoration: none;
}
.tile-list .tile-list-item:first-child { border-top: none; }
a.tile-list-item:hover { background: var(--surface-hover); }
.tile-list-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.tile-list-title {
    font-size: var(--text-base);
    font-weight: var(--weight-medium);
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tile-list-sub { font-size: var(--text-xs); color: var(--text-muted); }
.tile-rank {
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    color: var(--text-subtle);
    width: var(--space-10);
    text-align: right;
    flex-shrink: 0;
}

/* Velocity tile internals */
.tile-velocity-head { display: flex; align-items: baseline; gap: var(--space-5); flex-shrink: 0; margin-bottom: var(--space-2); }
.tile-velocity-headline { display: flex; align-items: baseline; gap: var(--space-4); }
.tile-velocity-headline .kpi-value { font-size: var(--text-4xl); }
.tile-velocity-headline .kpi-trend { margin-top: 0; }
.tile-breakdown { flex-shrink: 0; max-height: 45%; margin-top: var(--space-3); border-top: var(--border-width) solid var(--border-subtle); }
.tile-breakdown-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 90px 44px;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-1) 0;
    font-size: var(--text-sm);
    color: var(--text-body);
    text-decoration: none;
}
.tile-breakdown-row:hover { background: var(--surface-hover); }
.tile-breakdown-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile-breakdown-value { text-align: right; font-variant-numeric: tabular-nums; color: var(--text-body); }
.tile-breakdown-more { font-size: var(--text-xs); color: var(--text-muted); padding: var(--space-2) 0; }
/* Names the period the value column reports. It is not the period the headline number above the
   chart belongs to, so the tile prints both rather than letting one caption stand for the whole. */
.tile-breakdown-head { font-size: var(--text-xs); color: var(--text-muted); text-align: right; padding: var(--space-1) 0; }

/* Mermaid ticket status timeline — keep the rendered SVG inside its card. */
.timeline-wrap { overflow: auto; max-height: 380px; }
.mermaid { margin: 0; text-align: center; }
.mermaid svg { max-width: 100%; height: auto; }

/* The sparkline sits between the child's name and its value, and gives up its
   width to both: the name truncates, the value is tabular, this one flexes. */
.tile-breakdown-spark { flex: 1; min-width: 0; height: var(--space-8); }

/* The un-rotated face of a flip tile. It is the default, and saying so keeps
   the pair with .tile-back readable in the markup. */
.tile-front { transform: rotateY(0deg); }
