/* ==========================================================================
   SUN Energy — Coverage Map Dashboard
   Built for one deck of hardware: phone -> tablet -> mirrored to a smart TV.
   Sizing is driven by viewport units + a single --scale token so "TV mode"
   enlarges everything without re-laying anything out.
   ========================================================================== */

:root {
  /* Brand — swap these three to retheme the whole dashboard */
  /* Brand teal is dark enough for white text to clear WCAG AA (4.5:1) */
  --brand:        #0B7A70;
  --brand-deep:   #08635B;
  --accent:       #0B7A70;

  /* Surfaces — light theme */
  --bg:           #EEF3F7;
  --surface:      #FFFFFF;
  --surface-2:    #F1F5F9;
  --surface-3:    #E2E8F0;
  --line:         rgba(15, 32, 51, .10);
  --line-strong:  rgba(15, 32, 51, .22);

  /* Text */
  --text:         #10202F;
  --text-2:       #46586C;
  /* --text-3 is used for 11-12px labels, so it must still clear 4.5:1 on white */
  --text-3:       #5B6C7E;

  /* Semantic */
  --ok:           #0F766E;
  --warn:         #B45309;
  --info:         #1D4ED8;

  /* Typography scale — every size derives from --scale */
  --scale: 1;
  --fs-hero:  calc(clamp(1.5rem, 3.1vw, 2.5rem) * var(--scale));
  --fs-lg:    calc(clamp(1rem, 1.35vw, 1.25rem) * var(--scale));
  --fs-md:    calc(clamp(.875rem, 1.05vw, 1rem) * var(--scale));
  --fs-sm:    calc(clamp(.75rem, .85vw, .875rem) * var(--scale));
  --fs-xs:    calc(clamp(.6875rem, .72vw, .78rem) * var(--scale));

  --r-sm: 8px;
  --r:    14px;
  --r-lg: 20px;

  --shadow: 0 14px 34px -10px rgba(16, 40, 66, .26), 0 2px 8px -2px rgba(16, 40, 66, .12);
  --ring: 0 0 0 2px var(--brand);

  /* Header + KPI strip must never exceed a quarter of the screen */
  --kpi-max-h: 22vh;
}

/* TV / presentation mode: bigger everything, less chrome */
:root.tv {
  --scale: 1.45;
  /* Header + strip must still fit the brief's "1/4 of the screen or less",
     including on a portrait tablet where 1vh is small. */
  --kpi-max-h: 17vh;
}
/* The header carries no data worth enlarging — keep it thin so the extra
   height goes to the map. */
:root.tv .hdr { padding-top: .4rem; padding-bottom: .4rem; }
:root.tv .hdr__logo { height: 2.2rem; width: auto; max-width: 10rem; }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

/* Font size lives on <body>, never on <html>: scaling the root font size would
   also scale every `rem`, and --scale would compound with itself. */
body { font-size: var(--fs-md); }

button, input, select { font: inherit; color: inherit; }
button { cursor: pointer; border: 0; background: none; }

/* Focus is always visible — the dashboard is driven by touch AND keyboard on TV */
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 6px; }

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

/* ================================================================== layout */

.app {
  display: grid;
  grid-template-rows: auto auto 1fr;
  /* minmax(0,1fr) + min-width:0 on the children stops the horizontally
     scrollable KPI strip from widening the whole layout on small screens */
  grid-template-columns: minmax(0, 1fr);
  height: 100dvh;
  /* respect notches / rounded TV overscan */
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}
.app > * { min-width: 0; }

/* --------------------------------------------------------------- app header */

.hdr {
  display: flex;
  align-items: center;
  gap: calc(.75rem * var(--scale));
  padding: calc(.5rem * var(--scale)) calc(.85rem * var(--scale));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  z-index: 40;
}

/* Height is fixed, width follows the artwork's aspect ratio, so a wide wordmark
   fills the space instead of being letterboxed inside a square. */
.hdr__logo {
  height: calc(2.1rem * var(--scale));
  width: auto;
  max-width: calc(9rem * var(--scale));
  flex: none;
  object-fit: contain;
  object-position: left center;
}

.hdr__titles { min-width: 0; }
.hdr__title {
  margin: 0;
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: -.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hdr__sub {
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.hdr__spacer { flex: 1 1 auto; }
.hdr__tools { display: flex; align-items: center; gap: calc(.4rem * var(--scale)); flex: none; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: .45em;
  padding: calc(.45rem * var(--scale)) calc(.7rem * var(--scale));
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  white-space: nowrap;
  transition: background .15s, color .15s, border-color .15s, transform .1s;
}
.btn:hover { background: var(--surface-3); color: var(--text); }
.btn:active { transform: scale(.97); }
.btn[aria-pressed="true"], .btn.is-on {
  background: color-mix(in srgb, var(--brand) 20%, transparent);
  border-color: color-mix(in srgb, var(--brand) 55%, transparent);
  color: var(--brand);
}
.btn__icon { width: 1.1em; height: 1.1em; flex: none; }
/* On narrow screens the tool buttons collapse to icons */
@media (max-width: 860px) { .btn__label { display: none; } }

.badge {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  padding: calc(.25rem * var(--scale)) calc(.55rem * var(--scale));
  font-size: var(--fs-xs);
  font-weight: 600;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ok) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--ok) 40%, transparent);
  color: var(--ok);
}
/* Sync-to-Cloudflare indicator (local server only) */
.badge--sync { cursor: pointer; border-width: 1px; border-style: solid; }
/* Two badges plus six buttons squeeze the title on a laptop screen. Below this
   width the badges collapse to their dot; the tooltip still explains them. */
