/* ============================================================================
   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); }

/* A tile whose body is a table rather than a chart. For those the declared height is a floor and
   not a ceiling: a ranked table cut off at the card's edge shows no scrollbar and no eighth row,
   and opening the fold under a row pushes more of it out of sight. The front face leaves the
   absolute stack so the card is sized by what is in it, and grows again when a fold opens; the
   back face still fills whatever height that comes to. */
.tile-host:has(.tile-grows) { height: auto; }
.tile-host:has(.tile-grows) .tile-card { height: auto; }
.tile-host:has(.tile-grows) .tile-front {
    position: relative;
    inset: auto;
    min-height: var(--tile-height, 300px);
}
/* Basis auto, or the body reports a hypothetical height of zero and the card collapses onto its
   header: flex: 1 measures a 0 basis against a container that already has a height, and this one
   no longer does. */
.tile-host:has(.tile-grows) .tile-body { flex: 1 0 auto; }
/* The back face is as tall as the front, and .tile-settings-actions pushes itself to the bottom of
   whatever it is in. On a card grown to its table that put Save a thousand pixels under the last
   field. The form keeps its own height here, so the empty space is below the controls instead of
   spread through them. */
.tile-host:has(.tile-grows) .tile-settings-form { height: auto; }

.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); }
