Commit Graph
632 Commits
Author SHA1 Message Date
Gmer4Lfe 26693fd11f Add play_state_sync.sh — sync watched state across Emby + Jellyfin
Syncs played/resume state bidirectionally across all configured Emby
and Jellyfin servers. Newest LastPlayedDate wins; users matched by
name (case-insensitive); unmatched users skipped cleanly.

Matching: IMDb/TMDB for movies, TVDB+s/e for episodes, MusicBrainz
track ID for audio. Also syncs resume positions for partial plays.

Config: PLAY_SYNC_ENABLED, PLAY_SYNC_DAYS (default 90), PLAY_SYNC_TYPES.
Modes: --dry-run, --status, --full (ignore day window), --log.
Wired into DAILY_MAINTENANCE_SCRIPTS after arr cleanup.
2026-05-30 00:02:18 -04:00
Gmer4Lfe ce9748096c Scheduler: extra args input on script rows
- Tools and Custom: args input always visible (translucent at rest, full on hover)
- Orch children: args input hidden when orch is on, shown when orch is off
  (same toggle as the cron field — orch off = standalone mode)
- vvRunJob/vvDryRun: pick up .vv-script-args value, POST as extra_args
- run.php / dryrun.php: accept extra_args, validate against shell metacharacters,
  append to run_job.sh invocation (flows through SCRIPT_ARGS to the script)
- CSS: .vv-script-args — 30% opacity at rest, 100% on hover/focus
2026-05-29 23:56:15 -04:00
Gmer4Lfe d2eb060fe5 docker_prune_images: add --all mode for full orphan cleanup
Default still removes only dangling (untagged) images.
--all removes stopped containers first, then all images not used
by any running container — clears tagged orphan images left behind
by removed or stopped apps.
2026-05-29 23:36:59 -04:00
Gmer4Lfe 86048b32d3 Plugin: common.php library, watchdog expansion, monitor + scheduler improvements
PHP architecture:
- Extract common.php from monitor.php — shared system functions (vv_system_info,
  vv_memory_breakdown, vv_remote_hosts_stats, disk/GPU/UPS/network/docker/parity, etc.)
  now live in one place; monitor.php and watchdog.php both require common.php
- Add unraid_api.php as explicit include (was implicit via config.php chain)
- confform.php: add missing require_once config.php (implicit dep made explicit)
- Delete orphaned pages/docs.php and pages/config.php (absorbed into scheduler)

Watchdog page:
- Add Storage watchdog card (growth + log strikes, baseline age, suppress ceilings)
- Add Network watchdog card (NPM strikes, DDNS domain/container, NPM URL)
- One host per row layout — all 5 watchdog cards equally spaced via inner grid
- Watchdog now uses Unraid API for local system stats; remote nodes with API key
  but no SSH get system info from vv_remote_hosts_stats() with api_only flag
- SSH bundle: /proc/meminfo passed as raw section instead of awk-parsed header
  fields — fixes RAM showing 0 on remote hosts where awk quoting was unreliable
- vv_wd_local_system() rewritten as thin wrapper over vv_system_info() + vv_memory_breakdown()

Monitor page:
- Watchdog card: add stability strikes, storage watchdog strikes, network NPM status,
  live system stats (rootfs/log/tmp %, RAM free, load, CPU temp, zombies, NIC, sshd)
- Row height: switch from max-height on cards to minmax(0, calc(...)) on grid track —
  all cards in a row now fill to the tallest card's height correctly (fixes Pools card
  being shorter than neighbours)

