<form version="1.1">
  <label>Pulse macOS Fleet Dashboard</label>
  <description>Enterprise performance monitoring for macOS fleet</description>

  <!-- ==================== INPUTS ==================== -->
  <fieldset submitButton="false" autoRun="true">
    <input type="text" token="index_name" searchWhenChanged="true">
      <label>Index</label>
      <default>main</default>
    </input>
    <input type="text" token="sourcetype_name" searchWhenChanged="true">
      <label>Sourcetype</label>
      <default>macos:pulse</default>
    </input>
    <input type="time" token="time_range" searchWhenChanged="true">
      <label>Time Range</label>
      <default>
        <earliest>-4h@h</earliest>
        <latest>now</latest>
      </default>
    </input>
    <input type="dropdown" token="chart_span" searchWhenChanged="true">
      <label>Chart Span</label>
      <choice value="">Auto</choice>
      <choice value="span=2s">2 seconds</choice>
      <choice value="span=10s">10 seconds</choice>
      <choice value="span=30s">30 seconds</choice>
      <choice value="span=1m">1 minute</choice>
      <choice value="span=5m">5 minutes</choice>
      <choice value="span=15m">15 minutes</choice>
      <default></default>
    </input>
    <input type="dropdown" token="watched_process_filter" searchWhenChanged="true">
      <label>Watched Process</label>
      <choice value="*">All Processes</choice>
      <default>*</default>
      <search>
        <query>index=$index_name$ sourcetype="$sourcetype_name$" event_type="watched_process"
| spath
| spath path=watched.process_pattern output=watched.process_pattern
| dedup watched.process_pattern
| table watched.process_pattern
| sort watched.process_pattern</query>
        <earliest>-7d</earliest>
        <latest>now</latest>
      </search>
      <fieldForLabel>watched.process_pattern</fieldForLabel>
      <fieldForValue>watched.process_pattern</fieldForValue>
      <prefix>watched.process_pattern="</prefix>
      <suffix>"</suffix>
    </input>
    <input type="dropdown" token="cpu_threshold_val" searchWhenChanged="true">
      <label>CPU Threshold</label>
      <choice value="70">70%</choice>
      <choice value="80">80%</choice>
      <choice value="90">90%</choice>
      <choice value="95">95%</choice>
      <default>80</default>
    </input>
    <input type="dropdown" token="sustained_span" searchWhenChanged="true">
      <label>Sustained For</label>
      <choice value="10s">Any spike</choice>
      <choice value="1m">1 minute</choice>
      <choice value="5m">5 minutes</choice>
      <choice value="15m">15 minutes</choice>
      <default>10s</default>
    </input>
  </fieldset>

  <!-- ==================== BASE SEARCH ==================== -->
  <!--
    Uses | spath for automatic JSON field extraction, then explicit spath path=
    extractions for memory fields. The blanket | spath has extraction limits
    (extraction_cutoff, max fields) that can drop fields at the end of the JSON.
    Since "memory" is serialized LAST (after processes which expand to 184+ fields),
    it frequently gets dropped. Explicit path= extractions bypass these limits.

    _time is NOT overridden here — Splunk/Cribl should parse it at ingest.
    If charts show incorrect times, add after the spath line:
      | eval _time = strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%3N%z")
  -->
  <!-- Per-host base search (driven by Host Performance Summary dropdown) -->
  <search id="base_host">
    <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$ NOT event_type="watched_process"
| spath
| spath path=memory.mem_active_mb output=memory.mem_active_mb
| spath path=memory.mem_wired_mb output=memory.mem_wired_mb
| spath path=memory.mem_compressed_mb output=memory.mem_compressed_mb
| spath path=memory.swap_used_mb output=memory.swap_used_mb
| spath path=memory.memory_pressure output=memory.memory_pressure
| spath path=cpu.cpu_idle output=cpu.cpu_idle
| spath path=cpu.thermal_state output=cpu.thermal_state
| spath path=cpu.thermal_throttle output=cpu.thermal_throttle
| spath path=cpu.load_1m output=cpu.load_1m
| spath path=cpu.cpu_user output=cpu.cpu_user
| spath path=cpu.cpu_sys output=cpu.cpu_sys
| spath path=disk.disk_free_gb output=disk.disk_free_gb
| spath path=power.battery_percent output=power.battery_percent
| spath path=power.on_ac output=power.on_ac
| eval hostname = mvindex(hostname, 0)
| eval cpu_used = 100 - 'cpu.cpu_idle'
| eval mem_used_gb = ('memory.mem_active_mb' + 'memory.mem_wired_mb' + 'memory.mem_compressed_mb') / 1024</query>
    <earliest>$time_range.earliest$</earliest>
    <latest>$time_range.latest$</latest>
  </search>

  <!-- Fleet-wide base search (ignores host filter so Fleet Overview always shows all devices) -->
  <search id="base_fleet">
    <query>index=$index_name$ sourcetype="$sourcetype_name$" NOT event_type="watched_process"
| spath
| spath path=memory.mem_active_mb output=memory.mem_active_mb
| spath path=memory.mem_wired_mb output=memory.mem_wired_mb
| spath path=memory.mem_compressed_mb output=memory.mem_compressed_mb
| spath path=memory.swap_used_mb output=memory.swap_used_mb
| spath path=memory.memory_pressure output=memory.memory_pressure
| spath path=cpu.cpu_idle output=cpu.cpu_idle
| spath path=cpu.thermal_state output=cpu.thermal_state
| spath path=cpu.thermal_throttle output=cpu.thermal_throttle
| spath path=cpu.load_1m output=cpu.load_1m
| spath path=cpu.cpu_user output=cpu.cpu_user
| spath path=cpu.cpu_sys output=cpu.cpu_sys
| spath path=disk.disk_free_gb output=disk.disk_free_gb
| spath path=power.battery_percent output=power.battery_percent
| spath path=power.on_ac output=power.on_ac
| eval hostname = mvindex(hostname, 0)
| eval cpu_used = 100 - 'cpu.cpu_idle'
| eval mem_used_gb = ('memory.mem_active_mb' + 'memory.mem_wired_mb' + 'memory.mem_compressed_mb') / 1024</query>
    <earliest>$time_range.earliest$</earliest>
    <latest>$time_range.latest$</latest>
  </search>

  <!-- ==================== SECTION 1: FLEET OVERVIEW ==================== -->
  <row>
    <panel>
      <html>
        <h3 style="border-left: 4px solid #1a9cff; padding: 8px 16px; margin: 0;">
          Fleet Overview
        </h3>
      </html>
    </panel>
  </row>

  <row>
    <panel>
      <title>Hosts Reporting</title>
      <single>
        <search base="base_fleet">
          <query>| stats dc(hostname) as total_hosts</query>
        </search>
        <option name="colorMode">block</option>
        <option name="drilldown">none</option>
        <option name="rangeColors">["0x53a051","0x53a051"]</option>
        <option name="underLabel">Active Devices</option>
        <option name="useColors">1</option>
      </single>
    </panel>
    <panel>
      <title>High CPU</title>
      <single>
        <search base="base_fleet">
          <query>| bin _time span=$sustained_span$