@media (max-width: 1240px) {
  .badge span:not(.badge__dot) { display: none; }
  .badge { padding-left: calc(.4rem * var(--scale)); padding-right: calc(.4rem * var(--scale)); }
}
.badge--sync[hidden] { display: none; }
.badge--sync.is-pending,
.badge--sync.is-syncing {
  color: var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  border-color: color-mix(in srgb, var(--warn) 38%, transparent);
}
.badge--sync.is-error {
  color: #B42318;
  background: rgba(180, 35, 24, .1);
  border-color: rgba(180, 35, 24, .35);
}
.badge--sync.is-syncing .badge__dot { animation: pulse .9s ease-in-out infinite; }
.badge--sync.is-ok .badge__dot,
.badge--sync.is-idle .badge__dot { animation: none; }

.badge__dot {
  width: .5em; height: .5em; border-radius: 999px; background: currentColor;
  animation: pulse 2.2s ease-in-out infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* ------------------------------------------------------------- KPI headline */

.kpis {
  display: grid;
  grid-auto-flow: column;
  /* Never squeeze a card below the width its number needs — with 7 cards on a
     1025 px screen, `1fr` shrank them to 136 px and clipped the unit. Below the
     minimum the strip scrolls instead. */
  grid-auto-columns: minmax(calc(11.25rem * var(--scale)), 1fr);
  gap: calc(.5rem * var(--scale));
  align-content: center;
  max-height: var(--kpi-max-h);
  padding: calc(.55rem * var(--scale)) calc(.85rem * var(--scale));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
  z-index: 30;
}
/* Narrower than a landscape tablet, the cards keep a legible minimum width and
   the strip swipes horizontally instead of squeezing text into ellipses. */
@media (max-width: 1024px) {
  .kpis { scroll-snap-type: x mandatory; }
  .kpi { scroll-snap-align: start; padding-left: calc(.6rem * var(--scale)); padding-right: calc(.6rem * var(--scale)); }
}
.kpis:empty { display: none; }
.kpis[hidden] { display: none; }

.kpi {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .15em;
  min-width: 0;
  padding: calc(.6rem * var(--scale)) calc(.8rem * var(--scale));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: 0 1px 2px rgba(16, 40, 66, .05);
  overflow: hidden;
}
/* accent hairline */
.kpi::before {
  content: "";
  position: absolute; inset: 0 auto 0 0;
  width: 3px;
  background: linear-gradient(180deg, var(--brand), var(--brand-deep));
}
.kpi--accent-2::before { background: linear-gradient(180deg, var(--accent), #0E7490); }
.kpi--accent-3::before { background: linear-gradient(180deg, var(--info), #1D4ED8); }

/* language switch — always shows the language you'd switch TO */
.btn--lang { padding-left: calc(.6rem * var(--scale)); padding-right: calc(.6rem * var(--scale)); }
.btn__lang { font-weight: 800; letter-spacing: .04em; font-size: var(--fs-sm); }

/* --- KPI card head: icon + label + optional (i) --- */
.kpi__head { display: flex; align-items: center; gap: .4em; }
.kpi__head .kpi__label { flex: 1 1 auto; min-width: 0; }

/* Rounded tinted badge so each metric is identifiable at a glance from the TV.
   --tint is set per card in CARD_DEFS; every value is dark enough to clear
   WCAG AA against its own 12% wash. */
.kpi__icon {
  --tint: var(--brand);
  flex: none;
  display: grid;
  place-items: center;
  width: calc(1.4rem * var(--scale));
  height: calc(1.4rem * var(--scale));
  border-radius: 7px;
  color: var(--tint);
  background: color-mix(in srgb, var(--tint) 13%, transparent);
}
.kpi__icon svg { width: 68%; height: 68%; }

/* Lives inside .kpi__label, so it must opt out of the label's uppercase and
   letter-spacing and sit on the text baseline. */
.kpi__info {
  flex: none;
  display: inline-grid;
  vertical-align: -.18em;
  place-items: center;
  width: 1.25em;
  height: 1.25em;
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--fs-xs);
  font-weight: 800;
  font-style: italic;
  line-height: 1;
  color: var(--brand);
  background: rgba(11, 122, 112, .12);
  border: 1px solid rgba(11, 122, 112, .35);
  border-radius: 999px;
}
.kpi__info:hover { color: #fff; background: var(--brand); }

/* "+" after a headline figure: the portfolio keeps growing.
   Sits tight against the last digit — "347+" reads as "more than 347",
   whereas "347 +" reads as an unfinished sum. The spacing before the unit
   is added by .kpi__unit instead, so only the unit is spaced off. */
.kpi__plus {
  margin-left: 0;
  font-size: .62em;
  font-weight: 800;
  color: var(--brand);
  vertical-align: .42em;   /* sits just above the digits' midline */
  line-height: 1;
}

/* Methodology popover. Fixed positioning (coordinates set in JS) so it is never
   clipped by the KPI strip's horizontal scroll container. */
.kpi__pop {
  position: fixed;
  z-index: 1150;
  width: min(22rem, calc(100vw - 1.5rem));
  padding: calc(.7rem * var(--scale)) calc(.8rem * var(--scale));
  border-radius: var(--r);
  /* Fully opaque: this panel carries the disclaimer text, so it must stay
     readable regardless of what sits behind it or whether backdrop-filter works. */
  background: var(--surface);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity .15s, transform .15s;
  pointer-events: none;
  text-align: left;
  white-space: normal;
}
.kpi__pop.is-open { opacity: 1; transform: none; pointer-events: auto; }
.kpi__pop-title  { margin: 0 1.4em .35em 0; font-size: var(--fs-xs); font-weight: 800; color: var(--text); }
.kpi__pop-body   { margin: 0 0 .45em; font-size: var(--fs-xs); line-height: 1.5; color: var(--text-2); }
.kpi__pop-meta   { margin: 0 0 .35em; font-size: var(--fs-xs); line-height: 1.45; color: var(--text-2); }
.kpi__pop-warn   { margin: 0; font-size: var(--fs-xs); line-height: 1.45; color: var(--warn); }
.kpi__pop-close {
  position: absolute;
  top: .3rem; right: .45rem;
  font-size: 1.1em;
  line-height: 1;
  color: var(--text-3);
}
.kpi__pop-close:hover { color: var(--text); }

.kpi__label {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .04em;
  line-height: 1.25;
  text-transform: uppercase;
  color: var(--text-3);
  /* Wraps to two lines rather than truncating — "Clean Electricity Generation"
     must stay readable from across a room. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.kpi__value {
  display: flex;
  align-items: baseline;
  gap: 0;                    /* spacing is per-child; see .kpi__plus / .kpi__unit */
  font-size: var(--fs-hero);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -.025em;
  font-variant-numeric: tabular-nums;
}
/* .72em here = .3em at the parent's font size, matching the old flex gap */
.kpi__unit { margin-left: .72em; font-size: .42em; font-weight: 700; color: var(--brand); letter-spacing: 0; }
.kpi__note {
  font-size: var(--fs-xs);
  color: var(--text-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.kpi__note--warn { color: var(--warn); }

/* ------------------------------------------------------------------ map bay */

.stage { position: relative; min-height: 0; }
#map { position: absolute; inset: 0; background: #DCE8F0; }
.leaflet-container { background: #DCE8F0; font: inherit; outline: none; }
.leaflet-control-attribution {
  background: rgba(255, 255, 255, .82) !important;
  color: var(--text-3) !important;
  font-size: 10px !important;
}
.leaflet-control-attribution a { color: var(--text-2) !important; }
/* Leaflet's own +/- and home buttons, restyled for the light theme */
.leaflet-bar { border: 1px solid var(--line) !important; box-shadow: var(--shadow) !important; border-radius: 10px !important; overflow: hidden; }
.leaflet-bar a {
  background: var(--surface) !important;
  color: var(--text-2) !important;
  border-bottom-color: var(--line) !important;
}
.leaflet-bar a:hover { background: var(--surface-2) !important; color: var(--text) !important; }

/* floating overlay shell shared by the toolbar, legend and detail sheet */
.glass {
  background: rgba(255, 255, 255, .94);
  backdrop-filter: blur(12px) saturate(1.1);
  -webkit-backdrop-filter: blur(12px) saturate(1.1);
  border: 1px solid var(--line);
  border-radius: var(--r);
  box-shadow: var(--shadow);
}

/* --- map toolbar (search + filters) --- */
.maptools {
  position: absolute;
  top: calc(.7rem * var(--scale));
  left: calc(.7rem * var(--scale));
  z-index: 500;
  display: flex;
  flex-direction: column;
  gap: calc(.45rem * var(--scale));
  max-width: min(24rem, calc(100% - 1.4rem));
}
:root.tv .maptools { display: none; }

.search {
  display: flex;
  align-items: center;
  gap: .5em;
  padding: calc(.45rem * var(--scale)) calc(.7rem * var(--scale));
}
.search__icon { width: 1.1em; height: 1.1em; color: var(--text-3); flex: none; }
.search input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: none;
  font-size: var(--fs-sm);
}
.search input::placeholder { color: var(--text-3); }
.search__clear { color: var(--text-3); padding: 0 .2em; font-size: 1.1em; }
.search__clear:hover { color: var(--text); }

.chips { display: flex; flex-wrap: wrap; gap: calc(.3rem * var(--scale)); }
.chip {
  padding: calc(.3rem * var(--scale)) calc(.6rem * var(--scale));
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-2);
  background: rgba(255, 255, 255, .94);
  backdrop-filter: blur(10px);
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: 0 1px 3px rgba(16, 40, 66, .08);
}
.chip:hover { color: var(--text); border-color: var(--line-strong); }
.chip.is-on {
  color: #FFFFFF;
  background: var(--brand);
  border-color: var(--brand);
}
.chip__count { opacity: .65; font-weight: 700; }

/* --- popover (card picker / filter panel) --- */
.pop {
  position: absolute;
  z-index: 900;
  width: min(20rem, calc(100vw - 1.5rem));
  max-height: min(70vh, 34rem);
  overflow-y: auto;
  padding: calc(.75rem * var(--scale));
  display: none;
}
.pop.is-open { display: block; }
.pop__title {
  margin: 0 0 .6em;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-3);
}
.pop__group + .pop__group { margin-top: 1em; padding-top: .9em; border-top: 1px solid var(--line); }

.toggle {
  display: flex;
  align-items: center;
  gap: .6em;
  padding: calc(.4rem * var(--scale)) calc(.25rem * var(--scale));
  font-size: var(--fs-sm);
  border-radius: var(--r-sm);
  cursor: pointer;
}
.toggle:hover { background: var(--surface-2); }
.toggle input { accent-color: var(--brand); width: 1.05em; height: 1.05em; flex: none; }
.toggle__text { flex: 1 1 auto; min-width: 0; }
.toggle__count { font-size: var(--fs-xs); color: var(--text-3); font-variant-numeric: tabular-nums; }

.field { display: block; margin-bottom: .7em; }
.field__label {
  display: block;
  margin-bottom: .3em;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-3);
}
.field select {
  width: 100%;
  padding: calc(.42rem * var(--scale)) calc(.55rem * var(--scale));
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
}

/* --- map pins ---
   Teardrop marker: a rounded square rotated 45deg gives the classic map-pin
   point at the bottom, with the icon counter-rotated so it stays upright. */
.pin {
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform .12s ease-out;
  transform-origin: 50% 100%;
}
.pin:hover { transform: scale(1.16); z-index: 800; }

.pin__disc {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  background: var(--brand);
  border: 2px solid #FFFFFF;
  /* round three corners, leave the bottom-left sharp, then rotate it down */
  border-radius: 50% 50% 50% 4px;
  transform: rotate(45deg);
  box-shadow: 0 3px 8px rgba(16, 40, 66, .38);
}
.pin__disc img,
.pin__disc svg {
  width: 66%;
  height: 66%;
  object-fit: contain;
  transform: rotate(-45deg);   /* keep the icon upright inside the rotated pin */
  pointer-events: none;
}

/* --- artwork pins ---
   The supplied artwork is used EXACTLY as provided: no tinting, fading, filters
   or overlays. Every project gets the same pin regardless of data quality; the
   detail panel is what reports coordinate precision. */
.pin__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
}

/* province-level guesses read as hollow so nobody mistakes them for a surveyed site */
.pin--approx .pin__disc {
  background: #FFFFFF;
  border: 2px dashed var(--brand);
  box-shadow: 0 2px 6px rgba(16, 40, 66, .22);
}
.pin--approx .pin__disc img { opacity: .7; }

/* projects with a playable video get a ring */
.pin--video .pin__disc {
  box-shadow: 0 3px 8px rgba(16, 40, 66, .38), 0 0 0 3px rgba(11, 122, 112, .28);
}
.pin--active .pin__disc {
  background: var(--brand-deep);
  border-color: #FFFFFF;
  box-shadow: 0 4px 12px rgba(16, 40, 66, .5), 0 0 0 4px rgba(11, 122, 112, .35);
}
.pin__ping {
  position: absolute;
  inset: -30%;
  border-radius: 999px;
  border: 2px solid var(--brand);
  animation: ping 1.7s cubic-bezier(0, 0, .2, 1) infinite;
  pointer-events: none;
}
@keyframes ping { 0% { transform: scale(.6); opacity: .8; } 100% { transform: scale(1.85); opacity: 0; } }
/* artwork pins are taller than wide — centre the ping on the head, keep it round */
.pin--art .pin__ping {
  inset: auto;
  left: 50%;
  top: 40%;
  width: 150%;
  aspect-ratio: 1;
  margin: 0;
  transform-origin: 50% 50%;
  animation: ping-art 1.7s cubic-bezier(0, 0, .2, 1) infinite;
}
@keyframes ping-art {
  0%   { transform: translate(-50%, -50%) scale(.5); opacity: .8; }
  100% { transform: translate(-50%, -50%) scale(1.5); opacity: 0; }
}


/* --- detail sheet --- */
.sheet {
  position: absolute;
  z-index: 700;
  left: calc(.7rem * var(--scale));
  bottom: calc(.7rem * var(--scale));
  width: min(26rem, calc(100% - 1.4rem));
  max-height: calc(100% - 1.4rem);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: translateY(calc(100% + 2rem));
  opacity: 0;
  visibility: hidden;
  transition: transform .26s cubic-bezier(.22, 1, .36, 1), opacity .2s, visibility .26s;
}
.sheet.is-open { transform: none; opacity: 1; visibility: visible; }
/* phone: full-width bottom sheet */
@media (max-width: 640px) {
  .sheet { left: 0; right: 0; bottom: 0; width: auto; border-radius: var(--r-lg) var(--r-lg) 0 0; max-height: 78%; }
}
:root.tv .sheet { width: min(34rem, calc(100% - 1.4rem)); }

.sheet__head {
  display: flex;
  align-items: flex-start;
  gap: .6em;
  padding: calc(.85rem * var(--scale)) calc(.95rem * var(--scale)) calc(.6rem * var(--scale));
  border-bottom: 1px solid var(--line);
}
.sheet__eyebrow {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--brand);
}
.sheet__title {
  margin: .15em 0 0;
  font-size: var(--fs-lg);
  font-weight: 750;
  line-height: 1.22;
  letter-spacing: -.012em;
}
.sheet__close {
  flex: none;
  width: calc(1.9rem * var(--scale));
  height: calc(1.9rem * var(--scale));
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  color: var(--text-3);
  background: var(--surface-2);
  font-size: 1.05em;
}
.sheet__close:hover { color: var(--text); background: var(--surface-3); }

.sheet__body { padding: calc(.8rem * var(--scale)) calc(.95rem * var(--scale)); overflow-y: auto; }

.specs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(.65rem * var(--scale)); }
.spec { min-width: 0; }
.spec--wide { grid-column: 1 / -1; }
.spec__k {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-3);
}
.spec__v {
  margin-top: .15em;
  font-size: var(--fs-md);
  font-weight: 650;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.spec__v em { font-style: normal; font-size: .72em; font-weight: 600; color: var(--brand); margin-left: .25em; }
.spec__v--muted { color: var(--text-3); font-weight: 500; }

.tags { display: flex; flex-wrap: wrap; gap: .35em; margin-top: calc(.8rem * var(--scale)); }
.tag {
  padding: .25em .6em;
  font-size: var(--fs-xs);
  font-weight: 600;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-2);
}
.tag--warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 35%, transparent); background: color-mix(in srgb, var(--warn) 12%, transparent); }
.tag--ok   { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 35%, transparent);   background: color-mix(in srgb, var(--ok) 12%, transparent); }