Scheduler page:
- Add Tools section above Custom Scripts — lists Tools/*.sh with run/dry-run/cron/log
- vv_tools_scripts() function in scheduler.php include

Tools:
- Add docker_prune_images.sh — removes dangling Docker images; --dry-run and --status modes
2026-05-29 23:33:52 -04:00
Gmer4Lfe 8f05f0d27c Docker tab: full inspect detail — icon, IP, ports, paths per container
- Replace pill view with per-container rows showing full detail
- docker inspect batch call: networks+IPs, port mappings, bind mounts
- Container icon from template XML, name links to WebUI if configured
- Network IP badges (blue), port mapping badges (yellow)
- Bind mount paths shown src→dst, collapsible expand for >3 paths
- Large folders (>7 containers) auto-span full width
- Status badge: RUNNING/STOPPED/PAUSED/EXITED with colour
- Stopped containers dimmed, icons load from template XMLs
2026-05-28 22:48:06 -04:00
Gmer4Lfe 00a8a64c4e Docker tab: own config file, folder.view3 becomes optional sync target
- Primary store moved to /boot/config/plugins/varaverk/docker_folders.json
- No dependency on folder.view3 plugin being installed
- Auto-imports folder.view3 JSON on first run if it exists (one-time bootstrap)
- Mirrors writes to folder.view3 JSON only if that plugin's directory is present
- Toolbar shows '⇄ folder.view3' badge when sync is active
2026-05-28 22:41:10 -04:00
Gmer4Lfe e95fd30e13 Varaverk: Docker tab — folder management with conf + folder.view3 JSON dual-write
- New Docker tab (between Scheduler and Watchdog)
- Reads /boot/config/plugins/folder.view3/docker.json — fully compatible with folder.view3 plugin
- Writes changes to both docker.json AND HOST*_DOCKER_FOLDER_MAP in host*.conf simultaneously
- Edit mode: rename folders (inline input), delete folders, move containers via popover picker, create new folders
- Drift banner: highlights containers where conf desired state doesn't match json actual state
- Sync conf→JSON: apply conf desired state to json (fixes drift after onboard)
- Sync JSON→conf: capture manual json edits back into conf
- Ungrouped section shows all containers not assigned to any folder
- Onboard scripts can read HOST*_DOCKER_FOLDER_MAP to auto-place new containers
2026-05-28 22:39:44 -04:00
Gmer4Lfe fb051b60c1 Varaverk: FallBack + Watchdog tabs; plugin path restructure to Plugin/unraid/
- FallBack tab: per-node tier inventory + active fallback card with duration, tier, handback strikes, running container status
- Watchdog tab: live system health (RAM bar + thresholds, load, uptime, daemon), docker watchdog strikes + skip list + restart history, stability strikes + reboot log, resource pressure alert card, config inventory (mem limits, required, pause/stop lists)
- Swapped partnership/arrs tab order; FallBack between partnership and watchdog
- Plugin source tree moved from Plugin/usr/local/emhttp/plugins/varaverk/ to Plugin/unraid/
- Deployment/ conf templates added
2026-05-28 22:24:50 -04:00
FailedProxy 64d95aa991 conf edit 2026-05-28 20:59:53 -04:00
Gmer4Lfe 446794e41d Varaverk: grid layout tweaks + mobile folder fix
- Move Parity to row 4 (cols 1-2), Scripts to row 2
- GPU card: span 2
- Memory card: proc strip clips with ellipsis instead of overflowing
- Containers & VMs: use JS window.innerWidth check (<=640) for single-column
  folder layout instead of CSS media query — fixes badge alignment on mobile
  where Unraid's viewport doesn't trigger CSS breakpoints reliably
2026-05-26 19:19:22 -04:00
Gmer4Lfe 072c8b720d Varaverk: monitor grid overhaul + Containers & VMs card
Monitor layout (8-col grid):
- Row 1: System | Power | CPU | Memory | Network
- Row 2: Scripts | Partner & Fallback (span 3) | Containers & VMs (span 4)
- Row 3: GPU (span 2) | Transcode (span 2) | Streams (span 4)
- Row 4: Parity (cols 1-2) | Pools (cols 3-4) | Array (cols 5-8)

New Containers & VMs card:
- Reads FolderView3 folders from docker.json; expand/collapse per folder
- VMs listed first (virsh); containers show start/stop/webui/edit actions
- 2-column balanced layout; collapses to 1 column below 900px
- Docker WebUI URLs resolved from dockerMan template XMLs

New files: include/vms.php, include/docker_folders.php, api/docker_action.php, api/snapshot.php
Responsive: explicit grid-column placements reset at 1024px breakpoint
2026-05-26 18:59:19 -04:00
Gmer4Lfe 6078af0dbb Varaverk: arrange mode, folder management, rsync standalone, layout fixes
Scheduler UI:
- Arrange mode: drag scripts between orchs and reorder within arrays; right
  panel shows unassigned script pool; Save Arrangement commits to master.conf
- + Folder: named collapsible subfolders for Custom Scripts stored in schedule.json
- Rsync children: hide Run/Dry Run/Log/location when orch is ON; show standalone
  location + cron controls when orch is OFF; cron only fires when both filled
- Non-conf-managed children (transcode): toggles now show enabled when orch is on
- Right panel height sync: fix ResizeObserver feedback loop via align-self:flex-start
  on left panel and left.offsetHeight in vvFitRight
- How do I use this: updated to cover arrange, folders, rsync standalone, transcode

New API endpoints:
- board.php, clearlock.php, movescript.php, rawconf.php, readscript.php
- reorderarray.php, rsync_standalone.php, savefolders.php

run.php / dryrun.php: accept optional --location= arg for standalone rsync calls
2026-05-25 21:46:12 -04:00
Gmer4Lfe 6b30768853 webgui_watchdog: mark executable 2026-05-24 23:33:39 -04:00
Gmer4Lfe 8e0d68665c Move dangling image prune from docker_update.sh to docker_daily_restart.sh
Restarts create the orphans — prune belongs in the same script immediately
after the restart loop, not in the update script that runs before it.
2026-05-24 23:32:00 -04:00
Gmer4Lfe 83ff9fca5e docker_update: prune dangling images after pull (both normal and remainder modes)
Daily restarts create orphaned images that were never cleaned up.
Added docker image prune -f section matching docker_update_remaining.sh pattern.
Prune runs each cycle so orphans from the previous day's restart are removed daily
rather than waiting for the weekly docker_update_remaining.sh sweep.
2026-05-24 23:14:31 -04:00
Gmer4Lfe 83195ebb9e monthly_maintenance: reschedule to 0 0 15 * * (15th at midnight)
Previously ran daily at 3am with internal self-gating. Now scheduled directly
on the 15th — uptime and interval gates still apply; a skip means next month's 15th.
2026-05-24 23:10:31 -04:00
Gmer4Lfe dfb462a18e Varaverk: suggested cron hints left of every cron input
- vv_script_children() now calls vv_script_suggested_cron() per child,
  adding suggested_cron and suggested_label to each child entry
- Orch rows (non-event) and regular script child rows render a faint
  .vv-cron-hint span to the left of the cron input showing the suggested
  cron from the script's # Schedule: header
- Clicking the hint fills in the cron input (data-cron + onclick)
- Tooltip shows full "suggested_cron — label" string on hover
- .vv-cron-hint: color #444, brightens to #888 on hover
2026-05-24 22:59:33 -04:00
Gmer4Lfe d6cfcaef46 Varaverk: always open Scheduler Information on page load; restore button for last log 2026-05-24 22:51:35 -04:00
Gmer4Lfe eade6072b0 Varaverk: Scheduler Information panel with structured how-it-works sections
- Right panel title: "Suggested Schedules" → "Scheduler Information"
- Removed top hint paragraph; info now lives in the panel itself
- Added three collapsible info sections (open by default) at top of panel:
  Controls — toggle, cron, run, dry run, log, stop, verbose, config
  Orchestrators — enabled/disabled behavior, child cron rules, event triggers
  Children & Advanced — toggle semantics, independent cron, rsync flag badge
- Bullet lists use CSS columns:2 with column-fill:balance for even distribution
- "Suggested Schedules" divider separates info from parsed schedule blocks
- Added vvToggleSug() for DOM-traversal-based accordion (no ID dependency)
2026-05-24 22:47:34 -04:00
Gmer4Lfe cdd2dfc990 Varaverk: rsync flag toggle + stop button
- Rsync/rsync.sh children in Advanced now show as conf_flag type when the
  parent orch controls a *_RSYNC_ENABLED tier flag; toggle writes true/false
  to master.conf instead of comment/uncommenting a SCRIPTS array entry
- Added vv_conf_flag_value() and vv_conf_flag_set() helpers in scheduler.php
- Added api/flag_toggle.php endpoint (validates *_RSYNC_ENABLED pattern)
- Added api/stop.php: kills process group, sweeps stuck locks, updates stat
- vv-flag-badge CSS (amber, monospace) to distinguish from event/script rows
- vvSaveChild() routes conf_flag children to flag_toggle.php unconditionally
- vvApplyOrchState() keeps conf_flag toggle at real flag value; disables when orch off
- vvSaveAll() skips conf_flag children (immediate-save only)
2026-05-24 22:38:33 -04:00
Gmer4Lfe d0d56ed8b9 scheduler: orch-god toggle model — children managed via master.conf
When orch is ON (god mode):
- Children's toggle state is read from master.conf comment status
- Toggling a child comments/uncomments its line in the *_SCRIPTS array
- Child crons are suppressed in vv_cron_rebuild — orch is the sole trigger
- Children not found in any *_SCRIPTS array are shown disabled (read-only)

When orch is OFF:
- All children flip to off; schedule.json updated immediately
- A child with a cron value + enabled toggle gets its own independent cron entry
- A child with no cron does nothing when enabled

New: vv_conf_script_map() — cached per-request scan of master.conf arrays
New: vv_parse_conf_array_full() — includes commented entries (disabled scripts)
New: vv_conf_toggle_script() — comments/uncomments a script line in master.conf
New: api/conf_toggle.php — endpoint for child toggle → master.conf write
2026-05-24 22:24:14 -04:00
Gmer4Lfe 3186097941 scheduler: revert legend grid — restore original hint paragraph 2026-05-24 22:06:48 -04:00
Gmer4Lfe 4832dd8d4e scheduler: replace hint paragraph with compact 3-column legend grid 2026-05-24 21:56:27 -04:00
Gmer4Lfe 655c520ae8 scheduler: add Array Starting / Array Stopping event triggers
Two hardcoded event-triggered entries appear at the top of the scheduler
(array_started.sh / array_stopping.sh). They show an  Array Start /
 Array Stop badge instead of a cron field and are enabled/disabled via
the normal toggle.

Static event scripts fire them via Unraid's event system:
- event/disks_mounted/array_start_jobs  → runs in background (non-blocking)
- event/disks_unmounting/array_stop_jobs → runs foreground (blocks until done)

vv_cron_rebuild() skips @array_* entries so they never land in the cron file.
Scripts executed on each event are managed in master.conf via
ARRAY_START_SCRIPTS and ARRAY_STOP_SCRIPTS arrays.
2026-05-24 21:54:13 -04:00
Gmer4Lfe 1540c62d3a scheduler: smart auto-scroll replaces separate scroll lock checkbox; lidarr: allow single-seed candidates
Auto Scroll now pauses automatically when user scrolls up and resumes
when they reach the bottom — no separate Scroll Lock needed.

Lidarr discovery no longer hard-skips single-seed candidates; they
score low on breadth but can still reach threshold on merit.
2026-05-24 21:40:03 -04:00
Gmer4Lfe 253d5bf2e1 fix(varaverk): remove legacy /etc/cron.d/varaverk on cron rebuild
Before update_cron was wired in, the plugin wrote directly to
/etc/cron.d/varaverk. That file persisted after the migration and
caused duplicate job firing (two watchdog instances at :00/:15/:30/:45).
The second instance hit the lock, exited with warn, and overwrote the
stat file — killing the running indicator while the first was still live.

vv_cron_rebuild() now unlinks the legacy file after every update_cron
call. Since rebuild_cron (boot event) calls vv_cron_rebuild(), the
cleanup runs on every reboot and every schedule save.
2026-05-24 21:27:38 -04:00
Gmer4Lfe bcb1956bbe feat(varaverk): append logs per run with timestamp separator
run_job.sh was overwriting the log on every cron fire, making it
impossible to see run history. Switch to append mode, print a
── YYYY-MM-DD HH:MM:SS ── separator before each run, and trim to
the last 1000 lines after each run to keep logs bounded.
2026-05-24 21:12:28 -04:00
gmer4lfe 7a61a56ee1 fix(common): prepend /usr/local/sbin to PATH for cron compatibility
Cron runs with PATH=/usr/bin:/bin, which omits /usr/local/sbin where
tailscale (and other Unraid tools) live. resolve_remote_ip silently got
empty results from both tailscale calls, failing all 3 retry attempts
every scheduled run. Export the full path at source time so all scripts
find tailscale regardless of how they're invoked.
2026-05-24 21:08:39 -04:00
gmer4lfe fc1fd27a3f fix(common): lock registry prevents EXIT trap overwrite orphaning locks
Scripts calling acquire_lock followed by acquire_rsync_lock (rsync.sh)
or a custom EXIT trap (resource_watchdog.sh) would overwrite the lock
release trap, leaving the first lock file behind on exit.

Replace per-acquire trap with a _LOCK_FILES registry. _register_lock()
appends each lock file to the array and sets a single _release_all_locks
trap. All acquired locks release together on exit regardless of how many
traps are subsequently set or overwritten.

resource_watchdog.sh: chain _release_all_locks into both its custom trap
and the state-persisted disarm path so the lock releases in all exit paths.
2026-05-24 21:02:59 -04:00
Gmer4Lfe 207dead909 monitor: darken transcode stream bar to deep orange (#e65100) 2026-05-24 20:20:42 -04:00
Gmer4Lfe 2128539450 style(monitor): Live TV bar blue, transcode orange, direct green 2026-05-24 20:16:59 -04:00
Gmer4Lfe 0f6154ad82 style(monitor): stream progress bars match parity blue (#1e3a5a) 2026-05-24 20:15:46 -04:00
Gmer4Lfe 0757d07ddf fix(varaverk): move varaverk.plg to /boot/config/plugins/ root (matches all other plugins) 2026-05-24 20:10:21 -04:00
Gmer4Lfe 376c7f7bc8 feat(scheduler): persist invert preference in localStorage 2026-05-24 20:00:00 -04:00
Gmer4Lfe 51fe2664b8 feat(scheduler): scroll lock on by default, persisted in localStorage 2026-05-24 19:58:44 -04:00
gmer4lfe b754842da2 fix(common): retry Tailscale IP resolution up to 3 times before failing
The first cron run of critical_sync_maintenance failed because Tailscale
had a transient moment at startup. resolve_remote_ip() now retries 3 times
with a 5s delay before giving up, making it resilient to brief daemon
hiccups without masking real peer-offline conditions.
2026-05-24 18:56:59 -04:00
Gmer4Lfe 7c14627bc3 feat(scheduler): live running indicators for cron-triggered jobs
Add api/status.php — reads all scheduled jobs' stat files and returns a
{id: status} map. The scheduler page polls it every 3s and lights up the
dot on any job whose stat shows status=running, clearing it when done.

Replace single vvRunningId with vvRunningSet (Set) so multiple jobs can
show as running simultaneously. vvSetDot/vvClearDot handle per-job dots
and the header dot for the active job. Remove stale-count detection from
vvFetchRight — status poll is now the single source of truth.
2026-05-24 18:46:45 -04:00
Gmer4Lfe 6600565dd5 feat(varaverk): add local .plg registration; remove dev-mode cron fallback
Create varaverk.plg on flash (/boot/config/plugins/varaverk/varaverk.plg).
On boot, rebuild_cron copies it to /var/log/plugins/ so update_cron natively
picks up varaverk.cron and merges it into /etc/cron.d/root.

With the .plg in place the dev-mode direct-inject fallback in vv_cron_rebuild()
is no longer needed and has been removed.
2026-05-24 18:38:43 -04:00
Gmer4Lfe 03e72b6a06 fix(scheduler): use Unraid plugin cron mechanism instead of /etc/cron.d/varaverk
dcron uses the filename in /etc/cron.d/ as the username, so a file named
'varaverk' ran jobs as a non-existent user and was silently ignored.

Switch to writing /boot/config/plugins/varaverk/varaverk.cron and calling
update_cron (which merges all plugin .cron files into /etc/cron.d/root).

In dev mode (no varaverk.plg registered), fall back to injecting our entries
directly into the live root crontab via crontab -c /etc/cron.d -.
2026-05-24 18:11:50 -04:00
Gmer4Lfe 57d55c647c varaverk: replace User Scripts dependency with native job tracking
run_job.sh: new wrapper called by scheduler cron for every job.
  Writes /var/log/varaverk/<id>.json (status/start/end/exit/pid)
  and /var/log/varaverk/<id>.log (latest run output, overwritten).
  Maps exit 0→ok, 1→warn, 2+→error so watchdog "took action" runs
  show warn not error.

scheduler: cron now calls bash run_job.sh instead of inline bash -c.
  Added vv_job_stat_path(). LOG_DIR moved to config.php (shared).

monitor: vv_scripts_status() now reads /var/log/varaverk/*.json and
  */*.json — no User Scripts tmpScripts dependency at all.
  Stale running detection via /proc/<pid> check.
  Returns error_count in addition to running/ok/warn counts.