| stats min(cpu_used) as min_cpu by hostname _time
| where min_cpu > $cpu_threshold_val$
| stats dc(hostname) as high_cpu_hosts</query>
        </search>
        <option name="colorMode">block</option>
        <option name="drilldown">all</option>
        <option name="rangeColors">["0x53a051","0xf8be34","0xdc4e41"]</option>
        <option name="rangeValues">[0,1]</option>
        <option name="underLabel">Hosts Affected</option>
        <option name="useColors">1</option>
        <drilldown>
          <condition>
            <eval token="show_high_cpu_detail">if(isnotnull($show_high_cpu_detail$), null(), "true")</eval>
            <unset token="high_cpu_host"></unset>
            <unset token="show_mem_pressure_detail"></unset>
            <unset token="show_thermal_detail"></unset>
            <unset token="show_low_disk_detail"></unset>
            <unset token="show_low_battery_detail"></unset>
          </condition>
        </drilldown>
      </single>
    </panel>
    <panel>
      <title>Memory Pressure</title>
      <single>
        <search base="base_fleet">
          <query>| where 'memory.memory_pressure' != "normal"
| stats dc(hostname) as pressure_hosts</query>
        </search>
        <option name="colorMode">block</option>
        <option name="drilldown">all</option>
        <option name="rangeColors">["0x53a051","0xf8be34","0xdc4e41"]</option>
        <option name="rangeValues">[0,1]</option>
        <option name="underLabel">Warning/Critical</option>
        <option name="useColors">1</option>
        <drilldown>
          <condition>
            <eval token="show_mem_pressure_detail">if(isnotnull($show_mem_pressure_detail$), null(), "true")</eval>
            <unset token="show_high_cpu_detail"></unset>
            <unset token="show_thermal_detail"></unset>
            <unset token="show_low_disk_detail"></unset>
            <unset token="show_low_battery_detail"></unset>
          </condition>
        </drilldown>
      </single>
    </panel>
    <panel>
      <title>Thermal Throttling</title>
      <single>
        <search base="base_fleet">
          <query>| where 'cpu.thermal_state' != "nominal"
| stats dc(hostname) as thermal_hosts</query>
        </search>
        <option name="colorMode">block</option>
        <option name="drilldown">all</option>
        <option name="rangeColors">["0x53a051","0xf8be34","0xdc4e41"]</option>
        <option name="rangeValues">[0,1]</option>
        <option name="underLabel">Hosts Throttled</option>
        <option name="useColors">1</option>
        <drilldown>
          <condition>
            <eval token="show_thermal_detail">if(isnotnull($show_thermal_detail$), null(), "true")</eval>
            <unset token="show_high_cpu_detail"></unset>
            <unset token="show_mem_pressure_detail"></unset>
            <unset token="show_low_disk_detail"></unset>
            <unset token="show_low_battery_detail"></unset>
          </condition>
        </drilldown>
      </single>
    </panel>
    <panel>
      <title>Low Disk (&lt;10GB)</title>
      <single>
        <search base="base_fleet">
          <query>| where 'disk.disk_free_gb' &lt; 10
| stats dc(hostname) as low_disk_hosts</query>
        </search>
        <option name="colorMode">block</option>
        <option name="drilldown">all</option>
        <option name="rangeColors">["0x53a051","0xf8be34","0xdc4e41"]</option>
        <option name="rangeValues">[0,1]</option>
        <option name="underLabel">Hosts</option>
        <option name="useColors">1</option>
        <drilldown>
          <condition>
            <eval token="show_low_disk_detail">if(isnotnull($show_low_disk_detail$), null(), "true")</eval>
            <unset token="show_high_cpu_detail"></unset>
            <unset token="show_mem_pressure_detail"></unset>
            <unset token="show_thermal_detail"></unset>
            <unset token="show_low_battery_detail"></unset>
          </condition>
        </drilldown>
      </single>
    </panel>
    <panel>
      <title>Low Battery</title>
      <single>
        <search base="base_fleet">
          <query>| where 'power.battery_percent' &lt; 20 AND 'power.on_ac' = "false"
| stats dc(hostname) as low_battery</query>
        </search>
        <option name="colorMode">block</option>
        <option name="drilldown">all</option>
        <option name="rangeColors">["0x53a051","0xf8be34","0xdc4e41"]</option>
        <option name="rangeValues">[0,1]</option>
        <option name="underLabel">&lt;20% on Battery</option>
        <option name="useColors">1</option>
        <drilldown>
          <condition>
            <eval token="show_low_battery_detail">if(isnotnull($show_low_battery_detail$), null(), "true")</eval>
            <unset token="show_high_cpu_detail"></unset>
            <unset token="show_mem_pressure_detail"></unset>
            <unset token="show_thermal_detail"></unset>
            <unset token="show_low_disk_detail"></unset>
          </condition>
        </drilldown>
      </single>
    </panel>
  </row>

  <!-- High CPU Detail (shown when clicking the High CPU single value) -->
  <row depends="$show_high_cpu_detail$">
    <panel>
      <title>High CPU Detail — Hosts above $cpu_threshold_val$% for $sustained_span$+ (click a host for window detail)</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" NOT event_type="watched_process"