.sheet__foot { padding: 0 calc(.95rem * var(--scale)) calc(.95rem * var(--scale)); display: grid; gap: .5em; }

.btn-play {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .55em;
  width: 100%;
  padding: calc(.65rem * var(--scale)) calc(.9rem * var(--scale));
  font-size: var(--fs-md);
  font-weight: 700;
  color: #FFFFFF;
  background: var(--brand);
  border-radius: var(--r-sm);
  box-shadow: 0 6px 16px -6px rgba(11, 122, 112, .6);
  transition: filter .15s, transform .1s;
}
.btn-play:hover { filter: brightness(1.08); }
.btn-play:active { transform: scale(.985); }
.btn-play__icon { width: 1.25em; height: 1.25em; flex: none; }

.btn-play--disabled {
  background: var(--surface-2);
  border: 1px dashed var(--line-strong);
  color: var(--text-3);
  box-shadow: none;
  cursor: not-allowed;
  font-weight: 600;
  text-align: center;
  line-height: 1.35;
  flex-direction: column;
  gap: .2em;
}
.btn-play--disabled small { font-weight: 500; font-size: .8em; }

/* secondary action row under the play button (e.g. "Edit data project") */
.btn-secondary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .5em;
  width: 100%;
  padding: calc(.55rem * var(--scale)) calc(.9rem * var(--scale));
  font-size: var(--fs-sm);
  font-weight: 650;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  transition: background .15s, color .15s, border-color .15s;
}
.btn-secondary:hover { background: var(--surface-3); color: var(--text); border-color: var(--line-strong); }
.btn-secondary__icon { width: 1.05em; height: 1.05em; flex: none; }