pages/monitor.php: added error pill (red ✗), error icon/color in
  list, duration shown next to age for each script entry.
2026-05-24 17:51:31 -04:00
Gmer4Lfe ea9f39a803 watchdogs: make healthy-state outputs always visible
log() is gated on ENABLE_LOGGING — silent on normal runs. Healthy
confirmations (All healthy, Network healthy, Storage healthy, WebGUI
healthy) were invisible, making banners appear with nothing after them.
Switched all four to plain echo so they show every cycle.
2026-05-24 17:25:33 -04:00
Gmer4Lfe afd9129869 watchdogs: add echo banners to network/storage/stability
All watchdog scripts now open with a consistent echo banner:
  ━━━ <icon> <Name> Watchdog — <timestamp> ━━━

Previously network and storage used a quiet log() line (no separator),
and stability_watchdog used warn() which made every run show ⚠️.

Also: network_watchdog now logs "Network healthy " on clean exit,
and stability_watchdog's reboot-trigger block now says "Stability"
instead of the old "System Watchdog" label.
2026-05-24 17:20:55 -04:00
Gmer4Lfe b78fdddb04 fix(watchdogs): Tailscale false positive + system watchdog banner
network_watchdog: Tailscale JSON uses "BackendState": "Running" (with space)
but grep pattern matched "BackendState":"Running" (no space) — always failed.
Switched to grep -qE with \s* to handle both forms.

