/*
 * Structural styles only. Colour comes from props, so the component drops into
 * any design without a token layer to override. The few colours here belong to
 * the tooltip, which needs contrast against the page rather than the data.
 */
.heatmap {
  --heatmap-tooltip-bg: rgba(26, 24, 21, 0.97);
  --heatmap-tooltip-text: #ffffff;
  --heatmap-tooltip-border: rgba(255, 255, 255, 0.12);
  --heatmap-tooltip-shadow: 0 12px 28px rgba(26, 24, 21, 0.22);
  --heatmap-label: #57606a;

  display: inline-flex;
  flex-direction: column;
  gap: 16px;
  font-family: inherit;
}

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

/*
 * A 2x2 grid ties the label tracks to the cell grid: the column labels share a
 * column with the cells, so they cannot drift out of alignment when the row
 * label gutter changes width.
 */
.heatmap__body {
  display: grid;
  grid-template-columns: auto auto;
}

.heatmap__grid {
  grid-area: 2 / 2;
  position: relative;
}

.heatmap__cell-slot {
  position: absolute;
  display: grid;
  place-items: center;
}

.heatmap__cell-slot[tabindex] {
  cursor: default;
}

.heatmap__cell-slot[role="button"] {
  cursor: pointer;
}

.heatmap__cell-slot:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
  border-radius: 3px;
}

.heatmap__cell {
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
  transition: transform 140ms ease-out, opacity 140ms ease-out;
}

.heatmap__cell-content {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: grid;
  place-items: center;
  min-width: 0;
  padding: 1px;
  overflow: hidden;
  font-weight: 500;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  text-align: center;
  white-space: nowrap;
  color: var(--heatmap-cell-content-color, currentColor);
  pointer-events: none;
  text-shadow: 0 1px 1px rgb(0 0 0 / 0.18);
}

.heatmap__cell-slot:hover .heatmap__cell,
.heatmap__cell-slot:focus-visible .heatmap__cell {
  transform: scale(1.18);
}

/* Column labels sit above the cells, each centred on the column it names. */
.heatmap__column-labels {
  grid-area: 1 / 2;
  position: relative;
}

.heatmap__column-label {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  color: var(--heatmap-label);
  font-size: 10px;
  line-height: 1;
  white-space: nowrap;
}

.heatmap__row-labels {
  grid-area: 2 / 1;
  /* Size the track to its widest label; the gutter is a margin, not a grid gap. */
  margin-inline-end: 6px;
  position: relative;
  display: grid;
  align-content: start;
  width: max-content;
}

.heatmap__row-label {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  color: var(--heatmap-label);
  font-size: 10px;
  line-height: 1;
  white-space: nowrap;
}

.heatmap__legend {
  display: flex;
  align-items: center;
  gap: 3px;
  color: var(--heatmap-label);
  font-size: 10px;
}

.heatmap__legend span:first-child {
  margin-right: 2px;
}

.heatmap__legend-unknown {
  margin-left: 0.5rem;
}

.heatmap__legend-swatch {
  display: inline-block;
  flex: none;
}

.heatmap__tooltip {
  position: absolute;
  bottom: calc(100% + 8px);
  z-index: 20;
  width: max-content;
  max-width: 220px;
  padding: 6px 9px;
  border: 1px solid var(--heatmap-tooltip-border);
  border-radius: 8px;
  background: var(--heatmap-tooltip-bg);
  box-shadow: var(--heatmap-tooltip-shadow);
  color: var(--heatmap-tooltip-text);
  font-size: 11px;
  line-height: 1.45;
  pointer-events: none;
}

.heatmap__tooltip[data-align="center"] {
  left: 50%;
  transform: translateX(-50%);
}

.heatmap__tooltip[data-align="start"] {
  left: 0;
}

.heatmap__tooltip[data-align="end"] {
  right: 0;
}

@media (prefers-color-scheme: dark) {
  .heatmap:not([data-heatmap-theme="light"]) {
    --heatmap-tooltip-bg: rgba(240, 238, 235, 0.97);
    --heatmap-tooltip-text: #171614;
    --heatmap-tooltip-border: rgba(0, 0, 0, 0.14);
    --heatmap-label: #9a938a;
  }
}

.heatmap[data-heatmap-theme="dark"] {
  --heatmap-tooltip-bg: rgba(240, 238, 235, 0.97);
  --heatmap-tooltip-text: #171614;
  --heatmap-tooltip-border: rgba(0, 0, 0, 0.14);
  --heatmap-label: #9a938a;
}