/* ---------------------------------------------------------- editor (modal) */

.form { display: grid; gap: calc(.7rem * var(--scale)); }
.form__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(.7rem * var(--scale)); }
@media (max-width: 560px) { .form__grid { grid-template-columns: minmax(0, 1fr); } }
.form__row--wide { grid-column: 1 / -1; }

.form__label {
  display: block;
  margin-bottom: .3em;
  font-size: var(--fs-xs);
  font-weight: 650;
  color: var(--text-2);
}
.form__hint { display: block; margin-top: .3em; font-size: var(--fs-xs); color: var(--text-3); line-height: 1.4; }
.form input[type="text"],
.form input[type="url"],
.form input[type="number"],
.form select,
.form textarea {
  width: 100%;
  padding: calc(.5rem * var(--scale)) calc(.6rem * var(--scale));
  font-size: var(--fs-sm);
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
}
.form textarea { resize: vertical; min-height: 3.2em; }
.form input::placeholder, .form textarea::placeholder { color: var(--text-3); }
.form input:focus, .form select:focus, .form textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(11, 122, 112, .18);
}
/* a field that currently holds a manual override */
.form__field--overridden input,
.form__field--overridden select,
.form__field--overridden textarea { border-color: var(--brand); background: rgba(11, 122, 112, .05); }