| spath
| spath path=cpu.cpu_idle output=cpu.cpu_idle
| eval hostname = mvindex(hostname, 0)
| eval cpu_used = 100 - 'cpu.cpu_idle'
| spath path=processes.top_cpu_processes{0}.name output=top_process
| bin _time span=$sustained_span$
| eventstats min(cpu_used) as min_cpu by hostname _time
| where min_cpu > $cpu_threshold_val$
| eventstats dc(_time) as episodes avg(cpu_used) as host_avg_cpu max(cpu_used) as host_peak_cpu latest(_time) as last_seen by hostname
| stats count as appearances first(episodes) as episodes first(host_avg_cpu) as avg_cpu first(host_peak_cpu) as peak_cpu first(last_seen) as last_seen by hostname top_process
| sort hostname -appearances
| streamstats count as rank by hostname
| where rank &lt;= 3
| eval proc_info = top_process." (".appearances."x)"
| stats first(episodes) as episodes first(avg_cpu) as avg_cpu first(peak_cpu) as peak_cpu first(last_seen) as last_seen values(proc_info) as top_offenders by hostname
| eval span_sec = case("$sustained_span$"=="10s", 10, "$sustained_span$"=="1m", 60, "$sustained_span$"=="5m", 300, "$sustained_span$"=="15m", 900, 1=1, 60)
| eval total_sec = episodes * span_sec
| eval duration = case(total_sec &lt; 60, total_sec." sec", total_sec &lt; 3600, round(total_sec/60, 0)." min", 1=1, round(total_sec/3600, 1)." hrs")
| eval avg_cpu = round(avg_cpu, 1)
| eval peak_cpu = round(peak_cpu, 1)
| eval last_seen = strftime(last_seen, "%m/%d %I:%M %p")
| eval top_offenders = mvjoin(top_offenders, ", ")
| table hostname duration avg_cpu peak_cpu last_seen top_offenders
| rename hostname as "Host", duration as "Total Duration", avg_cpu as "Avg CPU %", peak_cpu as "Peak CPU %", last_seen as "Last Seen", top_offenders as "Top Offenders"
| sort -"Peak CPU %"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">50</option>
        <option name="drilldown">row</option>
        <format type="color" field="Peak CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
        <drilldown>
          <set token="high_cpu_host">$row.Host$</set>
        </drilldown>
      </table>
    </panel>
  </row>

  <!-- High CPU Host Window Detail (shown when clicking a host in the High CPU Detail table) -->
  <row depends="$high_cpu_host$">
    <panel>
      <title>$high_cpu_host$ — Each $sustained_span$ window where CPU stayed above $cpu_threshold_val$%</title>
      <input type="link" token="high_cpu_host_close">
        <choice value="close">✕ Close</choice>
        <change>
          <condition value="close">
            <unset token="high_cpu_host"></unset>
            <unset token="high_cpu_host_close"></unset>
          </condition>
        </change>
      </input>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" hostname="$high_cpu_host$" NOT event_type="watched_process"
| spath
| spath path=cpu.cpu_idle output=cpu.cpu_idle
| eval hostname = mvindex(hostname, 0)
| eval cpu_used = 100 - 'cpu.cpu_idle'
| spath path=processes.top_cpu_processes{0}.name output=top_process
| spath path=processes.top_cpu_processes{0}.cpu_percent output=top_proc_cpu
| bin _time span=$sustained_span$
| stats min(cpu_used) as min_cpu avg(cpu_used) as avg_cpu max(cpu_used) as peak_cpu values(top_process) as top_processes max(top_proc_cpu) as top_proc_cpu count as samples by _time
| where min_cpu > $cpu_threshold_val$
| eval avg_cpu = round(avg_cpu, 1)
| eval peak_cpu = round(peak_cpu, 1)
| eval min_cpu = round(min_cpu, 1)
| eval top_proc_cpu = round(top_proc_cpu, 1)
| eval top_processes = mvjoin(top_processes, ", ")
| eval window = strftime(_time, "%m/%d %I:%M:%S %p")
| table window samples min_cpu avg_cpu peak_cpu top_processes top_proc_cpu
| rename window as "Window Start", samples as "Samples", min_cpu as "Min CPU %", avg_cpu as "Avg CPU %", peak_cpu as "Peak CPU %", top_processes as "Top Processes", top_proc_cpu as "Top Proc CPU %"
| sort -"Window Start"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">20</option>
        <option name="drilldown">none</option>
        <format type="color" field="Peak CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
        <format type="color" field="Min CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
      </table>
    </panel>
  </row>

  <!-- Memory Pressure Detail (shown when clicking Memory Pressure panel) -->
  <row depends="$show_mem_pressure_detail$">
    <panel>
      <title>Memory Pressure Detail — Affected Hosts (click "Memory Pressure" again to close)</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" NOT event_type="watched_process"
