/* PropertyInsider.sg — Tower View
   Unit-level availability grid for launched projects.

   Design note: this is a showflat sales chart, not a heatmap. State is
   carried by FILL and STRIKE, never by colour alone — an available unit is
   an open box you could still buy, a sold one is filled and struck through.
   Red is reserved for the single selected unit. The optional price map is
   the one place a colour ramp earns its keep, and it ships with a legend
   that names the dollar boundaries. */

.tv{--tv-cell:126px;--tv-gap:4px;--tv-gut:62px;--tv-h:132px}

/* ---------- summary rail ---------- */
.tv-rail{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:1px;background:var(--border);border:1px solid var(--border);border-radius:12px;overflow:hidden;margin-bottom:var(--s5)}
.tv-rail div{background:var(--white);padding:var(--s4)}
.tv-rail .k{display:block;font-size:10px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--text-muted);margin-bottom:5px}
.tv-rail .v{font-family:var(--font-mono);font-size:20px;font-weight:500;color:var(--text);line-height:1.15}
.tv-rail .v small{font-size:11.5px;color:var(--text-muted);font-weight:400}
.tv-rail .avail .v{color:var(--tv-green-ink)}
.tv-rail .s{display:block;font-size:11.5px;color:var(--text-muted);margin-top:3px;line-height:1.45}

/* ---------- controls ---------- */
.tv-bar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s3);margin-bottom:var(--s4)}
.tv-bar .lbl{font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted)}
.tv-group{display:flex;flex-wrap:wrap;gap:6px}
.tv-tab,.tv-chip{font-family:var(--font-sans);font-size:12.5px;font-weight:600;line-height:1;padding:8px 12px;border:1px solid var(--line-strong);background:var(--white);color:var(--text);border-radius:7px;cursor:pointer;transition:border-color .15s,background .15s,color .15s}
.tv-tab:hover,.tv-chip:hover{border-color:var(--ink-3)}
.tv-tab[aria-pressed="true"],.tv-chip[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:#fff}
.tv-tab .n,.tv-chip .n{font-weight:400;opacity:.65;margin-left:5px}
.tv-tab:focus-visible,.tv-chip:focus-visible,.tv-u:focus-visible{outline:2px solid var(--text-accent);outline-offset:2px}

/* ---------- the grid ---------- */
.tv-scroll{overflow-x:auto;overflow-y:hidden;border:1px solid var(--border);border-radius:12px;background:var(--white);padding:var(--s4) var(--s4) var(--s3)}
.tv-grid{display:inline-block;min-width:100%}
.tv-head,.tv-row{display:flex;gap:var(--tv-gap);align-items:stretch}
.tv-row{margin-bottom:var(--tv-gap)}
.tv-head{margin-bottom:7px;padding-bottom:6px;border-bottom:1px solid var(--border);position:sticky;top:0;background:var(--white);z-index:4}

/* the storey ruler: a spine down the left, floors reading top-down like a
   real elevation. This is the widget's signature — it is why the grid is
   recognisably a building and not a spreadsheet. */
.tv-gut{flex:0 0 var(--tv-gut);width:var(--tv-gut);display:flex;flex-direction:column;align-items:flex-end;justify-content:center;padding-right:10px;position:relative;font-family:var(--font-mono);font-size:12px;font-weight:600;color:var(--text);letter-spacing:.02em;text-align:right;line-height:1.25}
.tv-gut b{display:block;font-size:9.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted)}
.tv-row .tv-gut::after{content:'';position:absolute;right:0;top:-2px;bottom:-2px;width:1px;background:var(--border)}
.tv-head .tv-gut{color:var(--text-muted);font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;justify-content:center}

.tv-hc{flex:0 0 var(--tv-cell);width:var(--tv-cell);text-align:center;font-family:var(--font-mono);font-size:12.5px;font-weight:700;color:var(--text);letter-spacing:.03em;align-self:center}

/* ---------- unit cells ----------
   Three states, and each is legible without colour: an available unit says
   "Available", a sold unit carries a price and a date, a non-existent slot is
   hatched and empty. Green is confirmation, not the message itself. */
.tv{--tv-green-bg:#E4F5EA;--tv-green-bd:#9BCFB1;--tv-green-ink:#10603A}
.tv-u,.tv-x{flex:0 0 var(--tv-cell);width:var(--tv-cell);min-height:var(--tv-h);border-radius:7px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:8px 6px;text-align:center;font-family:var(--font-mono)}
.tv-x{border:1px solid var(--border);opacity:.85;background:repeating-linear-gradient(-45deg,var(--mist) 0 5px,var(--white) 5px 10px)}

.tv-u{border:1px solid var(--line-strong);background:var(--white);color:var(--text);cursor:pointer;position:relative;transition:transform .12s,box-shadow .12s,border-color .12s}
.tv-u:hover{transform:translateY(-1px);box-shadow:var(--shadow-lg);border-color:var(--ink-3);z-index:2}
.tv-u .n{font-size:12.5px;font-weight:700;color:var(--text);line-height:1.2}
.tv-u .d{font-size:11px;color:var(--text-soft);line-height:1.35}
.tv-u .p{font-size:12px;font-weight:700;color:var(--text);line-height:1.3;margin-top:2px}
.tv-u .av{font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--tv-green-ink);margin-top:4px;border:1px solid var(--tv-green-bd);border-radius:999px;padding:3px 9px;background:rgba(255,255,255,.72)}