.form__inline { display: flex; gap: .45em; align-items: stretch; }
.form__inline input { flex: 1 1 auto; min-width: 0; }
.form__inline .btn-primary { flex: none; white-space: nowrap; }

/* geocoder candidate list */
.geores {
  display: grid;
  gap: .25em;
  margin-top: .45em;
  max-height: 12rem;
  overflow-y: auto;
}
.geores[hidden] { display: none; }
.geores__item {
  display: block;
  width: 100%;
  padding: calc(.4rem * var(--scale)) calc(.55rem * var(--scale));
  text-align: left;
  font-size: var(--fs-xs);
  line-height: 1.4;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}
.geores__item:hover { color: var(--text); border-color: var(--brand); background: rgba(11, 122, 112, .07); }
.geores__item strong { color: var(--text); }
.geores__coord { display: block; color: var(--text-3); font-variant-numeric: tabular-nums; }

.form__actions {
  display: flex;
  gap: .5em;
  align-items: center;
  padding-top: calc(.3rem * var(--scale));
  border-top: 1px solid var(--line);
}
.form__actions .grow { flex: 1 1 auto; }
.btn-primary {
  padding: calc(.55rem * var(--scale)) calc(1.1rem * var(--scale));
  font-size: var(--fs-sm);
  font-weight: 700;
  color: #FFFFFF;
  background: var(--brand);
  border-radius: var(--r-sm);
  box-shadow: 0 6px 16px -6px rgba(11, 122, 112, .6);
}
.btn-primary:hover { filter: brightness(1.07); }
.btn-primary:disabled { opacity: .55; cursor: progress; }
.btn-danger {
  padding: calc(.55rem * var(--scale)) calc(.8rem * var(--scale));
  font-size: var(--fs-sm);
  font-weight: 650;
  color: #B42318;
  background: rgba(180, 35, 24, .07);
  border: 1px solid rgba(180, 35, 24, .28);
  border-radius: var(--r-sm);
}
.btn-danger:hover { background: rgba(180, 35, 24, .13); }