system_watchdog: add echo banner at run start so its output is visually
separated from docker_watchdog in orchestrator logs. Previously appeared
as a continuation of the docker section since docker_watchdog has a banner
but system_watchdog did not.
2026-05-24 17:11:54 -04:00
Gmer4Lfe 760fb4a3e2 varaverk: monitor tab overhaul — grid layout, dynamic thresholds, new cards
Layout:
- Switch from three flex rows to single CSS grid (repeat(8,1fr)) — eliminates
  gap-count discrepancy between rows; span 1/2/4/8 cards are now pixel-identical
  across all rows regardless of how many cards share the row

New cards:
- Scripts card: last-run status (ok/warn/running/unknown), clickable filter pills,
  7-row scroll, flex:1 matching Power/Parity
- Streams card: live per-second progress counters, color-coded play buttons
  (blue=live, orange=transcoding, green=direct, yellow=paused), server badges
  with counts, left-fill column layout (3 per col)
- Transcode card: NVMe indicator, active session count + scrollable list,
  session rows with server icon + title + type + method

Dynamic thresholds:
- vv_disk_thresholds() reads /boot/config/plugins/dynamix/dynamix.cfg
- Disk utilization bars: green/orange/red based on warning/critical settings
- Temperature colors: hot/max for HDD, hotssd/maxssd for SSD/NVMe
- Thresholds update every poll cycle; changing Unraid settings takes effect automatically