@media (prefers-reduced-motion: reduce) {
  .heatmap__cell {
    transition: none;
  }
  .heatmap__cell-slot:hover .heatmap__cell,
  .heatmap__cell-slot:focus-visible .heatmap__cell {
    transform: none;
  }
}

/* Three-dimensional charts share the colour ramp and tooltip tokens with the
 * flat grid. Neutral architecture lets the data provide the colour. */
.heatmap3d {
  --heatmap3d-floor: #f0f1f3;
  --heatmap3d-floor-edge: #d9dde2;
  --heatmap3d-grid: #d9dde2;
  --heatmap3d-ink: #7b838e;
  --heatmap3d-window: #f7fff5;
  position: relative;
  display: flex;
  width: 100%;
  min-width: 0;
}
.heatmap3d__scene {
  display: block;
  width: 100%;
  height: clamp(280px, 40vw, 440px);
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
}
.heatmap3d__scene[data-interactive="true"] { cursor: grab; touch-action: none; }
.heatmap3d__scene[data-dragging="true"] { cursor: grabbing; }
.heatmap3d__scene:focus-visible { outline: 2px solid var(--heatmap3d-ink); outline-offset: -3px; border-radius: 12px; }
.heatmap3d__floor-edge { fill: var(--heatmap3d-floor-edge); }
.heatmap3d__floor-surface { fill: var(--heatmap3d-floor); stroke: var(--heatmap3d-floor-edge); stroke-width: 0.7; }
.heatmap3d__grid-line { fill: none; stroke: var(--heatmap3d-grid); stroke-width: 0.45; opacity: 0.7; }
.heatmap3d__contact-shadow { fill: #111b2b; opacity: 0.13; }
.heatmap3d__surface { stroke: #ffffff30; stroke-width: 0.3; stroke-linejoin: round; }
.heatmap3d[data-shape="circle"] .heatmap3d__surface[data-face="left"],
.heatmap3d[data-shape="circle"] .heatmap3d__surface[data-face="right"] { stroke: none; }
.heatmap3d__window { fill: var(--heatmap3d-window); }
.heatmap3d__roof { fill: #07101b; fill-opacity: 0.11; stroke: #ffffff70; stroke-width: 0.45; stroke-linejoin: round; pointer-events: none; }
.heatmap3d__unknown { stroke: var(--heatmap3d-ink); stroke-width: 0.7; stroke-dasharray: 2 1.8; pointer-events: all; }
.heatmap3d__cell { outline: none; }
.heatmap3d__cell[role="button"] { cursor: pointer; }
.heatmap3d__cell[data-active="true"] .heatmap3d__surface,
.heatmap3d__cell:focus-visible .heatmap3d__surface { stroke: #fff; stroke-width: 1.15; }
.heatmap3d__cell[data-active="true"] .heatmap3d__unknown,
.heatmap3d__cell:focus-visible .heatmap3d__unknown { stroke: currentColor; stroke-width: 1.5; stroke-dasharray: none; }
.heatmap3d__cell:focus-visible { filter: drop-shadow(0 0 1px var(--heatmap3d-ink)); }
.heatmap3d__axis line { stroke: var(--heatmap3d-ink); stroke-width: 0.6; opacity: 0.65; }
.heatmap3d__axis text, .heatmap3d__labels text { fill: var(--heatmap3d-ink); font-family: inherit; font-size: 8px; }
.heatmap3d__labels, .heatmap3d__axis { pointer-events: none; }
.heatmap3d .heatmap3d__tooltip {
  display: flex;
  flex-direction: column;
  gap: 3px;
  bottom: auto;
  right: auto;
  padding: 9px 12px;
  border-radius: 9px;
  transform: translate(-50%, calc(-100% - 14px));
}
.heatmap3d .heatmap3d__tooltip[data-align="start"] { transform: translate(0, calc(-100% - 14px)); }
.heatmap3d .heatmap3d__tooltip[data-align="end"] { transform: translate(-100%, calc(-100% - 14px)); }
.heatmap3d__tooltip strong { font-size: 17px; font-variant-numeric: tabular-nums; line-height: 1.2; }
.heatmap3d__tooltip-label { opacity: 0.7; font-size: 10px; }
.heatmap3d__controls {
  position: absolute;
  right: 8px;
  bottom: 8px;
  display: flex;
  gap: 2px;
  padding: 3px;
  border: 1px solid color-mix(in srgb, var(--heatmap3d-ink) 18%, transparent);
  border-radius: 9px;
  background: color-mix(in srgb, var(--heatmap3d-floor) 90%, transparent);
  backdrop-filter: blur(8px);
}
.heatmap3d__controls button {
  display: grid;
  place-items: center;
  min-width: 32px;
  height: 32px;
  padding: 0 7px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--heatmap3d-ink);
  font-family: inherit;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.heatmap3d__controls button:hover { background: color-mix(in srgb, var(--heatmap3d-ink) 10%, transparent); }
.heatmap3d__controls button:disabled { opacity: 0.35; cursor: default; }
.heatmap3d__controls button:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }
.heatmap3d__reset svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.35; stroke-linecap: round; stroke-linejoin: round; }
.heatmap3d__legend { justify-content: center; padding: 4px 0 10px; }
.heatmap3d__legend .heatmap__legend-swatch { width: 9px; height: 9px; border-radius: 2px; }
.heatmap3d__legend-unknown-swatch { width: 9px; height: 9px; border: 1px dashed var(--heatmap3d-ink); border-radius: 2px; }
.heatmap3d__static-legend text { fill: var(--heatmap3d-ink); font-family: inherit; font-size: 8px; }
.heatmap3d__sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.heatmap3d[data-heatmap-theme="dark"] {
  --heatmap3d-floor: #24272d;
  --heatmap3d-floor-edge: #353a43;
  --heatmap3d-grid: #3d424b;
  --heatmap3d-ink: #a6afbc;
  --heatmap3d-window: #ffd786;
}
@media (prefers-color-scheme: dark) {
  .heatmap3d:not([data-heatmap-theme="light"]) {
    --heatmap3d-floor: #24272d;
    --heatmap3d-floor-edge: #353a43;
    --heatmap3d-grid: #3d424b;
    --heatmap3d-ink: #a6afbc;
    --heatmap3d-window: #ffd786;
  }
}
@media (pointer: coarse) {
  .heatmap3d__controls button { min-width: 44px; height: 44px; }
}
@media (max-width: 540px) {
  .heatmap3d__controls {
    position: relative;
    right: auto;
    bottom: auto;
    align-self: center;
    margin: 0 0 12px;
  }
  .heatmap3d__legend { flex-wrap: wrap; }
}

/* A pattern is an overlay, so the resolved face colour remains visible below
 * sparse bitmap marks. This also keeps custom bitmap patterns colour-agnostic. */
.heatmap3d__surface-base { stroke: none; }

/* Grow is an entrance explanation: the grid settles into its measured
 * elevations in painter order. It is opt-in and uses transform/opacity only,
 * so changing a value never causes the chart to jump in layout. */
@keyframes heatmap3d-grow {
  from { opacity: 0; transform: translateY(4px) scale(0.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes heatmap3d-reduced-fade {
  from { opacity: 0.45; }
  to { opacity: 1; }
}

.heatmap3d[data-animation="grow"] .heatmap3d__cell[data-known="true"],
.heatmap3d__scene[data-animation="grow"] .heatmap3d__cell[data-known="true"] {
  animation: heatmap3d-grow 240ms cubic-bezier(0.23, 1, 0.32, 1) both;
  animation-delay: calc(min(var(--heatmap3d-index, 0), 32) * 26ms);
  transform-box: fill-box;
  transform-origin: center bottom;
}

/* Reduced motion preserves the arrival cue while removing spatial travel and
 * all stagger. */
@media (prefers-reduced-motion: reduce) {
  .heatmap3d[data-animation="grow"] .heatmap3d__cell[data-known="true"],
  .heatmap3d__scene[data-animation="grow"] .heatmap3d__cell[data-known="true"] {
    animation: heatmap3d-reduced-fade 160ms ease-out both;
    animation-delay: 0ms;
    transform: none;
  }
}

/* The night preset owns its architectural neutrals even when the page itself
 * is in light mode. */
.heatmap3d[data-heatmap3d-theme="night"],
.heatmap3d__scene[data-heatmap3d-theme="night"] {
  --heatmap3d-floor: #24272d;
  --heatmap3d-floor-edge: #353a43;
  --heatmap3d-grid: #3d424b;
  --heatmap3d-ink: #a6afbc;
  --heatmap3d-window: #ffd786;
}

.heatmap3d[data-heatmap3d-theme="seasonal"],
.heatmap3d__scene[data-heatmap3d-theme="seasonal"] {
  --heatmap3d-window: #fffaf0;
}

.heatmap3d[data-heatmap3d-theme="rainbow"],
.heatmap3d__scene[data-heatmap3d-theme="rainbow"] {
  --heatmap3d-ink: #687080;
  --heatmap3d-window: #fff8dd;
}