.form__status { font-size: var(--fs-xs); font-weight: 600; color: var(--text-3); }
.form__status--ok { color: var(--ok); }
.form__status--err { color: #B42318; }

.form__banner {
  padding: calc(.55rem * var(--scale)) calc(.7rem * var(--scale));
  font-size: var(--fs-xs);
  line-height: 1.45;
  color: var(--warn);
  background: rgba(180, 83, 9, .08);
  border: 1px solid rgba(180, 83, 9, .25);
  border-radius: var(--r-sm);
}

/* ====================================================== table view (Excel-like) */

.tablev {
  position: fixed;
  inset: 0;
  z-index: 1100;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  opacity: 0;
  visibility: hidden;
  transition: opacity .18s, visibility .18s;
}
.tablev.is-open { opacity: 1; visibility: visible; }

.tablev__head {
  display: flex;
  align-items: center;
  gap: calc(.6rem * var(--scale));
  flex-wrap: wrap;
  padding: calc(.55rem * var(--scale)) calc(.85rem * var(--scale));
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.tablev__icon { width: 1.2em; height: 1.2em; color: var(--brand); flex: none; }
.tablev__title { margin: 0; font-size: var(--fs-lg); font-weight: 700; }
.tablev__count { font-size: var(--fs-xs); font-weight: 650; color: var(--text-3); }
.tablev__head .grow { flex: 1 1 auto; }

.tablev__search {
  width: min(22rem, 40vw);
  padding: calc(.4rem * var(--scale)) calc(.65rem * var(--scale));
  font-size: var(--fs-sm);
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
}
.tablev__search:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px rgba(11, 122, 112, .16); }

.tablev__toggle {
  display: inline-flex;
  align-items: center;
  gap: .4em;
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-2);
  white-space: nowrap;
  cursor: pointer;
}
.tablev__toggle input { accent-color: var(--brand); width: 1em; height: 1em; }

.tablev__hint {
  padding: calc(.45rem * var(--scale)) calc(.85rem * var(--scale));
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--text-2);
  background: rgba(11, 122, 112, .06);
  border-bottom: 1px solid var(--line);
}

.tablev__scroll { flex: 1 1 auto; overflow: auto; }
.tablev__empty { padding: 2rem; text-align: center; color: var(--text-3); font-size: var(--fs-sm); }

.tbl {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.tbl thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: calc(.5rem * var(--scale)) calc(.55rem * var(--scale));
  text-align: left;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--text-2);
  background: var(--surface-3);
  border-bottom: 1px solid var(--line-strong);
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}
.tbl thead th:hover { color: var(--text); background: var(--surface-2); }
.tbl thead th[data-nosort] { cursor: default; }
.tbl thead th .arrow { opacity: .45; font-size: .9em; }
.tbl thead th.is-sorted { color: var(--brand); }
.tbl thead th.is-sorted .arrow { opacity: 1; }