/* available: the one thing a buyer is scanning for */
.tv-u.a{background:var(--tv-green-bg);border-color:var(--tv-green-bd)}
.tv-u.a .n{color:var(--tv-green-ink)}
.tv-u.a .d{color:#2C6B4B}
.tv-u.a:hover{border-color:var(--tv-green-ink)}

.tv-u.sel{border-color:var(--text-accent);box-shadow:0 0 0 2px var(--clay-soft);z-index:3}
.tv-u.sel .n{color:var(--text-accent)}
.tv-u.dim{opacity:.22;pointer-events:none}

/* price map: sold units ramp on psf. Legend prints the dollar boundaries so
   the colour is a shortcut to a number that is also written down. Available
   units never tint — they have no transacted price to rank. */
.tv.heat .tv-u.h1{background:#EDF1F8;border-color:#D6DEEE}
.tv.heat .tv-u.h2{background:#DCE4F2;border-color:#C3CFE6}
.tv.heat .tv-u.h3{background:#C6D3EA;border-color:#AFC0DF}
.tv.heat .tv-u.h4{background:#AEC0E1;border-color:#96AAD4}
.tv.heat .tv-u.h5{background:#93A9D6;border-color:#7A92C9}

/* compact: unit numbers only, so a 59-storey tower fits on one screen */
.tv.compact{--tv-cell:52px;--tv-h:34px;--tv-gut:46px}
.tv.compact .tv-u .d,.tv.compact .tv-u .p,.tv.compact .tv-u .av{display:none}
.tv.compact .tv-u{padding:0}
.tv.compact .tv-u .n{font-size:11px;font-weight:600}
.tv.compact .tv-hc{font-size:11px}
.tv.compact .tv-gut{font-size:11px}
.tv.compact .tv-gut b{display:none}

/* ---------- legend ---------- */
.tv-legend{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s4);align-items:center;margin-top:var(--s3);font-size:12px;color:var(--text-muted)}
.tv-legend span{display:inline-flex;align-items:center;gap:7px}
.tv-key{width:17px;height:14px;border-radius:4px;border:1px solid var(--line-strong);background:var(--white);position:relative;flex:none}
.tv-key.a{background:var(--tv-green-bg);border-color:var(--tv-green-bd)}
.tv-key.s{background:var(--white);border-color:var(--line-strong)}
.tv-key.x{background:repeating-linear-gradient(-45deg,var(--mist) 0 4px,var(--white) 4px 8px);border-color:var(--border)}
.tv-key.k1{background:#EDF1F8}.tv-key.k3{background:#C6D3EA}.tv-key.k5{background:#93A9D6}

/* ---------- selected-unit strip ---------- */
.tv-detail{margin-top:var(--s4);border:1px solid var(--border);border-left:3px solid var(--text-accent);border-radius:10px;background:var(--white);padding:var(--s4) var(--s5)}
.tv-detail[hidden]{display:none}
.tv-detail .u{font-family:var(--font-serif);font-size:21px;font-weight:600;letter-spacing:-.01em;margin-bottom:var(--s1)}
.tv-detail .st{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted);margin-bottom:var(--s3)}
.tv-detail dl{margin:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(128px,1fr));gap:var(--s3) var(--s4)}
.tv-detail dt{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted);margin-bottom:3px}
.tv-detail dd{margin:0;font-family:var(--font-mono);font-size:15.5px;font-weight:500;color:var(--text)}
.tv-detail .note{font-size:12px;color:var(--text-muted);line-height:1.6;margin:var(--s3) 0 0;padding-top:var(--s3);border-top:1px solid var(--border)}
.tv-hint{font-size:12.5px;color:var(--text-muted);margin-top:var(--s3);line-height:1.6}

/* ---------- stack strength table ---------- */
.tv-stacks{width:100%;border-collapse:collapse;margin-top:var(--s5);font-size:13.5px}
.tv-stacks th,.tv-stacks td{padding:9px 12px;text-align:left;border-bottom:1px solid var(--border)}
.tv-stacks th{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted);border-bottom:1px solid var(--line-strong)}
.tv-stacks td.mono{font-family:var(--font-mono)}
.tv-stacks tr:last-child td{border-bottom:0}

/* ---------- latest transactions ---------- */
.tv-txn{width:100%;border-collapse:collapse;font-size:13.5px;background:var(--white)}
.tv-txn th,.tv-txn td{padding:10px 12px;text-align:left;border-bottom:1px solid var(--border);white-space:nowrap}
.tv-txn th{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--text-muted);border-bottom:1px solid var(--line-strong)}
.tv-txn td.mono{font-family:var(--font-mono)}
.tv-txn tr:last-child td{border-bottom:0}
.tv-wrap{overflow-x:auto;border:1px solid var(--border);border-radius:12px}

/* ---------- coverage note ---------- */
.tv-partial{background:var(--ochre-soft);border:1px solid #E6D3A8;border-radius:10px;padding:var(--s3) var(--s4);font-size:13px;line-height:1.65;color:#5C430F;margin-bottom:var(--s4)}
.tv-partial strong{color:#3F2E09}

@media(max-width:700px){
  .tv{--tv-cell:112px;--tv-gut:52px;--tv-h:124px}
  .tv.compact{--tv-cell:48px;--tv-h:32px;--tv-gut:42px}
  .tv-scroll{padding:var(--s3)}
  .tv-u .n{font-size:12px}
  .tv-u .d{font-size:10.5px}
  .tv-detail{padding:var(--s4)}
}
@media(prefers-reduced-motion:reduce){
  .tv-u{transition:none}
  .tv-u:hover{transform:none}
}
