"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Omega Sentry V2</title>\n  <style>\n    :root {\n      color-scheme: dark;\n      --bg: #05070b;\n      --panel: rgba(12, 16, 24, 0.92);\n      --panel-soft: rgba(255, 255, 255, 0.035);\n      --line: rgba(212, 175, 55, 0.18);\n      --line-soft: rgba(255, 255, 255, 0.08);\n      --gold: #d4af37;\n      --green: #00e676;\n      --cyan: #00d9ff;\n      --amber: #ff9500;\n      --red: #ff3d3d;\n      --text: #ece8df;\n      --muted: #928e84;\n      --dim: #5d5a52;\n    }\n\n    * { box-sizing: border-box; }\n\n    body {\n      margin: 0;\n      min-height: 100vh;\n      background: linear-gradient(180deg, #080a10 0%, var(--bg) 100%);\n      color: var(--text);\n      font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n      letter-spacing: 0;\n    }\n\n    body::before {\n      content: \"\";\n      position: fixed;\n      inset: 0;\n      pointer-events: none;\n      background-image:\n        linear-gradient(rgba(255,255,255,0.025) 1px, transparent 1px),\n        linear-gradient(90deg, rgba(255,255,255,0.018) 1px, transparent 1px);\n      background-size: 48px 48px;\n      mask-image: linear-gradient(to bottom, black, transparent 82%);\n    }\n\n    .shell {\n      width: min(1440px, 100%);\n      margin: 0 auto;\n      padding: 18px;\n    }\n\n    header {\n      min-height: 18vh;\n      display: grid;\n      grid-template-columns: minmax(0, 1fr) auto;\n      gap: 18px;\n      align-items: end;\n      padding: 16px 0 18px;\n      border-bottom: 1px solid var(--line);\n    }\n\n    h1, h2, h3, p { margin: 0; }\n\n    h1 {\n      font-size: clamp(2rem, 6vw, 5.4rem);\n      line-height: 0.9;\n      max-width: 980px;\n      font-weight: 800;\n      letter-spacing: 0;\n    }\n\n    .kicker, .label, .mono {\n      font-family: \"JetBrains Mono\", \"SFMono-Regular\", Consolas, monospace;\n      text-transform: uppercase;\n      letter-spacing: 0.14em;\n    }\n\n    .kicker {\n      color: var(--gold);\n      font-size: 0.72rem;\n      margin-bottom: 10px;\n    }\n\n    .subtitle {\n      margin-top: 14px;\n      color: var(--muted);\n      max-width: 760px;\n      font-size: 0.98rem;\n      line-height: 1.45;\n    }\n\n    .status-stack {\n      display: grid;\n      gap: 8px;\n      justify-items: end;\n    }\n\n    .pill {\n      display: inline-flex;\n      align-items: center;\n      gap: 7px;\n      border: 1px solid var(--line);\n      background: rgba(212, 175, 55, 0.08);\n      color: var(--gold);\n      border-radius: 999px;\n      padding: 6px 10px;\n      font-size: 0.72rem;\n      font-weight: 800;\n      white-space: nowrap;\n    }\n\n    .dot {\n      width: 7px;\n      height: 7px;\n      border-radius: 50%;\n      background: currentColor;\n      box-shadow: 0 0 14px currentColor;\n    }\n\n    nav {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 8px;\n      margin: 16px 0;\n    }\n\n    button, .link-button {\n      border: 1px solid var(--line-soft);\n      background: var(--panel-soft);\n      color: var(--text);\n      border-radius: 8px;\n      padding: 9px 12px;\n      cursor: pointer;\n      font: inherit;\n      text-decoration: none;\n    }\n\n    button.active {\n      border-color: rgba(212, 175, 55, 0.54);\n      background: rgba(212, 175, 55, 0.12);\n      color: var(--gold);\n    }\n\n    .grid {\n      display: grid;\n      grid-template-columns: 1.2fr 0.8fr;\n      gap: 14px;\n      align-items: start;\n    }\n\n    .panel {\n      border: 1px solid var(--line-soft);\n      background: var(--panel);\n      border-radius: 8px;\n      padding: 14px;\n      backdrop-filter: blur(18px);\n    }\n\n    .panel h2 {\n      font-size: 0.86rem;\n      color: var(--gold);\n      margin-bottom: 12px;\n    }\n\n    .metric-grid {\n      display: grid;\n      grid-template-columns: repeat(4, minmax(0, 1fr));\n      gap: 10px;\n    }\n\n    .metric {\n      border: 1px solid var(--line-soft);\n      background: rgba(255,255,255,0.028);\n      border-radius: 8px;\n      padding: 12px;\n      min-width: 0;\n    }\n\n    .label {\n      color: var(--dim);\n      font-size: 0.64rem;\n      margin-bottom: 7px;\n    }\n\n    .value {\n      font-size: clamp(1.25rem, 3vw, 2rem);\n      line-height: 1;\n      font-weight: 800;\n      color: var(--text);\n      overflow-wrap: anywhere;\n    }\n\n    .value.small { font-size: 1rem; line-height: 1.25; }\n    .green { color: var(--green); }\n    .amber { color: var(--amber); }\n    .red { color: var(--red); }\n    .cyan { color: var(--cyan); }\n    .gold { color: var(--gold); }\n\n    .stack { display: grid; gap: 10px; }\n\n    .source-grid {\n      display: grid;\n      grid-template-columns: repeat(3, minmax(0, 1fr));\n      gap: 10px;\n    }\n\n    .source-card {\n      border: 1px solid var(--line-soft);\n      background: rgba(255,255,255,0.028);\n      border-radius: 8px;\n      padding: 10px;\n      min-width: 0;\n    }\n\n    .source-card .row-title {\n      font-size: 0.82rem;\n    }\n\n    .notice {\n      border: 1px solid rgba(255, 149, 0, 0.34);\n      background: rgba(255, 149, 0, 0.08);\n      color: var(--text);\n      border-radius: 8px;\n      padding: 10px 12px;\n      margin-bottom: 14px;\n    }\n\n    .notice.hidden { display: none; }\n\n    .row {\n      display: grid;\n      grid-template-columns: auto 1fr auto;\n      gap: 10px;\n      align-items: center;\n      border-bottom: 1px solid var(--line-soft);\n      padding: 9px 0;\n    }\n\n    .row:last-child { border-bottom: none; }\n\n    .row-title {\n      min-width: 0;\n      color: var(--text);\n      font-weight: 700;\n      overflow-wrap: anywhere;\n    }\n\n    .row-sub {\n      color: var(--muted);\n      font-size: 0.82rem;\n      margin-top: 3px;\n      overflow-wrap: anywhere;\n    }\n\n    .badge {\n      border: 1px solid var(--line-soft);\n      border-radius: 999px;\n      padding: 3px 7px;\n      font-size: 0.68rem;\n      color: var(--muted);\n      white-space: nowrap;\n    }\n\n    .split {\n      display: grid;\n      grid-template-columns: repeat(2, minmax(0, 1fr));\n      gap: 14px;\n    }\n\n    .farm-layout {\n      display: grid;\n      grid-template-columns: minmax(280px, 0.85fr) minmax(0, 1.35fr);\n      gap: 14px;\n      align-items: start;\n    }\n\n    .toolbar {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 8px;\n      align-items: center;\n      margin-bottom: 12px;\n    }\n\n    input, select, textarea {\n      width: 100%;\n      border: 1px solid var(--line-soft);\n      background: rgba(255,255,255,0.04);\n      color: var(--text);\n      border-radius: 8px;\n      padding: 9px 10px;\n      font: inherit;\n      min-height: 40px;\n    }\n\n    textarea {\n      resize: vertical;\n      min-height: 76px;\n    }\n\n    label {\n      display: grid;\n      gap: 5px;\n      color: var(--muted);\n      font-size: 0.78rem;\n      min-width: 0;\n    }\n\n    .form-grid {\n      display: grid;\n      grid-template-columns: repeat(2, minmax(0, 1fr));\n      gap: 10px;\n    }\n\n    .span-2 { grid-column: 1 / -1; }\n\n    .library-list, .inventory-grid {\n      display: grid;\n      gap: 10px;\n    }\n\n    .inventory-grid {\n      grid-template-columns: repeat(2, minmax(0, 1fr));\n    }\n\n    .farm-card {\n      border: 1px solid var(--line-soft);\n      background: rgba(255,255,255,0.028);\n      border-radius: 8px;\n      padding: 12px;\n      min-width: 0;\n    }\n\n    .farm-card-head {\n      display: grid;\n      grid-template-columns: 1fr auto;\n      gap: 10px;\n      align-items: start;\n    }\n\n    .farm-meta {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 6px;\n      margin-top: 8px;\n    }\n\n    .thumb-row {\n      display: flex;\n      gap: 8px;\n      overflow-x: auto;\n      padding-top: 9px;\n    }\n\n    .thumb-row img {\n      width: 76px;\n      height: 76px;\n      object-fit: cover;\n      border-radius: 8px;\n      border: 1px solid var(--line-soft);\n      flex: 0 0 auto;\n      background: rgba(255,255,255,0.04);\n    }\n\n    .button-row {\n      display: flex;\n      flex-wrap: wrap;\n      gap: 6px;\n      margin-top: 10px;\n    }\n\n    .button-row button {\n      padding: 7px 9px;\n      font-size: 0.82rem;\n    }\n\n    .hidden { display: none !important; }\n\n    footer {\n      color: var(--dim);\n      padding: 22px 0 8px;\n      font-size: 0.78rem;\n    }\n\n    @media (max-width: 900px) {\n      header, .grid, .split, .farm-layout { grid-template-columns: 1fr; }\n      .status-stack { justify-items: start; }\n      .metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n      .source-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n      .inventory-grid { grid-template-columns: 1fr; }\n      .shell { padding: 12px; }\n    }\n\n    @media (max-width: 420px) {\n      .metric-grid { grid-template-columns: 1fr; }\n      .source-grid { grid-template-columns: 1fr; }\n      .form-grid { grid-template-columns: 1fr; }\n      .span-2 { grid-column: auto; }\n      .row { grid-template-columns: 1fr; }\n      nav button { flex: 1 1 44%; }\n    }\n  </style>\n</head>\n<body>\n  <main class=\"shell\">\n    <header>\n      <section>\n        <div class=\"kicker\">Omega Estate Operating System / Sentry V2</div>\n        <h1>Command Center</h1>\n        <p class=\"subtitle\" id=\"mission-copy\">\n          Local-first estate intelligence for family protection, regenerative land work, and low-friction daily decisions.\n        </p>\n      </section>\n      <aside class=\"status-stack\">\n        <div class=\"pill\" id=\"health-pill\"><span class=\"dot\"></span><span>Loading</span></div>\n        <div class=\"pill\"><span class=\"dot\"></span><span id=\"view-label\">Admin view</span></div>\n        <a class=\"link-button mono\" href=\"/dashboard.html\">Reset</a>\n      </aside>\n    </header>\n\n    <nav aria-label=\"Dashboard sections\">\n      <button class=\"active\" data-section=\"overview\">Overview</button>\n      <button data-section=\"sentry\">Sentry</button>\n      <button data-section=\"farm\">Farm</button>\n      <button data-section=\"tasks\">Tasks</button>\n      <button data-section=\"agents\">Agents</button>\n      <button data-section=\"sources\">Sources</button>\n      <button data-section=\"public\">Public</button>\n      <button data-section=\"family\">Family</button>\n    </nav>\n\n    <div id=\"error-banner\" class=\"notice hidden\" role=\"status\"></div>\n\n    <section id=\"overview\" class=\"section grid\">\n      <div class=\"stack\">\n        <div class=\"panel\">\n          <h2 class=\"mono\">Morning Readout</h2>\n          <div class=\"metric-grid\">\n            <div class=\"metric\"><div class=\"label\">Weather</div><div class=\"value small\" id=\"weather\">--</div></div>\n            <div class=\"metric\"><div class=\"label\">Tasks</div><div class=\"value\" id=\"task-count\">--</div></div>\n            <div class=\"metric\"><div class=\"label\">Irrigation</div><div class=\"value\" id=\"irrigation\">--</div></div>\n            <div class=\"metric\"><div class=\"label\">Agents</div><div class=\"value\" id=\"agent-count\">--</div></div>\n          </div>\n        </div>\n        <div class=\"panel\">\n          <h2 class=\"mono\">Focus</h2>\n          <p id=\"focus\" class=\"row-sub\">Loading estate focus...</p>\n        </div>\n        <div class=\"panel\">\n          <h2 class=\"mono\">Source Health</h2>\n          <div id=\"source-summary\" class=\"source-grid\"></div>\n        </div>\n      </div>\n      <div class=\"panel\">\n        <h2 class=\"mono\">Next Best Actions</h2>\n        <div id=\"next-actions\" class=\"stack\"></div>\n      </div>\n    </section>\n\n    <section id=\"sentry\" class=\"section hidden\">\n      <div class=\"split\">\n        <div class=\"panel\">\n          <h2 class=\"mono\">Device Perimeter</h2>\n          <div id=\"devices\"></div>\n        </div>\n        <div class=\"panel\">\n          <h2 class=\"mono\">Land Telemetry</h2>\n          <div id=\"land\"></div>\n        </div>\n      </div>\n    </section>\n\n    <section id=\"farm\" class=\"section hidden\">\n      <div class=\"farm-layout\">\n        <div class=\"stack\">\n          <div class=\"panel\">\n            <h2 class=\"mono\">Farm Library</h2>\n            <div class=\"toolbar\">\n              <input id=\"farm-library-search\" type=\"search\" placeholder=\"Search crops, animals, supplies\">\n              <select id=\"farm-library-category\" aria-label=\"Filter library category\"></select>\n            </div>\n            <div id=\"farm-library-list\" class=\"library-list\"></div>\n          </div>\n          <div class=\"panel\">\n            <h2 class=\"mono\">Add Inventory</h2>\n            <div class=\"toolbar\">\n              <input id=\"farm-token\" type=\"password\" autocomplete=\"off\" placeholder=\"Local token for changes\">\n              <button id=\"farm-token-save\" type=\"button\">Use Token</button>\n            </div>\n            <form id=\"farm-form\" class=\"form-grid\">\n              <label class=\"span-2\">Library Option\n                <select id=\"farm-catalog-select\" name=\"catalog_id\"></select>\n              </label>\n              <label>Name\n                <input id=\"farm-name\" name=\"name\" required maxlength=\"120\">\n              </label>\n              <label>Variety / Breed\n                <input id=\"farm-variety\" name=\"variety\" maxlength=\"80\">\n              </label>\n              <label>Type\n                <select id=\"farm-type\" name=\"type\">\n                  <option value=\"plant\">Plant</option>\n                  <option value=\"animal\">Animal</option>\n                  <option value=\"supply\">Supply</option>\n                  <option value=\"equipment\">Equipment</option>\n                  <option value=\"product\">Product</option>\n                  <option value=\"other\">Other</option>\n                </select>\n              </label>\n              <label>Status\n                <select id=\"farm-status\" name=\"status\">\n                  <option value=\"planned\">Planned</option>\n                  <option value=\"seeded\">Seeded</option>\n                  <option value=\"growing\">Growing</option>\n                  <option value=\"ready\">Ready</option>\n                  <option value=\"harvested\">Harvested</option>\n                  <option value=\"stored\">Stored</option>\n                  <option value=\"sold\">Sold</option>\n                  <option value=\"archived\">Archived</option>\n                </select>\n              </label>\n              <label>Quantity\n                <input id=\"farm-quantity\" name=\"quantity\" type=\"number\" min=\"0\" step=\"0.01\" value=\"1\">\n              </label>\n              <label>Unit\n                <input id=\"farm-unit\" name=\"unit\" maxlength=\"40\" value=\"each\">\n              </label>\n              <label>Location\n                <input id=\"farm-location\" name=\"location\" maxlength=\"120\" placeholder=\"greenhouse, north bed, coop\">\n              </label>\n              <label>Source\n                <input id=\"farm-source\" name=\"source\" maxlength=\"120\" placeholder=\"saved seed, nursery, supplier\">\n              </label>\n              <label>Planted Date\n                <input id=\"farm-planted-date\" name=\"planted_date\" type=\"date\">\n              </label>\n              <label>Expected Harvest\n                <input id=\"farm-harvest-date\" name=\"expected_harvest_date\" type=\"date\">\n              </label>\n              <label class=\"span-2\">Tags\n                <input id=\"farm-tags\" name=\"tags\" maxlength=\"160\" placeholder=\"spring, greenhouse, plant-sale\">\n              </label>\n              <label class=\"span-2\">Photo\n                <input id=\"farm-photo\" name=\"photo\" type=\"file\" accept=\"image/jpeg,image/png,image/webp,image/gif\">\n              </label>\n              <label class=\"span-2\">Notes\n                <textarea id=\"farm-notes\" name=\"notes\" maxlength=\"2000\"></textarea>\n              </label>\n              <button class=\"span-2\" type=\"submit\">Add to Inventory</button>\n            </form>\n          </div>\n        </div>\n        <div class=\"panel\">\n          <h2 class=\"mono\">Growing & Inventory</h2>\n          <div class=\"toolbar\">\n            <input id=\"farm-inventory-search\" type=\"search\" placeholder=\"Search inventory\">\n            <select id=\"farm-status-filter\" aria-label=\"Filter inventory status\">\n              <option value=\"\">All statuses</option>\n              <option value=\"planned\">Planned</option>\n              <option value=\"seeded\">Seeded</option>\n              <option value=\"growing\">Growing</option>\n              <option value=\"ready\">Ready</option>\n              <option value=\"harvested\">Harvested</option>\n              <option value=\"stored\">Stored</option>\n              <option value=\"sold\">Sold</option>\n              <option value=\"archived\">Archived</option>\n            </select>\n          </div>\n          <div id=\"farm-inventory-summary\" class=\"notice hidden\"></div>\n          <div id=\"farm-inventory-list\" class=\"inventory-grid\"></div>\n        </div>\n      </div>\n    </section>\n\n    <section id=\"tasks\" class=\"section hidden\">\n      <div class=\"panel\">\n        <h2 class=\"mono\">Task Queue</h2>\n        <div id=\"tasks-list\"></div>\n      </div>\n    </section>\n\n    <section id=\"agents\" class=\"section hidden\">\n      <div class=\"panel\">\n        <h2 class=\"mono\">Hermes / Omega Activity</h2>\n        <div id=\"activity-list\"></div>\n      </div>\n    </section>\n\n    <section id=\"sources\" class=\"section hidden\">\n      <div class=\"panel\">\n        <h2 class=\"mono\">Telemetry Sources</h2>\n        <div id=\"sources-list\" class=\"source-grid\"></div>\n      </div>\n    </section>\n\n    <section id=\"public\" class=\"section hidden\">\n      <div class=\"grid\">\n        <div class=\"panel\">\n          <h2 class=\"mono\">Public Estate Signal</h2>\n          <p class=\"row-sub\">Weather, land progress, and public-facing availability. No private devices, tasks, or command surfaces are shown here.</p>\n          <div id=\"public-summary\" class=\"stack\" style=\"margin-top:12px\"></div>\n        </div>\n        <div class=\"panel\">\n          <h2 class=\"mono\">Opportunities</h2>\n          <div id=\"public-opportunities\"></div>\n        </div>\n      </div>\n    </section>\n\n    <section id=\"family\" class=\"section hidden\">\n      <div class=\"grid\">\n        <div class=\"panel\">\n          <h2 class=\"mono\">Family View</h2>\n          <p class=\"row-sub\">A low-noise view for house rhythm, weather, calendar, and encouraging land progress.</p>\n          <div id=\"family-summary\" class=\"stack\" style=\"margin-top:12px\"></div>\n        </div>\n        <div class=\"panel\">\n          <h2 class=\"mono\">Calendar</h2>\n          <div id=\"calendar-list\"></div>\n        </div>\n      </div>\n    </section>\n\n    <footer class=\"mono\">\n      <span id=\"updated\">Last updated: --</span>\n      <span> / Static S3-ready dashboard. Sensitive command execution remains local.</span>\n    </footer>\n  </main>\n\n  <script>\n    const state = {};\n    const sourceStatus = {};\n    const farmState = {\n      catalog: { categories: [] },\n      inventory: { items: [] }\n    };\n    const qs = new URLSearchParams(location.search);\n    const view = qs.get('view') || location.pathname.split('/').filter(Boolean)[0] || 'admin';\n\n    const files = {\n      pulse: 'state/digital_pulse.json',\n      tasks: 'state/tasks.json',\n      weather: 'state/weather.json',\n      hydrowise: 'state/hydrowise.json',\n      ecobee: 'state/ecobee.json',\n      soil: 'state/soil_telemetry.json',\n      activity: 'state/agent_activity.json',\n      grants: 'state/grants.json',\n      calendar: 'state/local_events.json'\n    };\n\n    function esc(value) {\n      return String(value ?? '').replace(/[&<>\"']/g, (char) => ({\n        '&': '&amp;',\n        '<': '&lt;',\n        '>': '&gt;',\n        '\"': '&quot;',\n        \"'\": '&#39;'\n      }[char]));\n    }\n\n    async function getJson(key, fallback) {\n      try {\n        const response = await fetch(`${files[key]}?v=${Date.now()}`, { cache: 'no-store' });\n        if (!response.ok) throw new Error(`${response.status}`);\n        const value = await response.json();\n        sourceStatus[key] = { ok: true, status: response.status, loadedAt: new Date().toISOString() };\n        return value;\n      } catch (error) {\n        sourceStatus[key] = { ok: false, error: error.message || 'load failed', loadedAt: new Date().toISOString() };\n        return fallback;\n      }\n    }\n\n    function show(section) {\n      document.querySelectorAll('.section').forEach((el) => el.classList.add('hidden'));\n      document.querySelectorAll('nav button').forEach((el) => el.classList.remove('active'));\n      document.getElementById(section)?.classList.remove('hidden');\n      document.querySelector(`nav button[data-section=\"${section}\"]`)?.classList.add('active');\n    }\n\n    function row(title, sub, badge, tone) {\n      return `<div class=\"row\">\n        <div class=\"dot ${tone || 'gold'}\"></div>\n        <div><div class=\"row-title\">${esc(title)}</div><div class=\"row-sub\">${esc(sub || '')}</div></div>\n        ${badge ? `<span class=\"badge ${tone || ''}\">${esc(badge)}</span>` : ''}\n      </div>`;\n    }\n\n    function asArray(value) {\n      return Array.isArray(value) ? value : [];\n    }\n\n    function farmItems() {\n      return asArray(farmState.catalog.categories).flatMap((category) =>\n        asArray(category.items).map((item) => ({\n          ...item,\n          category_id: category.id,\n          category_name: category.name\n        }))\n      );\n    }\n\n    function inventoryItems() {\n      return asArray(farmState.inventory.items);\n    }\n\n    function farmApiHeaders() {\n      const headers = { 'Content-Type': 'application/json' };\n      const token = sessionStorage.getItem('omega_sentry_token');\n      if (token) headers['X-Sentry-Token'] = token;\n      return headers;\n    }\n\n    async function farmApi(path, options = {}) {\n      const response = await fetch(path, {\n        cache: 'no-store',\n        ...options,\n        headers: {\n          ...(options.body ? farmApiHeaders() : {}),\n          ...(options.headers || {})\n        }\n      });\n      const payload = await response.json().catch(() => ({}));\n      if (!response.ok) {\n        const message = payload?.error?.message || payload?.error || `Request failed: ${response.status}`;\n        throw new Error(message);\n      }\n      return payload;\n    }\n\n    function showFarmMessage(message, tone = 'gold') {\n      const box = document.getElementById('farm-inventory-summary');\n      box.textContent = message;\n      box.classList.remove('hidden');\n      box.style.borderColor = tone === 'red' ? 'rgba(255, 61, 61, 0.34)' : 'rgba(212, 175, 55, 0.34)';\n    }\n\n    function renderFarmSelect() {\n      const select = document.getElementById('farm-catalog-select');\n      if (!select) return;\n      const grouped = asArray(farmState.catalog.categories).map((category) => {\n        const options = asArray(category.items).map((item) =>\n          `<option value=\"${esc(item.id)}\" data-category=\"${esc(category.id)}\">${esc(item.name)}</option>`\n        ).join('');\n        return `<optgroup label=\"${esc(category.name)}\">${options}</optgroup>`;\n      }).join('');\n      select.innerHTML = `<option value=\"\">Custom item</option>${grouped}`;\n    }\n\n    function renderFarmLibrary() {\n      const list = document.getElementById('farm-library-list');\n      const categorySelect = document.getElementById('farm-library-category');\n      if (!list || !categorySelect) return;\n      const currentCategory = categorySelect.value;\n      const search = document.getElementById('farm-library-search').value.toLowerCase().trim();\n\n      categorySelect.innerHTML = '<option value=\"\">All categories</option>' + asArray(farmState.catalog.categories).map((category) =>\n        `<option value=\"${esc(category.id)}\">${esc(category.name)}</option>`\n      ).join('');\n      categorySelect.value = currentCategory;\n\n      const matches = farmItems().filter((item) => {\n        const haystack = `${item.name} ${item.category_name} ${asArray(item.common_varieties).join(' ')} ${item.notes || ''}`.toLowerCase();\n        return (!currentCategory || item.category_id === currentCategory) && (!search || haystack.includes(search));\n      });\n\n      list.innerHTML = matches.slice(0, 24).map((item) => {\n        const varieties = asArray(item.common_varieties).slice(0, 4).join(', ');\n        return `<article class=\"farm-card\">\n          <div class=\"farm-card-head\">\n            <div>\n              <div class=\"row-title\">${esc(item.name)}</div>\n              <div class=\"row-sub\">${esc(item.category_name)}${item.season ? ` / ${esc(item.season)}` : ''}</div>\n            </div>\n            <button type=\"button\" data-farm-action=\"use-catalog\" data-catalog-id=\"${esc(item.id)}\">Use</button>\n          </div>\n          <div class=\"farm-meta\">\n            ${item.days_to_harvest ? `<span class=\"badge\">${esc(item.days_to_harvest)}</span>` : ''}\n            ${item.spacing ? `<span class=\"badge\">${esc(item.spacing)}</span>` : ''}\n            ${item.production ? `<span class=\"badge\">${esc(item.production)}</span>` : ''}\n          </div>\n          <div class=\"row-sub\" style=\"margin-top:8px\">${esc(varieties || item.notes || '')}</div>\n        </article>`;\n      }).join('') || row('No library matches', 'Try a broader search or add a custom item.', 'CUSTOM', 'amber');\n    }\n\n    function useCatalogItem(catalogId) {\n      const item = farmItems().find((candidate) => candidate.id === catalogId);\n      if (!item) return;\n      document.getElementById('farm-catalog-select').value = item.id;\n      document.getElementById('farm-name').value = item.name;\n      document.getElementById('farm-type').value = item.type || 'plant';\n      document.getElementById('farm-unit').value = item.type === 'animal' ? 'head' : item.type === 'supply' ? 'bags' : 'each';\n      document.getElementById('farm-tags').value = item.category_name;\n      document.getElementById('farm-notes').value = item.notes || '';\n    }\n\n    function farmPayloadFromForm() {\n      const form = document.getElementById('farm-form');\n      const data = new FormData(form);\n      const selected = farmItems().find((item) => item.id === data.get('catalog_id'));\n      return {\n        catalog_id: String(data.get('catalog_id') || ''),\n        category_id: selected?.category_id || '',\n        name: String(data.get('name') || ''),\n        variety: String(data.get('variety') || ''),\n        type: String(data.get('type') || 'plant'),\n        status: String(data.get('status') || 'planned'),\n        quantity: String(data.get('quantity') || '0'),\n        unit: String(data.get('unit') || 'each'),\n        location: String(data.get('location') || ''),\n        source: String(data.get('source') || ''),\n        planted_date: String(data.get('planted_date') || ''),\n        expected_harvest_date: String(data.get('expected_harvest_date') || ''),\n        tags: String(data.get('tags') || ''),\n        notes: String(data.get('notes') || '')\n      };\n    }\n\n    function fileToDataUrl(file) {\n      return new Promise((resolve, reject) => {\n        const reader = new FileReader();\n        reader.onload = () => resolve(reader.result);\n        reader.onerror = () => reject(reader.error || new Error('Photo read failed'));\n        reader.readAsDataURL(file);\n      });\n    }\n\n    async function refreshFarm() {\n      const [catalog, inventory] = await Promise.all([\n        farmApi('/api/farm/catalog'),\n        farmApi('/api/farm/inventory')\n      ]);\n      farmState.catalog = catalog;\n      farmState.inventory = inventory;\n      renderFarmSelect();\n      renderFarmLibrary();\n      renderFarmInventory();\n    }\n\n    async function addFarmItem(event) {\n      event.preventDefault();\n      const button = event.submitter || document.querySelector('#farm-form button[type=\"submit\"]');\n      button.disabled = true;\n      try {\n        const created = await farmApi('/api/farm/inventory', {\n          method: 'POST',\n          body: JSON.stringify(farmPayloadFromForm())\n        });\n        const file = document.getElementById('farm-photo').files[0];\n        if (file) {\n          if (file.size > 5 * 1024 * 1024) throw new Error('Photo must be 5 MB or smaller');\n          const dataUrl = await fileToDataUrl(file);\n          await farmApi(`/api/farm/photos?itemId=${encodeURIComponent(created.data.id)}`, {\n            method: 'POST',\n            body: JSON.stringify({ filename: file.name, contentType: file.type, dataUrl })\n          });\n        }\n        event.target.reset();\n        document.getElementById('farm-quantity').value = '1';\n        document.getElementById('farm-unit').value = 'each';\n        showFarmMessage('Inventory item added.');\n        await refreshFarm();\n      } catch (error) {\n        showFarmMessage(error.message || 'Could not add inventory item', 'red');\n      } finally {\n        button.disabled = false;\n      }\n    }\n\n    async function patchFarmItem(itemId, patch) {\n      const item = inventoryItems().find((candidate) => candidate.id === itemId);\n      if (!item) return;\n      const updated = { ...item, ...patch };\n      try {\n        await farmApi(`/api/farm/inventory/${encodeURIComponent(itemId)}`, {\n          method: 'PATCH',\n          body: JSON.stringify(updated)\n        });\n        await refreshFarm();\n      } catch (error) {\n        showFarmMessage(error.message || 'Could not update inventory item', 'red');\n      }\n    }\n\n    async function deleteFarmItem(itemId) {\n      if (!confirm('Archive is safer for records. Delete this inventory item?')) return;\n      try {\n        await farmApi(`/api/farm/inventory/${encodeURIComponent(itemId)}`, { method: 'DELETE' });\n        showFarmMessage('Inventory item deleted.');\n        await refreshFarm();\n      } catch (error) {\n        showFarmMessage(error.message || 'Could not delete inventory item', 'red');\n      }\n    }\n\n    function renderFarmInventory() {\n      const list = document.getElementById('farm-inventory-list');\n      if (!list) return;\n      const search = document.getElementById('farm-inventory-search').value.toLowerCase().trim();\n      const status = document.getElementById('farm-status-filter').value;\n      const items = inventoryItems().filter((item) => {\n        const haystack = `${item.name} ${item.variety} ${item.location} ${item.notes} ${asArray(item.tags).join(' ')}`.toLowerCase();\n        return (!status || item.status === status) && (!search || haystack.includes(search));\n      });\n      const active = inventoryItems().filter((item) => !['sold', 'archived'].includes(item.status));\n      const plantCount = active.filter((item) => item.type === 'plant').length;\n      const animalCount = active.filter((item) => item.type === 'animal').length;\n      const summary = document.getElementById('farm-inventory-summary');\n      summary.textContent = `${active.length} active records / ${plantCount} plant groups / ${animalCount} animal groups`;\n      summary.classList.remove('hidden');\n\n      list.innerHTML = items.map((item) => {\n        const photos = asArray(item.photos).slice(0, 4).map((photo) =>\n          `<img src=\"${esc(photo.url)}\" alt=\"${esc(item.name)} photo\" loading=\"lazy\">`\n        ).join('');\n        const title = `${item.name}${item.variety ? ` / ${item.variety}` : ''}`;\n        return `<article class=\"farm-card\">\n          <div class=\"farm-card-head\">\n            <div>\n              <div class=\"row-title\">${esc(title)}</div>\n              <div class=\"row-sub\">${esc(item.location || 'No location')} / ${esc(item.type)} / updated ${esc(ageLabel(item.updated_at))}</div>\n            </div>\n            <span class=\"badge ${item.status === 'ready' || item.status === 'growing' ? 'green' : 'gold'}\">${esc(item.status)}</span>\n          </div>\n          <div class=\"farm-meta\">\n            <span class=\"badge\">${esc(item.quantity)} ${esc(item.unit)}</span>\n            ${item.expected_harvest_date ? `<span class=\"badge\">harvest ${esc(item.expected_harvest_date)}</span>` : ''}\n            ${asArray(item.tags).slice(0, 3).map((tag) => `<span class=\"badge\">${esc(tag)}</span>`).join('')}\n          </div>\n          ${item.notes ? `<div class=\"row-sub\" style=\"margin-top:8px\">${esc(item.notes)}</div>` : ''}\n          ${photos ? `<div class=\"thumb-row\">${photos}</div>` : ''}\n          <div class=\"button-row\">\n            <button type=\"button\" data-farm-action=\"status\" data-item-id=\"${esc(item.id)}\" data-status=\"growing\">Growing</button>\n            <button type=\"button\" data-farm-action=\"status\" data-item-id=\"${esc(item.id)}\" data-status=\"ready\">Ready</button>\n            <button type=\"button\" data-farm-action=\"status\" data-item-id=\"${esc(item.id)}\" data-status=\"harvested\">Harvested</button>\n            <button type=\"button\" data-farm-action=\"status\" data-item-id=\"${esc(item.id)}\" data-status=\"sold\">Sold</button>\n            <button type=\"button\" data-farm-action=\"delete\" data-item-id=\"${esc(item.id)}\">Delete</button>\n          </div>\n        </article>`;\n      }).join('') || row('No inventory yet', 'Choose from the library or add a custom crop, animal, supply, tool, or product.', 'ADD', 'amber');\n    }\n\n    function tempText(weather) {\n      const temp = weather?.temp ?? weather?.temperature ?? weather?.current_temp_f;\n      return temp !== undefined && temp !== null ? `${temp}F ${weather.condition || ''}` : '';\n    }\n\n    function ageLabel(timestamp) {\n      if (!timestamp) return 'No timestamp';\n      const time = new Date(timestamp).getTime();\n      if (!Number.isFinite(time)) return 'Timestamp unreadable';\n      const minutes = Math.max(0, Math.round((Date.now() - time) / 60000));\n      if (minutes < 2) return 'just now';\n      if (minutes < 90) return `${minutes}m ago`;\n      const hours = Math.round(minutes / 60);\n      if (hours < 48) return `${hours}h ago`;\n      return `${Math.round(hours / 24)}d ago`;\n    }\n\n    function sourceTimestamp(key, value) {\n      if (key === 'pulse') return value?.timestamp;\n      if (key === 'activity') return value?.last_updated;\n      if (key === 'hydrowise') return value?.last_updated;\n      if (key === 'calendar') return value?.last_updated;\n      if (key === 'grants') return value?.last_updated;\n      return value?.timestamp || value?.last_updated || sourceStatus[key]?.loadedAt;\n    }\n\n    function renderSources() {\n      const cards = Object.keys(files).map((key) => {\n        const status = sourceStatus[key] || {};\n        const value = state[key];\n        const timestamp = sourceTimestamp(key, value);\n        const ok = status.ok !== false;\n        const tone = ok ? 'green' : 'red';\n        const detail = ok ? `${ageLabel(timestamp)} / ${files[key]}` : `${status.error || 'failed'} / ${files[key]}`;\n        return `<div class=\"source-card\">\n          <div class=\"row-title ${tone}\">${esc(key.toUpperCase())}</div>\n          <div class=\"row-sub\">${esc(detail)}</div>\n        </div>`;\n      }).join('');\n      document.getElementById('source-summary').innerHTML = cards;\n      document.getElementById('sources-list').innerHTML = cards;\n    }\n\n    function taskSummary(tasks) {\n      const pending = tasks.filter((task) => String(task.status).toUpperCase() !== 'COMPLETE');\n      const critical = pending.filter((task) => String(task.priority).toUpperCase() === 'CRITICAL');\n      const high = pending.filter((task) => String(task.priority).toUpperCase() === 'HIGH');\n      return { pending, critical, high };\n    }\n\n    function render() {\n      const tasks = asArray(state.tasks);\n      const taskStats = taskSummary(tasks);\n      const pulse = state.pulse || {};\n      const weather = pulse.weather || state.weather?.weather || {};\n      const ecobee = state.ecobee || {};\n      const hydroZones = asArray(state.hydrowise?.zones);\n      const activeZones = hydroZones.filter((zone) => zone.running);\n      const activity = state.activity || {};\n      const agents = Object.entries(activity.agents || {});\n      const activeAgents = agents.filter(([, agent]) => String(agent.status).toUpperCase() === 'ACTIVE');\n      const soil = asArray(state.soil)[asArray(state.soil).length - 1]?.data || null;\n      const health = taskStats.critical.length ? 'Critical tasks' : taskStats.high.length ? 'High priority' : 'Nominal';\n      const healthTone = taskStats.critical.length ? 'red' : taskStats.high.length ? 'amber' : 'green';\n      const weatherLabel = tempText(weather);\n\n      document.getElementById('health-pill').innerHTML = `<span class=\"dot ${healthTone}\"></span><span>${esc(health)}</span>`;\n      document.getElementById('view-label').textContent = `${view.toUpperCase()} view`;\n      document.getElementById('updated').textContent = `Last updated: ${esc(pulse.timestamp || activity.last_updated || new Date().toISOString())}`;\n      document.getElementById('weather').textContent = weatherLabel || (weather.error ? 'Weather stale' : 'Awaiting packet');\n      document.getElementById('task-count').textContent = `${taskStats.pending.length}`;\n      document.getElementById('task-count').className = `value ${taskStats.critical.length ? 'red' : taskStats.high.length ? 'amber' : 'green'}`;\n      document.getElementById('irrigation').textContent = hydroZones.length ? `${activeZones.length}/${hydroZones.length}` : '--';\n      document.getElementById('agent-count').textContent = `${activeAgents.length}/${agents.length}`;\n      document.getElementById('focus').textContent = pulse.focus || 'Protect family time, keep estate systems healthy, and surface only decisions worth attention.';\n\n      document.getElementById('next-actions').innerHTML = taskStats.pending.slice(0, 5).map((task) =>\n        row(task.task || task.title || 'Untitled task', `Status: ${task.status || 'PENDING'}`, task.priority || 'MEDIUM', String(task.priority).toUpperCase() === 'CRITICAL' ? 'red' : 'amber')\n      ).join('') || row('No pending actions', 'The task queue is clear.', 'OK', 'green');\n\n      document.getElementById('devices').innerHTML = [\n        row('Main House Climate', `${ecobee.current_temp_f || '--'}F current / target ${ecobee.target_temp_f || '--'}F / ${ecobee.hvac_mode || 'unknown'}`, ecobee.equipment_status || 'n/a', 'cyan'),\n        row('Hydrowise Controller', `${activeZones.length} active zones out of ${hydroZones.length || 0}`, activeZones.length ? 'RUNNING' : 'IDLE', activeZones.length ? 'green' : 'gold'),\n        row('Dashboard Sync', activity.last_updated || 'No recent activity timestamp', 'TRACKED', 'gold')\n      ].join('');\n\n      document.getElementById('land').innerHTML = [\n        row('Soil Plot', soil ? `pH ${soil.ph}, moisture ${soil.moisture}%, temp ${soil.temperature}F` : 'No current soil packet', 'AGROFORESTRY', soil ? 'green' : 'amber'),\n        row('Weather Radar', state.weather?.radar?.radar_url || 'No radar URL', state.weather?.radar?.status || 'UNKNOWN', 'cyan'),\n        row('Running Zones', activeZones.map((zone) => zone.name).slice(0, 5).join(', ') || 'None', `${activeZones.length} active`, activeZones.length ? 'green' : 'gold')\n      ].join('');\n\n      document.getElementById('tasks-list').innerHTML = tasks.map((task) =>\n        row(`#${task.id} ${task.task || task.title}`, task.status || 'PENDING', task.priority || 'MEDIUM', String(task.priority).toUpperCase() === 'CRITICAL' ? 'red' : String(task.priority).toUpperCase() === 'HIGH' ? 'amber' : 'gold')\n      ).join('');\n\n      document.getElementById('activity-list').innerHTML = (activity.recent_activity || []).map((item) =>\n        row(item.agent || 'agent', item.action || item.details || '', item.status || '', String(item.status).toUpperCase() === 'COMPLETE' ? 'green' : 'gold')\n      ).join('') || row('No activity packet', 'Run the Omega activity agent to refresh.', 'STALE', 'amber');\n\n      document.getElementById('public-summary').innerHTML = [\n        row('Weather', weatherLabel || 'Weather refresh pending', 'PUBLIC', 'cyan'),\n        row('Land Work', 'Agroforestry, soil building, and resilient estate systems in progress.', 'ACTIVE', 'green'),\n        row('Availability', 'Plant sales and venue information can be surfaced here as inventory matures.', 'SOON', 'gold')\n      ].join('');\n\n      const grants = Array.isArray(state.grants?.grants) ? state.grants.grants : Array.isArray(state.grants) ? state.grants : [];\n      document.getElementById('public-opportunities').innerHTML = grants.slice(0, 5).map((grant) =>\n        row(grant.title || grant.name || 'Opportunity', grant.summary || grant.description || grant.url || '', grant.value || grant.deadline || '', 'gold')\n      ).join('') || row('No public opportunities loaded', 'Grant and opportunity scouts can populate this section.', 'PENDING', 'amber');\n\n      const calendar = Array.isArray(state.calendar?.events) ? state.calendar.events : pulse.calendar || [];\n      document.getElementById('family-summary').innerHTML = [\n        row('House Climate', `${ecobee.current_temp_f || '--'}F / ${ecobee.hvac_mode || 'unknown'}`, 'HOME', 'cyan'),\n        row('Today Outside', weatherLabel || 'Weather refresh pending', 'WEATHER', 'green'),\n        row('Garden Signal', soil ? `Soil moisture ${soil.moisture}% on ${soil.plot_id || 'plot'}` : 'Soil agent pending', 'LAND', soil ? 'green' : 'amber')\n      ].join('');\n      document.getElementById('calendar-list').innerHTML = calendar.slice(0, 6).map((event) =>\n        row(event.time || event.date || 'Today', event.event || event.title || 'Scheduled item', event.source || '', 'gold')\n      ).join('') || row('No calendar packet', 'Calendar sync has not produced a public packet yet.', 'PENDING', 'amber');\n\n      renderSources();\n    }\n\n    async function boot() {\n      try {\n        const entries = await Promise.all(Object.keys(files).map(async (key) => [key, await getJson(key, key === 'tasks' ? [] : {})]));\n        entries.forEach(([key, value]) => { state[key] = value; });\n        render();\n        refreshFarm().catch((error) => showFarmMessage(error.message || 'Farm data could not load', 'red'));\n        if (view === 'public') show('public');\n        else if (view === 'family') show('family');\n      } catch (error) {\n        const banner = document.getElementById('error-banner');\n        banner.textContent = `Dashboard render failed: ${error.message || error}`;\n        banner.classList.remove('hidden');\n        document.getElementById('health-pill').innerHTML = '<span class=\"dot red\"></span><span>Render error</span>';\n      }\n    }\n\n    document.querySelectorAll('nav button').forEach((button) => {\n      button.addEventListener('click', () => show(button.dataset.section));\n    });\n\n    document.getElementById('farm-form').addEventListener('submit', addFarmItem);\n    document.getElementById('farm-token-save').addEventListener('click', () => {\n      const token = document.getElementById('farm-token').value.trim();\n      if (token) {\n        sessionStorage.setItem('omega_sentry_token', token);\n        showFarmMessage('Local token set for this browser tab.');\n      } else {\n        sessionStorage.removeItem('omega_sentry_token');\n        showFarmMessage('Local token cleared.');\n      }\n    });\n    document.getElementById('farm-catalog-select').addEventListener('change', (event) => useCatalogItem(event.target.value));\n    document.getElementById('farm-library-search').addEventListener('input', renderFarmLibrary);\n    document.getElementById('farm-library-category').addEventListener('change', renderFarmLibrary);\n    document.getElementById('farm-inventory-search').addEventListener('input', renderFarmInventory);\n    document.getElementById('farm-status-filter').addEventListener('change', renderFarmInventory);\n    document.getElementById('farm').addEventListener('click', (event) => {\n      const button = event.target.closest('button[data-farm-action]');\n      if (!button) return;\n      const action = button.dataset.farmAction;\n      if (action === 'use-catalog') {\n        useCatalogItem(button.dataset.catalogId);\n      }\n      if (action === 'status') {\n        patchFarmItem(button.dataset.itemId, { status: button.dataset.status });\n      }\n      if (action === 'delete') {\n        deleteFarmItem(button.dataset.itemId);\n      }\n    });\n\n    boot();\n  </script>\n</body>\n</html>\n"