| spath
| spath path=memory.memory_pressure output=memory.memory_pressure
| spath path=memory.mem_active_mb output=memory.mem_active_mb
| spath path=memory.mem_wired_mb output=memory.mem_wired_mb
| spath path=memory.mem_compressed_mb output=memory.mem_compressed_mb
| spath path=memory.swap_used_mb output=memory.swap_used_mb
| eval hostname = mvindex(hostname, 0)
| where 'memory.memory_pressure' != "normal"
| eval mem_used_gb = ('memory.mem_active_mb' + 'memory.mem_wired_mb' + 'memory.mem_compressed_mb') / 1024
| spath path=processes.top_mem_processes{0}.name output=top_process
| stats count as events latest(memory.memory_pressure) as pressure max(mem_used_gb) as peak_mem_gb max(memory.swap_used_mb) as peak_swap_mb latest(_time) as last_seen values(top_process) as top_processes by hostname
| eval peak_mem_gb = round(peak_mem_gb, 1)
| eval peak_swap_mb = round(peak_swap_mb, 0)
| eval last_seen = strftime(last_seen, "%m/%d %I:%M %p")
| eval top_processes = mvjoin(top_processes, ", ")
| table hostname pressure events peak_mem_gb peak_swap_mb last_seen top_processes
| rename hostname as "Host", pressure as "Pressure", events as "Events", peak_mem_gb as "Peak Mem (GB)", peak_swap_mb as "Peak Swap (MB)", last_seen as "Last Seen", top_processes as "Top Processes"
| sort -"Peak Mem (GB)"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">50</option>
        <option name="drilldown">none</option>
        <format type="color" field="Pressure">
          <colorPalette type="map">{"warning":#F8BE34,"critical":#DC4E41}</colorPalette>
        </format>
      </table>
    </panel>
  </row>

  <!-- Thermal Throttling Detail -->
  <row depends="$show_thermal_detail$">
    <panel>
      <title>Thermal Throttling Detail — Affected Hosts (click "Thermal Throttling" again to close)</title>
      <table>
        <search base="base_fleet">
          <query>| where 'cpu.thermal_state' != "nominal"
| stats latest(cpu.thermal_state) as thermal latest(cpu_used) as cpu_used by hostname
| eval cpu_used = round(cpu_used, 1)
| table hostname thermal cpu_used
| rename hostname as "Host", thermal as "Thermal State", cpu_used as "CPU %"
| sort -"CPU %"</query>
        </search>
        <option name="count">20</option>
        <option name="drilldown">none</option>
        <format type="color" field="Thermal State">
          <colorPalette type="map">{"fair":#F8BE34,"serious":#F1813F,"critical":#DC4E41}</colorPalette>
        </format>
      </table>
    </panel>
  </row>

  <!-- Low Disk Detail -->
  <row depends="$show_low_disk_detail$">
    <panel>
      <title>Low Disk Detail — Hosts with &lt;10GB Free (click "Low Disk" again to close)</title>
      <table>
        <search base="base_fleet">
          <query>| where 'disk.disk_free_gb' &lt; 10
| stats latest(disk.disk_free_gb) as disk_free latest(disk.disk_percent_used) as disk_pct by hostname
| eval disk_free = round(disk_free, 1)
| eval disk_pct = round(disk_pct, 1)
| table hostname disk_free disk_pct
| rename hostname as "Host", disk_free as "Free (GB)", disk_pct as "Used %"
| sort disk_free</query>
        </search>
        <option name="count">20</option>
        <option name="drilldown">none</option>
        <format type="color" field="Free (GB)">
          <colorPalette type="minMidMax" maxColor="#53A051" midColor="#F8BE34" minColor="#DC4E41"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="5"></scale>
        </format>
      </table>
    </panel>
  </row>

  <!-- Low Battery Detail -->
  <row depends="$show_low_battery_detail$">
    <panel>
      <title>Low Battery Detail — Hosts &lt;20% on Battery (click "Low Battery" again to close)</title>
      <table>
        <search base="base_fleet">
          <query>| where 'power.battery_percent' &lt; 20 AND 'power.on_ac' = "false"
| stats latest(power.battery_percent) as battery latest(power.battery_state) as state by hostname
| table hostname battery state
| rename hostname as "Host", battery as "Battery %", state as "State"
| sort battery</query>
        </search>
        <option name="count">20</option>
        <option name="drilldown">none</option>
        <format type="color" field="Battery %">
          <colorPalette type="minMidMax" maxColor="#53A051" midColor="#F8BE34" minColor="#DC4E41"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="10"></scale>
        </format>
      </table>
    </panel>
  </row>

  <row>
    <panel>
      <title>Host Performance Summary</title>
      <input type="dropdown" token="summary_host_filter" searchWhenChanged="true">
        <label>Select Host</label>
        <selectFirstChoice>false</selectFirstChoice>
        <choice value="__clear__">— Fleet View (no host) —</choice>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" NOT event_type="watched_process" earliest=-7d | stats count by hostname | fields hostname | sort hostname</query>
        </search>
        <fieldForLabel>hostname</fieldForLabel>
        <fieldForValue>hostname</fieldForValue>
        <prefix>hostname="</prefix>
        <suffix>"</suffix>
        <change>
          <condition value="__clear__">
            <unset token="show_host_detail"></unset>
            <unset token="summary_host_filter"></unset>
          </condition>
          <condition>
            <eval token="show_host_detail">"true"</eval>
          </condition>
        </change>
      </input>
      <table>
        <search base="base_fleet">
          <query>| stats latest(cpu_used) as cpu_used latest(cpu.thermal_state) as thermal latest(memory.memory_pressure) as mem_pressure latest(mem_used_gb) as mem_used_gb latest(memory.swap_used_mb) as swap_mb latest(disk.disk_free_gb) as disk_free_gb latest(power.battery_percent) as battery latest(power.on_ac) as on_ac latest(processes.process_count) as process_count by hostname
| eval cpu_used = round(cpu_used, 1)
| eval mem_used_gb = round(mem_used_gb, 1)
| eval disk_free_gb = round(disk_free_gb, 1)
| eval power = case(isnull(on_ac), "AC (Desktop)", on_ac = "true", "AC", 1=1, "Battery")
| table hostname cpu_used thermal mem_pressure mem_used_gb swap_mb disk_free_gb battery power process_count
| rename hostname as "Host", cpu_used as "CPU %", thermal as "Thermal", mem_pressure as "Mem Pressure", mem_used_gb as "Mem Used (GB)", swap_mb as "Swap (MB)", disk_free_gb as "Disk Free (GB)", battery as "Battery %", power as "Power", process_count as "Processes"
| sort -"CPU %"</query>
        </search>
        <option name="count">20</option>
        <option name="drilldown">row</option>
        <option name="wrap">true</option>
        <format type="color" field="CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
        <format type="color" field="Thermal">
          <colorPalette type="map">{"nominal":#53A051,"fair":#F8BE34,"serious":#F1813F,"critical":#DC4E41}</colorPalette>
        </format>
        <format type="color" field="Mem Pressure">
          <colorPalette type="map">{"normal":#53A051,"warning":#F8BE34,"critical":#DC4E41}</colorPalette>
        </format>
        <format type="color" field="Battery %">
          <colorPalette type="minMidMax" maxColor="#53A051" midColor="#F8BE34" minColor="#DC4E41"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
        <drilldown>
          <set token="summary_host_filter">hostname="$row.Host$"</set>
          <eval token="show_host_detail">"true"</eval>
        </drilldown>
      </table>
    </panel>
  </row>

  <!-- Message shown when no specific host is selected -->
  <row rejects="$show_host_detail$">
    <panel>
      <html>
        <p style="text-align: center; color: #999; padding: 40px 20px; font-size: 14px;">
          Select a host from the dropdown above to view detailed charts, processes, and watched process data.
        </p>
      </html>
    </panel>
  </row>

  <!-- ==================== SECTION 2: CPU & MEMORY ==================== -->
  <row depends="$show_host_detail$">
    <panel>
      <html>
        <h3 style="border-left: 4px solid #1a9cff; padding: 8px 16px; margin: 0;">
          CPU &amp; Memory
        </h3>
      </html>
    </panel>
  </row>

  <row depends="$show_host_detail$">
    <panel>
      <title>CPU Usage Trend</title>
      <chart>
        <search base="base_host">
          <query>| eval cpu_user_sys = 'cpu.cpu_user' + 'cpu.cpu_sys'
| timechart $chart_span$ avg(cpu_user_sys) as "Avg CPU %" max(cpu_user_sys) as "Peak CPU %" avg(cpu.cpu_user) as "User %" avg(cpu.cpu_sys) as "System %"</query>
        </search>
        <option name="charting.chart">line</option>
        <option name="charting.chart.nullValueMode">connect</option>
        <option name="charting.legend.placement">bottom</option>
        <option name="charting.axisTitleX.text">Time</option>
        <option name="charting.axisTitleY.text">%</option>
        <option name="charting.axisY.minimumNumber">0</option>
        <option name="charting.axisY.maximumNumber">100</option>
        <option name="charting.lineWidth">2</option>
        <option name="charting.drilldown">all</option>
        <drilldown>
          <set token="cpu_click_time">$click.value$</set>
          <eval token="cpu_click_display">strftime($click.value$, "%I:%M:%S %p")</eval>
          <unset token="mem_click_time"></unset>
          <unset token="mem_click_display"></unset>
          <unset token="disk_click_time"></unset>
          <unset token="disk_click_display"></unset>
          <unset token="net_click_time"></unset>
          <unset token="net_click_display"></unset>
        </drilldown>
      </chart>
    </panel>
    <panel>
      <title>Memory Usage Trend (GB)</title>
      <chart>
        <search base="base_host">
          <query>| eval active_gb = 'memory.mem_active_mb' / 1024
| eval wired_gb = 'memory.mem_wired_mb' / 1024
| eval compressed_gb = 'memory.mem_compressed_mb' / 1024
| eval swap_gb = 'memory.swap_used_mb' / 1024
| timechart $chart_span$ avg(active_gb) as "Active" avg(wired_gb) as "Wired" avg(compressed_gb) as "Compressed" avg(swap_gb) as "Swap"</query>
        </search>
        <option name="charting.chart">area</option>
        <option name="charting.chart.stackMode">stacked</option>
        <option name="charting.chart.nullValueMode">connect</option>
        <option name="charting.legend.placement">bottom</option>
        <option name="charting.axisTitleX.text">Time</option>
        <option name="charting.axisTitleY.text">GB</option>
        <option name="charting.drilldown">all</option>
        <drilldown>
          <set token="mem_click_time">$click.value$</set>
          <eval token="mem_click_display">strftime($click.value$, "%I:%M:%S %p")</eval>
          <unset token="cpu_click_time"></unset>
          <unset token="cpu_click_display"></unset>
          <unset token="disk_click_time"></unset>
          <unset token="disk_click_display"></unset>
          <unset token="net_click_time"></unset>
          <unset token="net_click_display"></unset>
        </drilldown>
      </chart>
    </panel>
  </row>

  <!-- CPU Drilldown: Top Processes around clicked time (±5 min) -->
  <row depends="$cpu_click_time$">
    <panel>
      <title>Top CPU Processes at $cpu_click_display$</title>
      <input type="link" token="cpu_drilldown_close">
        <choice value="close">✕ Close</choice>
        <change>
          <condition value="close">
            <unset token="cpu_click_time"></unset>
            <unset token="cpu_drilldown_close"></unset>
            <unset token="cpu_click_display"></unset>
          </condition>
        </change>
      </input>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$ NOT event_type="watched_process"
| spath
| spath path=cpu.cpu_idle output=cpu.cpu_idle
| eval hostname = mvindex(hostname, 0)
| eval _click_epoch = $cpu_click_time$
| where _time >= (_click_epoch - 60) AND _time &lt;= (_click_epoch + 60)
| eval cpu_used = 100 - 'cpu.cpu_idle'
| sort -cpu_used
| head 1
| spath path=processes.top_cpu_processes{} output=proc
| mvexpand proc
| spath input=proc
| eval cpu_percent = round(cpu_percent, 1)
| table name pid cpu_percent
| rename name as "Process", pid as "PID", cpu_percent as "CPU %"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">10</option>
        <option name="drilldown">none</option>
        <format type="color" field="Avg CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
      </table>
    </panel>
  </row>

  <!-- Memory Drilldown: Top Processes around clicked time (±5 min) -->
  <row depends="$mem_click_time$">
    <panel>
      <title>Top Memory Processes at $mem_click_display$</title>
      <input type="link" token="mem_drilldown_close">
        <choice value="close">✕ Close</choice>
        <change>
          <condition value="close">
            <unset token="mem_click_time"></unset>
            <unset token="mem_drilldown_close"></unset>
            <unset token="mem_click_display"></unset>
          </condition>
        </change>
      </input>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$ NOT event_type="watched_process"
| spath
| spath path=memory.mem_active_mb output=memory.mem_active_mb
| spath path=memory.mem_wired_mb output=memory.mem_wired_mb
| spath path=memory.mem_compressed_mb output=memory.mem_compressed_mb
| eval hostname = mvindex(hostname, 0)
| eval _click_epoch = $mem_click_time$
| where _time >= (_click_epoch - 60) AND _time &lt;= (_click_epoch + 60)
| eval mem_used_gb = ('memory.mem_active_mb' + 'memory.mem_wired_mb' + 'memory.mem_compressed_mb') / 1024
| sort -mem_used_gb
| head 1
| spath path=processes.top_mem_processes{} output=proc
| mvexpand proc
| spath input=proc
| eval memory_mb = round(memory_mb, 0)
| table name pid memory_mb
| rename name as "Process", pid as "PID", memory_mb as "Memory (MB)"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">10</option>
        <option name="drilldown">none</option>
        <format type="color" field="Avg Mem (MB)">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="500"></scale>
        </format>
      </table>
    </panel>
  </row>

  <!-- ==================== SECTION 3: DISK, NETWORK & GPU ==================== -->
  <row depends="$show_host_detail$">
    <panel>
      <html>
        <h3 style="border-left: 4px solid #1a9cff; padding: 8px 16px; margin: 0;">
          Disk, Network &amp; GPU
        </h3>
      </html>
    </panel>
  </row>

  <row depends="$show_host_detail$">
    <panel>
      <title>Disk I/O (KB/s)</title>
      <chart>
        <search base="base_host">
          <query>| timechart $chart_span$ avg(disk.disk_kb_read_sec) as "Read" avg(disk.disk_kb_write_sec) as "Write"</query>
        </search>
        <option name="charting.chart">area</option>
        <option name="charting.chart.nullValueMode">connect</option>
        <option name="charting.legend.placement">bottom</option>
        <option name="charting.axisTitleY.text">KB/s</option>
        <option name="charting.drilldown">all</option>
        <drilldown>
          <set token="disk_click_time">$click.value$</set>
          <eval token="disk_click_display">strftime($click.value$, "%I:%M:%S %p")</eval>
          <unset token="cpu_click_time"></unset>
          <unset token="cpu_click_display"></unset>
          <unset token="mem_click_time"></unset>
          <unset token="mem_click_display"></unset>
          <unset token="net_click_time"></unset>
          <unset token="net_click_display"></unset>
        </drilldown>
      </chart>
    </panel>
    <panel>
      <title>Network Traffic (MB)</title>
      <chart>
        <search base="base_host">
          <query>| eval net_in_mb = 'network.net_bytes_in' / 1048576
| eval net_out_mb = 'network.net_bytes_out' / 1048576
| timechart $chart_span$ avg(net_in_mb) as "Inbound" avg(net_out_mb) as "Outbound"</query>
        </search>
        <option name="charting.chart">area</option>
        <option name="charting.chart.nullValueMode">connect</option>
        <option name="charting.legend.placement">bottom</option>
        <option name="charting.axisTitleY.text">MB</option>
        <option name="charting.drilldown">all</option>
        <drilldown>
          <set token="net_click_time">$click.value$</set>
          <eval token="net_click_display">strftime($click.value$, "%I:%M:%S %p")</eval>
          <unset token="cpu_click_time"></unset>
          <unset token="cpu_click_display"></unset>
          <unset token="mem_click_time"></unset>
          <unset token="mem_click_display"></unset>
          <unset token="disk_click_time"></unset>
          <unset token="disk_click_display"></unset>
        </drilldown>
      </chart>
    </panel>
  </row>

  <!-- Disk I/O Drilldown: Top Processes around clicked time (±60s) -->
  <row depends="$disk_click_time$">
    <panel>
      <title>Top Disk I/O Processes at $disk_click_display$</title>
      <input type="link" token="disk_drilldown_close">
        <choice value="close">&#10005; Close</choice>
        <change>
          <condition value="close">
            <unset token="disk_click_time"></unset>
            <unset token="disk_drilldown_close"></unset>
            <unset token="disk_click_display"></unset>
          </condition>
        </change>
      </input>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$ NOT event_type="watched_process"
| spath
| eval hostname = mvindex(hostname, 0)
| eval _click_epoch = $disk_click_time$
| where _time >= (_click_epoch - 60) AND _time &lt;= (_click_epoch + 60)
| eval disk_total_kb = 'disk.disk_kb_read_sec' + 'disk.disk_kb_write_sec'
| sort -disk_total_kb
| head 1
| spath path=processes.top_disk_processes{} output=proc
| mvexpand proc
| spath input=proc
| eval read_kb = round(read_bytes_per_sec / 1024, 1)
| eval write_kb = round(write_bytes_per_sec / 1024, 1)
| eval total_kb = round((read_bytes_per_sec + write_bytes_per_sec) / 1024, 1)
| table name pid read_kb write_kb total_kb
| rename name as "Process", pid as "PID", read_kb as "Read (KB/s)", write_kb as "Write (KB/s)", total_kb as "Total (KB/s)"
| sort -"Total (KB/s)"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">10</option>
        <option name="drilldown">none</option>
        <format type="color" field="Total (KB/s)">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="500"></scale>
        </format>
      </table>
    </panel>
  </row>

  <!-- Network Drilldown: Top Processes around clicked time (±60s) -->
  <row depends="$net_click_time$">
    <panel>
      <title>Top Network Processes at $net_click_display$</title>
      <input type="link" token="net_drilldown_close">
        <choice value="close">&#10005; Close</choice>
        <change>
          <condition value="close">
            <unset token="net_click_time"></unset>
            <unset token="net_drilldown_close"></unset>
            <unset token="net_click_display"></unset>
          </condition>
        </change>
      </input>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$ NOT event_type="watched_process"
| spath
| eval hostname = mvindex(hostname, 0)
| eval _click_epoch = $net_click_time$
| where _time >= (_click_epoch - 60) AND _time &lt;= (_click_epoch + 60)
| eval net_total = 'network.net_bytes_in' + 'network.net_bytes_out'
| sort -net_total
| head 1
| spath path=processes.top_net_processes{} output=proc
| mvexpand proc
| spath input=proc
| eval in_kb = round(bytes_in / 1024, 1)
| eval out_kb = round(bytes_out / 1024, 1)
| eval total_kb = round((bytes_in + bytes_out) / 1024, 1)
| table name pid in_kb out_kb total_kb
| rename name as "Process", pid as "PID", in_kb as "In (KB)", out_kb as "Out (KB)", total_kb as "Total (KB)"
| sort -"Total (KB)"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">10</option>
        <option name="drilldown">none</option>
        <format type="color" field="Total (KB)">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="100"></scale>
        </format>
      </table>
    </panel>
  </row>

  <row depends="$show_host_detail$">
    <panel>
      <title>GPU Utilization Trend</title>
      <chart>
        <search base="base_host">
          <query>| timechart $chart_span$ avg(gpu.gpu_utilization_percent) as "GPU %" avg(gpu.gpu_renderer_percent) as "Renderer %" avg(gpu.gpu_tiler_percent) as "Tiler %"</query>
        </search>
        <option name="charting.chart">line</option>
        <option name="charting.chart.nullValueMode">connect</option>
        <option name="charting.legend.placement">bottom</option>
        <option name="charting.axisTitleY.text">%</option>
        <option name="charting.axisY.minimumNumber">0</option>
        <option name="charting.axisY.maximumNumber">100</option>
        <option name="charting.lineWidth">2</option>
        <option name="charting.drilldown">none</option>
      </chart>
    </panel>
    <panel>
      <title>Battery Level Trend</title>
      <chart>
        <search base="base_host">
          <query>| timechart $chart_span$ avg(power.battery_percent) as "Battery %"</query>
        </search>
        <option name="charting.chart">line</option>
        <option name="charting.chart.nullValueMode">connect</option>
        <option name="charting.legend.placement">none</option>
        <option name="charting.axisTitleY.text">%</option>
        <option name="charting.axisY.minimumNumber">0</option>
        <option name="charting.axisY.maximumNumber">100</option>
        <option name="charting.lineWidth">2</option>
        <option name="charting.drilldown">none</option>
      </chart>
    </panel>
  </row>

  <!-- ==================== SECTION 4: TOP PROCESSES ==================== -->
  <row depends="$show_host_detail$">
    <panel>
      <html>
        <h3 style="border-left: 4px solid #1a9cff; padding: 8px 16px; margin: 0;">
          Top Processes
        </h3>
      </html>
    </panel>
  </row>

  <row depends="$show_host_detail$">
    <panel>
      <title>Top CPU Processes (Sustained Activity)</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$ NOT event_type="watched_process"
| spath
| eval hostname = mvindex(hostname, 0)
| eventstats dc(_time) as total_events
| spath path=processes.top_cpu_processes{} output=proc
| mvexpand proc
| spath input=proc
| stats avg(cpu_percent) as avg_cpu max(cpu_percent) as peak_cpu dc(_time) as unique_samples values(pid) as pids values(ppid) as parent_pids first(total_events) as total_events by name
| eval presence = round(unique_samples / total_events * 100, 1)
| eval impact = case(
    presence >= 50 AND avg_cpu >= 20, "Sustained",
    presence >= 15, "Recurring",
    1=1, "Spike")
| eval impact_score = avg_cpu * presence / 100
| sort -impact_score
| head 10
| eval avg_cpu = round(avg_cpu, 1)
| eval peak_cpu = round(peak_cpu, 1)
| table name pids parent_pids avg_cpu peak_cpu presence impact
| rename name as "Process", pids as "PIDs", parent_pids as "Parent PIDs", avg_cpu as "Avg CPU %", peak_cpu as "Peak CPU %", presence as "Presence %", impact as "Impact"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">10</option>
        <option name="drilldown">none</option>
        <option name="wrap">true</option>
        <format type="color" field="Avg CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="percent" midValue="50"></scale>
        </format>
        <format type="color" field="Impact">
          <colorPalette type="map">{"Sustained":#DC4E41,"Recurring":#F8BE34,"Spike":#53A051}</colorPalette>
        </format>
      </table>
    </panel>
    <panel>
      <title>Top Memory Processes (Sustained Activity)</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$ NOT event_type="watched_process"
| spath
| eval hostname = mvindex(hostname, 0)
| eventstats dc(_time) as total_events
| spath path=processes.top_mem_processes{} output=proc
| mvexpand proc
| spath input=proc
| stats avg(memory_mb) as avg_mem max(memory_mb) as peak_mem dc(_time) as unique_samples values(pid) as pids values(ppid) as parent_pids first(total_events) as total_events by name
| eval presence = round(unique_samples / total_events * 100, 1)
| eval impact = case(
    presence >= 50 AND avg_mem >= 200, "Sustained",
    presence >= 15, "Recurring",
    1=1, "Spike")
| eval impact_score = avg_mem * presence / 100
| sort -impact_score
| head 10
| eval avg_mem = round(avg_mem, 0)
| eval peak_mem = round(peak_mem, 0)
| table name pids parent_pids avg_mem peak_mem presence impact
| rename name as "Process", pids as "PIDs", parent_pids as "Parent PIDs", avg_mem as "Avg Mem (MB)", peak_mem as "Peak Mem (MB)", presence as "Presence %", impact as "Impact"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">10</option>
        <option name="drilldown">none</option>
        <option name="wrap">true</option>
        <format type="color" field="Avg Mem (MB)">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="percent" midValue="50"></scale>
        </format>
        <format type="color" field="Impact">
          <colorPalette type="map">{"Sustained":#DC4E41,"Recurring":#F8BE34,"Spike":#53A051}</colorPalette>
        </format>
      </table>
    </panel>
  </row>

  <row depends="$show_host_detail$">
    <panel>
      <title>Top CPU Consumers During High CPU Events</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$ NOT event_type="watched_process"
| spath
| spath path=cpu.cpu_idle output=cpu.cpu_idle
| eval hostname = mvindex(hostname, 0)
| eval cpu_used = 100 - 'cpu.cpu_idle'
| where cpu_used > $cpu_threshold_val$
| spath path=processes.top_cpu_processes{} output=proc
| mvexpand proc
| spath input=proc
| stats count as occurrences avg(cpu_percent) as avg_cpu max(cpu_percent) as peak_cpu by name
| sort -occurrences
| head 5
| eval avg_cpu = round(avg_cpu, 1)
| eval peak_cpu = round(peak_cpu, 1)
| table name occurrences avg_cpu peak_cpu
| rename name as "Process", occurrences as "Events", avg_cpu as "Avg CPU %", peak_cpu as "Peak CPU %"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">5</option>
        <option name="drilldown">none</option>
        <format type="color" field="Avg CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
      </table>
    </panel>
    <panel>
      <title>Top Memory Consumers During Pressure Events</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" $summary_host_filter$
| spath
| spath path=memory.memory_pressure output=memory.memory_pressure
| eval hostname = mvindex(hostname, 0)
| where 'memory.memory_pressure' != "normal"
| spath path=processes.top_mem_processes{} output=proc
| mvexpand proc
| spath input=proc
| stats count as occurrences avg(memory_mb) as avg_mem max(memory_mb) as peak_mem by name
| sort -occurrences
| head 5
| eval avg_mem = round(avg_mem, 0)
| eval peak_mem = round(peak_mem, 0)
| table name occurrences avg_mem peak_mem
| rename name as "Process", occurrences as "Events", avg_mem as "Avg Mem (MB)", peak_mem as "Peak Mem (MB)"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">5</option>
        <option name="drilldown">none</option>
        <format type="color" field="Avg Mem (MB)">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="500"></scale>
        </format>
      </table>
    </panel>
  </row>

  <!-- ==================== SECTION 5: WATCHED PROCESSES ==================== -->
  <row depends="$show_host_detail$">
    <panel>
      <html>
        <h3 style="border-left: 4px solid #1a9cff; padding: 8px 16px; margin: 0;">
          Watched Processes
        </h3>
      </html>
    </panel>
  </row>

  <row depends="$show_host_detail$">
    <panel>
      <title>Watched Processes (Current Status)</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" event_type="watched_process" $summary_host_filter$
| spath
| eval hostname = mvindex(hostname, 0)
| search $watched_process_filter$
| stats latest(watched.cpu_percent) as cpu_percent latest(watched.memory_mb) as memory_mb latest(watched.process_count) as process_count by watched.process_pattern, hostname
| eval cpu_percent = round(cpu_percent, 1)
| eval memory_mb = round(memory_mb, 1)
| table hostname watched.process_pattern process_count cpu_percent memory_mb
| rename watched.process_pattern as "Pattern", hostname as "Host", process_count as "Instances", cpu_percent as "CPU %", memory_mb as "Memory (MB)"
| sort -cpu_percent</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">10</option>
        <option name="drilldown">none</option>
        <format type="color" field="CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
      </table>
    </panel>
    <panel>
      <title>Watched Process CPU Trend</title>
      <chart>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" event_type="watched_process" $summary_host_filter$
| spath
| search $watched_process_filter$
| timechart $chart_span$ avg(watched.cpu_percent) by watched.process_pattern</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="charting.chart">line</option>
        <option name="charting.chart.nullValueMode">connect</option>
        <option name="charting.legend.placement">bottom</option>
        <option name="charting.axisTitleY.text">CPU %</option>
        <option name="charting.lineWidth">2</option>
        <option name="charting.drilldown">none</option>
      </chart>
    </panel>
    <panel>
      <title>Watched Process Memory Trend</title>
      <chart>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" event_type="watched_process" $summary_host_filter$
| spath
| search $watched_process_filter$
| timechart $chart_span$ avg(watched.memory_mb) by watched.process_pattern</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="charting.chart">line</option>
        <option name="charting.chart.nullValueMode">connect</option>
        <option name="charting.legend.placement">bottom</option>
        <option name="charting.axisTitleY.text">Memory (MB)</option>
        <option name="charting.lineWidth">2</option>
        <option name="charting.drilldown">none</option>
      </chart>
    </panel>
  </row>

  <!-- ==================== SECTION 6: INCIDENTS & FLEET INFO ==================== -->
  <row>
    <panel>
      <html>
        <h3 style="border-left: 4px solid #1a9cff; padding: 8px 16px; margin: 0;">
          Incidents &amp; Fleet Info
        </h3>
      </html>
    </panel>
  </row>

  <row>
    <panel>
      <title>High CPU Events — Above $cpu_threshold_val$% sustained for $sustained_span$+</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" NOT event_type="watched_process"
| spath
| spath path=cpu.cpu_idle output=cpu.cpu_idle
| spath path=cpu.thermal_state output=cpu.thermal_state
| eval hostname = mvindex(hostname, 0)
| eval cpu_used = 100 - 'cpu.cpu_idle'
| spath path=processes.top_cpu_processes{0}.name output=top_process
| bin _time span=$sustained_span$
| eventstats min(cpu_used) as min_cpu by hostname _time
| where min_cpu > $cpu_threshold_val$
| eventstats dc(_time) as episodes avg(cpu_used) as host_avg_cpu max(cpu_used) as host_peak_cpu latest(cpu.thermal_state) as host_thermal latest(_time) as last_seen by hostname
| stats count as appearances first(episodes) as episodes first(host_avg_cpu) as avg_cpu first(host_peak_cpu) as peak_cpu first(host_thermal) as thermal first(last_seen) as last_seen by hostname top_process
| sort hostname -appearances
| streamstats count as rank by hostname
| where rank &lt;= 3
| eval proc_info = top_process." (".appearances."x)"
| stats first(episodes) as episodes first(avg_cpu) as avg_cpu first(peak_cpu) as peak_cpu first(thermal) as thermal first(last_seen) as last_seen values(proc_info) as top_offenders by hostname
| eval span_sec = case("$sustained_span$"=="10s", 10, "$sustained_span$"=="1m", 60, "$sustained_span$"=="5m", 300, "$sustained_span$"=="15m", 900, 1=1, 60)
| eval total_sec = episodes * span_sec
| eval duration = case(total_sec &lt; 60, total_sec." sec", total_sec &lt; 3600, round(total_sec/60, 0)." min", 1=1, round(total_sec/3600, 1)." hrs")
| eval avg_cpu = round(avg_cpu, 1)
| eval peak_cpu = round(peak_cpu, 1)
| eval last_seen = strftime(last_seen, "%m/%d %I:%M %p")
| eval top_offenders = mvjoin(top_offenders, ", ")
| table hostname duration avg_cpu peak_cpu thermal last_seen top_offenders
| rename hostname as "Host", duration as "Total Duration", avg_cpu as "Avg CPU %", peak_cpu as "Peak CPU %", thermal as "Thermal", last_seen as "Last Seen", top_offenders as "Top Offenders"
| sort -"Peak CPU %"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">50</option>
        <option name="drilldown">none</option>
        <format type="color" field="Peak CPU %">
          <colorPalette type="minMidMax" maxColor="#DC4E41" midColor="#F8BE34" minColor="#53A051"></colorPalette>
          <scale type="minMidMax" midType="number" midValue="50"></scale>
        </format>
        <format type="color" field="Thermal">
          <colorPalette type="map">{"nominal":#53A051,"fair":#F8BE34,"serious":#F1813F,"critical":#DC4E41}</colorPalette>
        </format>
      </table>
    </panel>
    <panel>
      <title>Memory Pressure Events (Warning/Critical)</title>
      <table>
        <search>
          <query>index=$index_name$ sourcetype="$sourcetype_name$" NOT event_type="watched_process"
| spath
| spath path=memory.memory_pressure output=memory.memory_pressure
| spath path=memory.mem_active_mb output=memory.mem_active_mb
| spath path=memory.mem_wired_mb output=memory.mem_wired_mb
| spath path=memory.mem_compressed_mb output=memory.mem_compressed_mb
| spath path=memory.swap_used_mb output=memory.swap_used_mb
| eval hostname = mvindex(hostname, 0)
| where 'memory.memory_pressure' != "normal"
| eval mem_used_gb = ('memory.mem_active_mb' + 'memory.mem_wired_mb' + 'memory.mem_compressed_mb') / 1024
| spath path=processes.top_mem_processes{0}.name output=top_process
| stats count as events latest(memory.memory_pressure) as pressure max(mem_used_gb) as peak_mem_gb max(memory.swap_used_mb) as peak_swap_mb latest(_time) as last_seen values(top_process) as top_processes by hostname
| eval peak_mem_gb = round(peak_mem_gb, 1)
| eval peak_swap_mb = round(peak_swap_mb, 0)
| eval last_seen = strftime(last_seen, "%m/%d %I:%M %p")
| eval top_processes = mvjoin(top_processes, ", ")
| table hostname pressure events peak_mem_gb peak_swap_mb last_seen top_processes
| rename hostname as "Host", pressure as "Pressure", events as "Events", peak_mem_gb as "Peak Mem (GB)", peak_swap_mb as "Peak Swap (MB)", last_seen as "Last Seen", top_processes as "Top Processes"
| sort -"Peak Mem (GB)"</query>
          <earliest>$time_range.earliest$</earliest>
          <latest>$time_range.latest$</latest>
        </search>
        <option name="count">50</option>
        <option name="drilldown">none</option>
        <format type="color" field="Pressure">
          <colorPalette type="map">{"warning":#F8BE34,"critical":#DC4E41}</colorPalette>
        </format>
      </table>
    </panel>
  </row>

  <row>
    <panel>
      <title>Power Status</title>
      <chart>
        <search base="base_fleet">
          <query>| stats latest(power.on_ac) as on_ac by hostname
| eval power_status = case(isnull(on_ac), "Desktop (AC)", on_ac = "true", "On AC Power", 1=1, "On Battery")
| stats count by power_status</query>
        </search>
        <option name="charting.chart">pie</option>
      </chart>
    </panel>
    <panel>
      <title>macOS Version</title>
      <chart>
        <search base="base_fleet">
          <query>| stats latest(system.macos_version) as version by hostname
| stats count by version
| rename version as "Version"</query>
        </search>
        <option name="charting.chart">pie</option>
      </chart>
    </panel>
    <panel>
      <title>Chip Type</title>
      <chart>
        <search base="base_fleet">
          <query>| stats latest(system.chip_description) as chip by hostname
| stats count by chip
| rename chip as "Chip"</query>
        </search>
        <option name="charting.chart">pie</option>
      </chart>
    </panel>
  </row>

</form>