Other:
- SSD path detection uses findmnt FSTYPE to exclude tmpfs/ramfs (fixes RAM
  being reported as SSD transcode target)
- varaverk.css h3: add white-space:normal + overflow:hidden to neutralise
  any Unraid global h3 styles that could force card width
2026-05-24 16:56:50 -04:00
Gmer4Lfe f85493cf35 watchdogs: fix Tailscale error propagation and docker watchdog output order
- network_watchdog: remove ISSUES++ for Tailscale — it's notify-only by
  design, no restart possible; cascading to system watchdog exit 1 was noise
- docker_watchdog: move banner to top of cycle so body errors appear under
  the correct section header, not visually inside Resource Manager output
2026-05-24 16:56:32 -04:00
Gmer4Lfe 359b051e64 varaverk: System card (identity/time/uptime/buttons); merge Fallback+Partner into one card 2026-05-24 12:00:09 -04:00
Gmer4Lfe 725c371f21 varaverk: remove GPU history chart 2026-05-24 11:53:54 -04:00
Gmer4Lfe 6be9873f49 varaverk: fallback and partner cards — 250px min-width, matched flex:1 2026-05-24 11:50:51 -04:00
Gmer4Lfe 36831f0693 Revert "varaverk: top row cards equal width (flex:1 across all five)"
This reverts commit f35a0f2dd4.
2026-05-24 11:49:30 -04:00
Gmer4Lfe f35a0f2dd4 varaverk: top row cards equal width (flex:1 across all five) 2026-05-24 11:48:50 -04:00