.tbl tbody td {
  padding: calc(.35rem * var(--scale)) calc(.55rem * var(--scale));
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
  background: var(--surface);
}
.tbl tbody tr:hover td { background: var(--surface-2); }
.tbl tbody tr.is-hidden td { background: repeating-linear-gradient(45deg, #FAFBFC, #FAFBFC 6px, #F1F4F7 6px, #F1F4F7 12px); color: var(--text-3); }
.tbl tbody tr.is-edited td:first-child { box-shadow: inset 3px 0 0 var(--brand); }

/* --- frozen Project column ---
   Stays put while the row scrolls sideways, so you always know which project
   you are editing. Every cell in the column carries an opaque background (see
   the rules above) — a translucent one would let the scrolling cells show
   through. Stacking: header corner > header row > frozen body cells > the rest. */
.tbl thead th:first-child,
.tbl tbody td:first-child {
  position: sticky;
  left: 0;
  border-right: 1px solid var(--line-strong);
}
.tbl thead th:first-child { z-index: 4; }
.tbl tbody td:first-child { z-index: 1; }

/* Drop shadow appears only once the table is actually scrolled sideways. */
.tablev__scroll.is-scrolled .tbl thead th:first-child,
.tablev__scroll.is-scrolled .tbl tbody td:first-child {
  box-shadow: 6px 0 8px -6px rgba(16, 40, 66, .28);
}
.tablev__scroll.is-scrolled .tbl tbody tr.is-edited td:first-child {
  box-shadow: inset 3px 0 0 var(--brand), 6px 0 8px -6px rgba(16, 40, 66, .28);
}

.tbl__name { font-weight: 650; color: var(--text); min-width: 14rem; }
/* On a phone the frozen column must not eat the whole width */
@media (max-width: 640px) { .tbl__name { min-width: 8.5rem; } }
.tbl__sub { display: block; font-size: var(--fs-xs); font-weight: 500; color: var(--text-3); }
.tbl__num { text-align: right; white-space: nowrap; }
.tbl__mini { font-size: var(--fs-xs); color: var(--text-2); white-space: nowrap; }

/* inline editable cells */
.tbl input.cell {
  width: 100%;
  min-width: 5.5rem;
  padding: calc(.25rem * var(--scale)) calc(.4rem * var(--scale));
  font-size: var(--fs-xs);
  font-family: inherit;
  color: var(--text);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
}
.tbl input.cell:hover { border-color: var(--line-strong); background: var(--surface); }
.tbl input.cell:focus { outline: none; border-color: var(--brand); background: var(--surface); box-shadow: 0 0 0 2px rgba(11, 122, 112, .15); }
.tbl input.cell.is-override { border-color: var(--brand); background: rgba(11, 122, 112, .06); }
.tbl input.cell.is-saving { border-color: var(--warn); }
.tbl input.cell.is-bad { border-color: #B42318; background: rgba(180, 35, 24, .06); }
.tbl input.cell--url { min-width: 15rem; }
.tbl input.cell--coord { min-width: 6.5rem; text-align: right; }

.tbl__act { display: flex; gap: .25em; }
.tbl__btn {
  display: grid;
  place-items: center;
  width: 1.85em;
  height: 1.85em;
  font-size: var(--fs-sm);
  line-height: 1;
  color: var(--text-2);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 6px;
}
.tbl__btn:hover { color: var(--brand); border-color: var(--brand); background: rgba(11, 122, 112, .08); }
.tbl__btn--del:hover { color: #B42318; border-color: rgba(180, 35, 24, .5); background: rgba(180, 35, 24, .09); }
.tbl__btn--undo { color: var(--brand); border-color: rgba(11, 122, 112, .4); }

.pill {
  display: inline-block;
  padding: .12em .5em;
  font-size: var(--fs-xs);
  font-weight: 650;
  border-radius: 999px;
  white-space: nowrap;
}
.pill--ok   { color: #0B5D55; background: rgba(11, 122, 112, .13); }
.pill--warn { color: #8A4309; background: rgba(180, 83, 9, .13); }
.pill--off  { color: var(--text-3); background: var(--surface-3); }
/* marks a project that was added from the dashboard, not from Excel */
.pill--new  { color: #5B21B6; background: rgba(109, 40, 217, .13); margin-left: .45em; }

/* "Tambah Project" gets the accent treatment — it is the primary action here */
.btn--add {
  color: #FFFFFF;
  background: var(--brand);
  border-color: var(--brand);
}
.btn--add:hover { background: var(--brand-deep); color: #FFFFFF; }

@media (max-width: 720px) {
  .tablev__hint { display: none; }
  .tablev__search { width: 100%; order: 10; }
}

/* ------------------------------------------------- logo / pin upload widget */

.upload {
  display: grid;
  gap: calc(.55rem * var(--scale));
  padding: calc(.75rem * var(--scale));
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
}
.upload__head { display: flex; align-items: center; justify-content: space-between; gap: .8em; }
.upload__title { font-size: var(--fs-md); font-weight: 700; }
.upload__sub { font-size: var(--fs-xs); color: var(--text-3); margin-top: .15em; }

/* Checkerboard so a transparent PNG is obviously transparent */
.upload__preview {
  flex: none;
  display: grid;
  place-items: center;
  width: calc(4.2rem * var(--scale));
  height: calc(4.2rem * var(--scale));
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background-color: #fff;
  background-image:
    linear-gradient(45deg, #E6EBF0 25%, transparent 25%, transparent 75%, #E6EBF0 75%),
    linear-gradient(45deg, #E6EBF0 25%, transparent 25%, transparent 75%, #E6EBF0 75%);
  background-size: 12px 12px;
  background-position: 0 0, 6px 6px;
}
.upload__preview--logo { width: calc(7.5rem * var(--scale)); }
.upload__preview img { max-width: 100%; max-height: 100%; object-fit: contain; }

.upload__drop {
  display: grid;
  justify-items: center;
  gap: .3em;
  padding: calc(.9rem * var(--scale)) calc(.8rem * var(--scale));
  text-align: center;
  cursor: pointer;
  background: var(--surface);
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--r-sm);
  transition: border-color .15s, background .15s;
}
.upload__drop:hover, .upload__drop.is-drag {
  border-color: var(--brand);
  background: rgba(11, 122, 112, .05);
}
.upload__icon { width: 1.5em; height: 1.5em; color: var(--brand); }
.upload__cta { font-size: var(--fs-sm); font-weight: 700; color: var(--text); }
.upload__hint { font-size: var(--fs-xs); color: var(--text-3); line-height: 1.45; }

.upload__status { font-size: var(--fs-xs); font-weight: 600; min-height: 1.2em; }
.upload__status--ok { color: var(--ok); }
.upload__status--err { color: #B42318; }
.upload__status--busy { color: var(--text-3); }

/* --- first-run hint pill --- */
.hint {
  position: absolute;
  left: calc(.7rem * var(--scale));
  bottom: calc(.7rem * var(--scale));
  z-index: 440;
  display: flex;
  align-items: center;
  gap: .5em;
  padding: calc(.45rem * var(--scale)) calc(.8rem * var(--scale));
  font-size: var(--fs-xs);
  font-weight: 600;
  color: var(--text-2);
  border-radius: 999px;
  transition: opacity .3s, visibility .3s;
}
.hint[hidden] { display: none; }
.hint.is-gone { opacity: 0; visibility: hidden; }
.hint__icon { width: 1.05em; height: 1.05em; color: var(--brand); flex: none; }
/* the zoom controls sit bottom-left too — keep clear of them */
.hint { left: calc(3.9rem * var(--scale)); }
@media (max-width: 560px) { .hint { display: none; } }

/* --- video modal --- */
.modal {
  position: fixed;
  inset: 0;
  z-index: 1200;
  display: grid;
  place-items: center;
  padding: clamp(.75rem, 3vw, 2.5rem);
  background: rgba(16, 32, 47, .55);
  backdrop-filter: blur(5px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s, visibility .2s;
}
/* the editor is taller than a video and needs to scroll on short screens */
.modal__panel--form { max-height: calc(100dvh - 2rem); display: flex; flex-direction: column; }
.modal__body { padding: calc(.9rem * var(--scale)); overflow-y: auto; }
.modal.is-open { opacity: 1; visibility: visible; }
.modal__panel {
  width: min(100%, 74rem);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
  transform: scale(.97);
  transition: transform .22s cubic-bezier(.22, 1, .36, 1);
}
.modal.is-open .modal__panel { transform: none; }
.modal__head {
  display: flex;
  align-items: center;
  gap: .6em;
  padding: calc(.6rem * var(--scale)) calc(.85rem * var(--scale));
  border-bottom: 1px solid var(--line);
}
.modal__title {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--fs-md);
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.modal__frame { position: relative; aspect-ratio: 16 / 9; background: #000; }
.modal__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* --- toast --- */
.toast {
  position: absolute;
  left: 50%;
  bottom: calc(1.2rem * var(--scale));
  transform: translate(-50%, 1.5rem);
  z-index: 1000;
  padding: calc(.5rem * var(--scale)) calc(.9rem * var(--scale));
  font-size: var(--fs-sm);
  font-weight: 600;
  border-radius: 999px;
  opacity: 0;
  visibility: hidden;
  transition: opacity .2s, transform .2s, visibility .2s;
  pointer-events: none;
}
.toast.is-open { opacity: 1; visibility: visible; transform: translate(-50%, 0); }

/* --- boot / empty states --- */
.boot {
  position: absolute;
  inset: 0;
  z-index: 1100;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 1rem;
  background: var(--bg);
  text-align: center;
  padding: 2rem;
}
.boot[hidden] { display: none; }
.boot__spin {
  width: 2.6rem; height: 2.6rem;
  border-radius: 999px;
  border: 3px solid var(--surface-3);
  border-top-color: var(--brand);
  animation: spin .9s linear infinite;
  flex: none;
}
@keyframes spin { to { transform: rotate(360deg); } }
.boot__msg { color: var(--text-2); font-size: var(--fs-sm); max-width: 30rem; line-height: 1.5; }
.boot__msg code {
  display: inline-block;
  margin-top: .5em;
  padding: .3em .5em;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: 6px;
  font-size: .92em;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

/* Signed-in identity + logout (online only) */
.badge--who { cursor: pointer; border: 1px solid var(--line); background: var(--surface-2); color: var(--text-2); }
.badge--who:hover { color: #B42318; border-color: rgba(180,35,24,.4); background: rgba(180,35,24,.07); }
.badge__icon { width: 1em; height: 1em; flex: none; }
/* Table button while the project list is still locked */
.btn--locked { opacity: .75; }
.btn--locked .btn__icon { color: var(--warn); }
