<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1" />

<title>Bulldog Command Center</title>

<style>

  :root{ --ink:#1B2559; --bg:#F5F6F8; --line:#E5E8EF; --muted:#64748B; --accent:#2563EB; --card:#FFFFFF; }

  *{box-sizing:border-box}

  html,body{margin:0;padding:0;background:var(--bg);color:var(--ink);font-family:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;}

  .wrap{max-width:1120px;margin:0 auto;padding:24px 16px 48px;}

  #login{min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px;}

  .loginCard{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:28px;max-width:380px;width:100%;box-shadow:0 1px 3px rgba(0,0,0,.04);}

  .logo{width:44px;height:44px;border-radius:12px;background:var(--accent);color:#fff;font-weight:800;font-size:20px;display:flex;align-items:center;justify-content:center;}

  h1{font-size:22px;font-weight:700;letter-spacing:-.01em;margin:14px 0 4px;}

  .sub{color:var(--muted);font-size:13px;margin:0 0 18px;}

  input[type=email]{width:100%;border:1px solid #CBD5E1;border-radius:9px;padding:11px 12px;font-size:14px;outline:none;}

  input[type=email]:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(37,99,235,.12);}

  button{cursor:pointer;font-family:inherit;}

  .btn{background:var(--accent);color:#fff;border:none;border-radius:9px;padding:11px 14px;font-size:14px;font-weight:600;width:100%;margin-top:12px;}

  .btn:disabled{opacity:.6;cursor:default;}

  .msg{font-size:13px;margin-top:12px;padding:10px 12px;border-radius:9px;display:none;}

  .msg.show{display:block;}

  .msg.ok{background:#DCFCE7;color:#166534;}

  .msg.err{background:#FEE2E2;color:#991B1B;}

  .head{background:var(--ink);color:#fff;border-radius:20px;padding:18px 22px;display:flex;flex-wrap:wrap;gap:14px;align-items:center;justify-content:space-between;}

  .head .left{display:flex;align-items:center;gap:12px;}

  .head h1{margin:0;font-size:20px;color:#fff;}

  .head .sub{color:#9FB0CE;margin:2px 0 0;}

  .head .right{display:flex;align-items:center;gap:10px;}

  .pill{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;}

  .dot{width:8px;height:8px;border-radius:50%;}

  .ghost{background:#2A3568;color:#D7DEEC;border:none;border-radius:9px;padding:9px 12px;font-size:13px;font-weight:600;}

  .danger{background:var(--accent);color:#fff;border:none;border-radius:9px;padding:9px 13px;font-size:13px;font-weight:600;}

  .kpis{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;margin-top:20px;}

  @media(min-width:640px){.kpis{grid-template-columns:repeat(3,1fr);}}

  @media(min-width:1024px){.kpis{grid-template-columns:repeat(6,1fr);}}

  .kpi{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px;text-align:left;}

  .kpi .n{font-size:24px;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:-.02em;}

  .kpi .l{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin-top:2px;}

  .kpi.click{cursor:pointer;transition:border-color .15s;}

  .kpi.click:hover{border-color:#94A3B8;}

  .kpi.active{border-color:var(--accent);}

  .panel{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px;margin-top:20px;}

  .frow{display:flex;flex-wrap:wrap;gap:8px;align-items:center;}

  .frow input[type=text]{flex:1;min-width:180px;border:1px solid #CBD5E1;border-radius:8px;padding:8px 11px;font-size:14px;outline:none;}

  .frow input[type=text]:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(37,99,235,.12);}

  .frow select{appearance:none;border:1px solid #CBD5E1;border-radius:8px;background:#fff;padding:8px 10px;font-size:13px;font-weight:600;color:#334155;}

  .chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px;}

  .chip{border:none;border-radius:999px;padding:5px 11px;font-size:12px;font-weight:600;}

  .clear{background:none;border:none;color:var(--muted);font-size:13px;text-decoration:underline;text-underline-offset:2px;font-weight:500;}

  .count{font-size:13px;color:var(--muted);margin:14px 2px 8px;}

  .count b{color:var(--ink);}

  .tableWrap{background:var(--card);border:1px solid var(--line);border-radius:14px;overflow:hidden;}

  .scroll{overflow-x:auto;}

  table{width:100%;border-collapse:collapse;font-size:14px;}

  thead tr{background:#F1F4F9;text-align:left;}

  th{font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);font-weight:600;padding:10px 12px;white-space:nowrap;}

  td{padding:9px 12px;border-top:1px solid #EEF1F6;vertical-align:top;}

  .item .name{font-weight:500;}

  .item .meta{font-size:11px;color:#94A3B8;font-variant-numeric:tabular-nums;margin-top:2px;}

  .editsel{appearance:none;border:1px solid transparent;border-radius:7px;padding:4px 22px 4px 9px;font-size:12px;font-weight:600;cursor:pointer;background-repeat:no-repeat;background-position:right 7px center;background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 12 12"><path d="M3 4.5l3 3 3-3" stroke="%23999" stroke-width="1.6" fill="none" stroke-linecap="round"/></svg>');}

  .editsel:hover{border-color:#CBD5E1;}

  .editsel:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgba(37,99,235,.12);}

  .editsel.muted{color:#94A3B8;background-color:#F8FAFC;}

  .expand{display:none;background:#F8FAFC;}

  .expand.show{display:table-row;}

  .expand td{font-size:13px;color:#334155;line-height:1.6;white-space:pre-wrap;}

  .expand .dates{margin-top:8px;font-size:12px;color:#94A3B8;display:flex;gap:18px;flex-wrap:wrap;}

  .expand .due{color:#991B1B;font-weight:600;}

  .disclose{cursor:pointer;}

  #toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background:var(--ink);color:#fff;padding:9px 16px;border-radius:999px;font-size:13px;font-weight:600;opacity:0;transition:opacity .2s;pointer-events:none;z-index:50;}

  #toast.show{opacity:1;}

  .foot{font-size:12px;color:#94A3B8;margin-top:16px;padding:0 2px;}

  .hidden{display:none !important;}

</style>

</head>

<body>

<div id="login">

  <div class="loginCard">

    <div class="logo">B</div>

    <h1>Bulldog Command Center</h1>

    <p class="sub">Private tracker. Sign in with your work email to get a one-time login link.</p>

    <input id="email" type="email" placeholder="you@bulldogadjusters.com" autocomplete="email" />

    <button class="btn" id="sendBtn">Email me a login link</button>

    <div class="msg" id="loginMsg"></div>

  </div>

</div>

<div id="app" class="hidden">

  <div class="wrap">

    <div class="head">

      <div class="left">

        <div class="logo">B</div>

        <div>

          <h1>Bulldog Command Center</h1>

          <p class="sub">Projects &amp; Ideas Tracker &middot; live &amp; editable</p>

        </div>

      </div>

      <div class="right">

        <span class="pill"><span class="dot" id="liveDot" style="background:#22C55E"></span><span id="liveTxt">LIVE</span></span>

        <button class="ghost" id="refreshBtn">Refresh</button>

        <button class="danger" id="logoutBtn">Sign out</button>

      </div>

    </div>

    <div class="kpis" id="kpis"></div>

    <div class="panel">

      <div class="frow">

        <input id="q" type="text" placeholder="Search items, details, BD-#…" />

        <select id="ownerF"></select>

        <select id="typeF"></select>

        <select id="priF">

          <option value="All">Any priority</option>

          <option value="High">High</option><option value="Medium">Medium</option>

          <option value="Low">Low</option><option value="None">No priority</option>

        </select>

        <button class="clear hidden" id="clearBtn">Clear</button>

      </div>

      <div class="chips" id="sysChips"></div>

    </div>

    <div class="count" id="count"></div>

    <div class="tableWrap">

      <div class="scroll">

        <table>

          <thead><tr>

            <th>Item</th><th>System</th><th>Type</th><th>Owner</th><th>Status</th><th>Priority</th>

          </tr></thead>

          <tbody id="tbody"></tbody>

        </table>

      </div>

    </div>

    <p class="foot">Every field is a dropdown — changes save to Supabase instantly. Click a row's name to see full context. Reads &amp; writes are restricted to your account only.</p>

  </div>

</div>

<div id="toast"></div>

<script type="module">

import { createClient } from "https://esm.sh/@supabase/supabase-js@2";

const SUPABASE_URL = "https://gzghwszoezxopcswrtdt.supabase.co";

const SUPABASE_KEY = "sb_publishable_f9S90BFXa3yI4dxV2OO33Q_uEPQ1-74";

const sb = createClient(SUPABASE_URL, SUPABASE_KEY);

const ENUMS = {

  system:["ESPO","YVA","Reporting Tool","BigQuery","Sales Tool","Cross-cutting","Other"],

  type:["Idea","Feature","To-do","Bug","Process"],

  owner:["Shumi","Adrian","Sauham","Max","Suvradip","Deep","Shana","Unassigned"],

  status:["New","Scoped","In Progress","Blocked","Shipped","Killed"],

  priority:["High","Medium","Low"]

};

const SYS={"ESPO":["#EEF2FF","#3730A3","#6366F1"],"YVA":["#ECFEFF","#155E75","#06B6D4"],"Reporting Tool":["#F0FDF4","#166534","#22C55E"],"BigQuery":["#FFFBEB","#92400E","#F59E0B"],"Sales Tool":["#FDF2F8","#9D174D","#EC4899"],"Cross-cutting":["#FAF5FF","#6B21A8","#A855F7"],"Other":["#F1F5F9","#334155","#64748B"]};

const STA={"New":["#E0E7FF","#3730A3"],"Scoped":["#DBEAFE","#1E40AF"],"In Progress":["#FEF3C7","#92400E"],"Blocked":["#FEE2E2","#991B1B"],"Shipped":["#DCFCE7","#166534"],"Killed":["#E5E7EB","#6B7280"]};

const PRI={"High":["#FEE2E2","#991B1B"],"Medium":["#FEF3C7","#92400E"],"Low":["#F1F5F9","#475569"]};

const STATUSES=ENUMS.status;

let rows=[];

let f={q:"",sys:[],sta:[],owner:"All",type:"All",pri:"All"};

let expanded=null;

const $=id=>document.getElementById(id);

const esc=s=>(s==null?"":String(s)).replace(/[&<>"]/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c]));

function toast(t){const el=$("toast");el.textContent=t;el.classList.add("show");clearTimeout(el._t);el._t=setTimeout(()=>el.classList.remove("show"),1600);}

function fmtDay(d){if(!d)return"";try{return new Date(d).toLocaleDateString(undefined,{month:"short",day:"numeric",year:"numeric"});}catch(e){return d;}}

async function init(){

  sb.auth.onAuthStateChange((_e,session)=>{ if(session){showApp();} });

  const {data:{session}}=await sb.auth.getSession();

  if(session){ showApp(); } else { $("login").classList.remove("hidden"); }

}

function showApp(){

  if(!$("app").classList.contains("hidden")) return;

  $("login").classList.add("hidden");

  $("app").classList.remove("hidden");

  buildStaticControls();

  loadRows();

}

$("sendBtn").addEventListener("click", async ()=>{

  const email=$("email").value.trim();

  const m=$("loginMsg");

  if(!email){m.className="msg show err";m.textContent="Enter your email.";return;}

  $("sendBtn").disabled=true;

  const {error}=await sb.auth.signInWithOtp({email,options:{emailRedirectTo:window.location.href.split("#")[0]}});

  if(error){m.className="msg show err";m.textContent=error.message;}

  else{m.className="msg show ok";m.textContent="Check your inbox — tap the link to sign in. You can close this tab after.";}

  $("sendBtn").disabled=false;

});

$("email").addEventListener("keydown",e=>{if(e.key==="Enter")$("sendBtn").click();});

$("logoutBtn").addEventListener("click", async ()=>{await sb.auth.signOut();location.reload();});

$("refreshBtn").addEventListener("click", loadRows);

async function loadRows(){

  $("liveTxt").textContent="Loading…";$("liveDot").style.background="#F59E0B";

  const {data,error}=await sb.from("items").select("*").order("id");

  if(error){$("liveTxt").textContent="ERROR";toast("Load failed: "+error.message);return;}

  rows=data||[];

  $("liveDot").style.background="#22C55E";$("liveTxt").textContent="LIVE";

  render();

}

async function updateField(id,field,value){

  const v=(field==="priority"&&value==="")?null:value;

  const row=rows.find(r=>r.id===id); const prev=row?row[field]:undefined;

  if(row) row[field]=v;

  render();

  const {error}=await sb.from("items").update({[field]:v}).eq("id",id);

  if(error){ if(row)row[field]=prev; render(); toast("Save failed: "+error.message); }

  else { toast("Saved"); }

}

function buildStaticControls(){

  const oF=$("ownerF"); oF.innerHTML='<option value="All">All owners</option>'+ENUMS.owner.map(o=>`<option>${o}</option>`).join("");

  const tF=$("typeF"); tF.innerHTML='<option value="All">All types</option>'+ENUMS.type.map(t=>`<option>${t}</option>`).join("");

  oF.addEventListener("change",e=>{f.owner=e.target.value;expanded=null;render();});

  tF.addEventListener("change",e=>{f.type=e.target.value;expanded=null;render();});

  $("priF").addEventListener("change",e=>{f.pri=e.target.value;expanded=null;render();});

  $("q").addEventListener("input",e=>{f.q=e.target.value;expanded=null;render();});

  $("clearBtn").addEventListener("click",()=>{f={q:"",sys:[],sta:[],owner:"All",type:"All",pri:"All"};$("q").value="";$("ownerF").value="All";$("typeF").value="All";$("priF").value="All";expanded=null;render();});

  const chips=$("sysChips");

  chips.innerHTML=ENUMS.system.map(s=>`<button class="chip" data-sys="${s}"></button>`).join("");

  chips.querySelectorAll("[data-sys]").forEach(b=>b.addEventListener("click",()=>{

    const s=b.getAttribute("data-sys");

    f.sys=f.sys.includes(s)?f.sys.filter(x=>x!==s):[...f.sys,s];expanded=null;render();

  }));

}

function filtered(){

  const ql=f.q.trim().toLowerCase();

  return rows.filter(r=>{

    if(ql){const hay=(r.item+" "+(r.details||"")+" "+(r.task_id||"")).toLowerCase();if(!hay.includes(ql))return false;}

    if(f.sys.length&&!f.sys.includes(r.system))return false;

    if(f.sta.length&&!f.sta.includes(r.status))return false;

    if(f.owner!=="All"&&r.owner!==f.owner)return false;

    if(f.type!=="All"&&r.type!==f.type)return false;

    if(f.pri!=="All"){if(f.pri==="None"){if(r.priority)return false;}else if(r.priority!==f.pri)return false;}

    return true;

  });

}

function selHtml(id,field,value){

  const opts=ENUMS[field];

  let map=field==="system"?SYS:field==="status"?STA:field==="priority"?PRI:null;

  let style="";

  if(map&&value&&map[value]){style=`background-color:${map[value][0]};color:${map[value][1]};`;}

  let html=`<select class="editsel${(field==='priority'&&!value)?' muted':''}" style="${style}" data-id="${id}" data-field="${field}">`;

  if(field==="priority"){html+=`<option value=""${!value?" selected":""}>—</option>`;}

  for(const o of opts){html+=`<option value="${esc(o)}"${o===value?" selected":""}>${esc(o)}</option>`;}

  html+=`</select>`;

  return html;

}

function render(){

  const counts={total:rows.length};STATUSES.forEach(s=>counts[s]=rows.filter(r=>r.status===s).length);

  const kpiDefs=[["Total","total",null],["New","New",STA.New],["In Progress","In Progress",STA["In Progress"]],["Blocked","Blocked",STA.Blocked],["Shipped","Shipped",STA.Shipped],["Killed","Killed",STA.Killed]];

  $("kpis").innerHTML=kpiDefs.map(([label,key,c])=>{

    const active=key!=="total"&&f.sta.includes(key);

    const col=c?`color:${c[1]}`:"";

    return `<button class="kpi click${active?" active":""}" data-kpi="${key}"><div class="n" style="${col}">${counts[key]}</div><div class="l">${label}</div></button>`;

  }).join("");

  $("kpis").querySelectorAll("[data-kpi]").forEach(b=>b.addEventListener("click",()=>{

    const k=b.getAttribute("data-kpi");

    if(k==="total"){f.sta=[];}else{f.sta=f.sta.includes(k)?f.sta.filter(x=>x!==k):[...f.sta,k];}

    expanded=null;render();

  }));

  $("sysChips").querySelectorAll("[data-sys]").forEach(b=>{

    const s=b.getAttribute("data-sys");const on=f.sys.includes(s);const c=SYS[s];

    b.style.background=on?c[2]:c[0];b.style.color=on?"#fff":c[1];b.textContent=s;

  });

  $("clearBtn").classList.toggle("hidden",!(f.q||f.sys.length||f.sta.length||f.owner!=="All"||f.type!=="All"||f.pri!=="All"));

  const list=filtered();

  $("count").innerHTML=`Showing <b>${list.length}</b> of ${rows.length} items`;

  const tb=$("tbody");

  tb.innerHTML=list.map(r=>{

    const meta=esc(r.task_id)+(r.target_date?"  ·  due "+esc(fmtDay(r.target_date)):"");

    const main=`<tr>

      <td class="item"><div class="name disclose" data-exp="${r.id}">${esc(r.item)}</div><div class="meta">${meta}</div></td>

      <td>${selHtml(r.id,"system",r.system)}</td>

      <td>${selHtml(r.id,"type",r.type)}</td>

      <td>${selHtml(r.id,"owner",r.owner)}</td>

      <td>${selHtml(r.id,"status",r.status)}</td>

      <td>${selHtml(r.id,"priority",r.priority||"")}</td>

    </tr>`;

    const exp=`<tr class="expand${expanded===r.id?" show":""}"><td colspan="6">${esc(r.details||"No additional detail.")}<div class="dates">${r.target_date?`<span class="due">Target: ${esc(fmtDay(r.target_date))}</span>`:""}${r.created_at?`<span>Added ${esc(fmtDay(r.created_at))}</span>`:""}</div></td></tr>`;

    return main+exp;

  }).join("")|| `<tr><td colspan="6" style="text-align:center;color:#94A3B8;padding:40px">No items match these filters.</td></tr>`;

  tb.querySelectorAll(".editsel").forEach(s=>s.addEventListener("change",e=>{

    updateField(Number(e.target.getAttribute("data-id")),e.target.getAttribute("data-field"),e.target.value);

  }));

  tb.querySelectorAll("[data-exp]").forEach(d=>d.addEventListener("click",()=>{

    const id=Number(d.getAttribute("data-exp"));expanded=expanded===id?null:id;render();

  }));

}

init();

</script>

</body>

</html>