# settings/hyprland.nix
{ config, pkgs, lib, inputs, ... }:
let
  hyprland-pkgs = inputs.hyprland.packages.${pkgs.stdenv.hostPlatform.system};
  anyrun-pkgs   = inputs.anyrun.packages.${pkgs.stdenv.hostPlatform.system};

  isMacbook = config.networking.hostName == "FredOS-Macbook";
  isGaming  = !isMacbook;

in
{
  config = lib.mkIf (lib.elem config.networking.hostName [ "FredOS-Gaming" "FredOS-Macbook" ]) {
    programs.hyprland = {
      enable = true;
      xwayland.enable = true;
      package       = hyprland-pkgs.hyprland;
      portalPackage = hyprland-pkgs.xdg-desktop-portal-hyprland;
    };

    xdg.portal = {
      enable = true;
      # xdg-desktop-portal-hyprland is registered automatically by
      # programs.hyprland.portalPackage; listing it here too produced a
      # duplicate user-unit symlink during nixos-rebuild.
      extraPortals = with pkgs; [
        xdg-desktop-portal-gtk
      ];
      config.hyprland.default = [ "hyprland" "gtk" ];
    };

    security.polkit.enable = true;

    # Polkit GUI agent for GUI sudo prompts under Hyprland
    systemd.user.services.polkit-gnome-authentication-agent-1 = {
      description = "polkit-gnome-authentication-agent-1";
      wantedBy = [ "graphical-session.target" ];
      partOf = [ "graphical-session.target" ];
      after = [ "graphical-session.target" ];
      serviceConfig = {
        Type = "simple";
        ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1";
        Restart = "on-failure";
      };
    };

    environment.systemPackages = with pkgs; [
      ghostty
      libnotify
      grim
      slurp
      wl-clipboard
      cliphist
      brightnessctl
      swayosd
      playerctl
      hyprpaper
      hyprlock
      hypridle
      hyprshot
      networkmanagerapplet
      pavucontrol
      polkit_gnome
      quickshell
      qt6.qt5compat
      zenity
      libcanberra-gtk3
    ];

    # Use upstream anyrun flake's HM module instead of the built-in one
    # for working daemon mode.
    home-manager.sharedModules = [
      ({ modulesPath, ... }: {
        disabledModules = [ "${modulesPath}/programs/anyrun.nix" ];
      })
      inputs.anyrun.homeManagerModules.default
    ];

    home-manager.users.fred = { config, lib, pkgs, inputs, ... }:
    let
      c    = config.lib.stylix.colors;
      rgb  = hex: "rgb(${hex})";
      rgba = hex: a: "rgba(${hex}${a})";
    in {
      # Stylix's Hyprland target injects settings.{general,decoration,group,misc}
      # as top-level keys, which render as hl.general()/hl.decoration()/… in Lua
      # mode — functions that don't exist.  Disable it and absorb the colours
      # into settings.config below.
      stylix.targets.hyprland.enable = false;

      # The disabled Hyprland target would normally enable this; do it
      # manually.  Stylix's hyprpaper target (auto-enabled) still handles
      # preload/wallpaper settings.
      services.hyprpaper.enable = true;

      wayland.windowManager.hyprland = {
        enable = true;
        configType = "lua";
        systemd.variables = [ "--all" ];
        package = hyprland-pkgs.hyprland;

        settings = {
          # hl.config({...}) — all static named-section configuration.
          # monitor is set per-host in hosts/FredOS-{Gaming,Macbook}.nix.
          config = {
            general = {
              gaps_in = 6;
              gaps_out = 12;
              border_size = 2;
              layout = "dwindle";
              resize_on_border = true;
              "col.active_border"   = rgb c.base0D;
              "col.inactive_border" = rgb c.base03;
            };

            decoration = {
              rounding = 8;
              blur = {
                enabled = true;
              };
              shadow.color = rgba c.base00 "99";
            };

            group = {
              "col.border_active"        = rgb c.base0D;
              "col.border_inactive"      = rgb c.base03;
              "col.border_locked_active" = rgb c.base0C;
              groupbar = {
                text_color    = rgb c.base05;
                "col.active"  = rgb c.base0D;
                "col.inactive" = rgb c.base03;
              };
            };

            render = {
              direct_scanout = false;
            };

            animations = {
              enabled = true;
            };

            input = {
              kb_layout = "gb,no";
              kb_options = "grp:alt_shift_toggle";
              follow_mouse = 1;
              accel_profile = "flat";
              sensitivity = 0;
            } // lib.optionalAttrs isMacbook {
              touchpad = {
                tap_to_click = true;
                tap_button_map = "lrm";
                natural_scroll = true;
              };
            };

            cursor = {
              no_warps = true;
            };

            dwindle = {
              preserve_split = true;
            };

            misc = {
              disable_hyprland_logo = true;
              disable_splash_rendering = true;
              # Apps demanding attention don't get to yank focus — they'll
              # show as urgent in the bar instead.
              focus_on_activate = false;
              vrr = 2;
              background_color = rgb c.base00;
            };

            # vfr moved from misc: to debug: in 0.55.0
            debug = {
              vfr = false; # keep compositor ticking, don't idle between frames
              disable_logs = false;
            };
          };
        };

        extraConfig =
          let
            powerMenu = pkgs.writeShellScript "power-menu" ''
              # Stop the daemon so standalone stdin mode can run cleanly.
              # systemd restarts it automatically afterwards (Restart=on-failure).
              systemctl --user stop anyrun.service 2>/dev/null || true
              choice=$(printf '%s\n' \
                $'\uf023  Lock' \
                $'\uf08b  Logout' \
                $'\uf01e  Reboot' \
                $'\uf011  Shutdown' \
                | ${anyrun-pkgs.anyrun}/bin/anyrun \
                    --plugins "${anyrun-pkgs.stdin}/lib/libstdin.so" \
                    --show-results-immediately true \
                    --hide-plugin-info true \
                    --close-on-click true)
              # Restart the daemon service (reset-failed clears the start-rate limiter).
              systemctl --user reset-failed anyrun.service 2>/dev/null
              systemctl --user start anyrun.service 2>/dev/null
              case "$choice" in
                *Lock)     ${pkgs.hyprlock}/bin/hyprlock ;;
                *Logout)   hyprctl dispatch exit ;;
                *Reboot)   systemctl reboot ;;
                *Shutdown) systemctl poweroff ;;
              esac
            '';
            kbdBrightUp = pkgs.writeShellScript "kbd-bright-up" ''
              ${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight set +10%
              brightness=$(${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight get)
              max=$(${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight max)
              echo $(( brightness * 100 / max )) > "$XDG_RUNTIME_DIR/wob.fifo"
            '';
            kbdBrightDown = pkgs.writeShellScript "kbd-bright-down" ''
              ${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight set 10%-
              brightness=$(${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight get)
              max=$(${pkgs.brightnessctl}/bin/brightnessctl -d smc::kbd_backlight max)
              echo $(( brightness * 100 / max )) > "$XDG_RUNTIME_DIR/wob.fifo"
            '';
          in
          ''
            -- Layer rules: blur behind bar and toasts
            hl.keyword("layerrule", "blur, quickshell-bar")
            hl.keyword("layerrule", "blur, quickshell-toast")
            hl.keyword("layerrule", "ignorealpha 0.3, quickshell-bar")
            hl.keyword("layerrule", "ignorealpha 0.3, quickshell-toast")

            -- Environment
            hl.env("XCURSOR_THEME", "Bibata-Modern-Ice")
            hl.env("XCURSOR_SIZE", "24")
            hl.env("HYPRCURSOR_THEME", "Bibata-Modern-Ice")
            hl.env("HYPRCURSOR_SIZE", "24")
            hl.env("ELECTRON_OZONE_PLATFORM_HINT", "wayland")
            hl.env("MOZ_ENABLE_WAYLAND", "1")
            hl.env("QT_QPA_PLATFORM", "wayland;xcb")
            hl.env("SDL_VIDEODRIVER", "wayland")
            hl.env("_JAVA_AWT_WM_NONREPARENTING", "1")
            ${lib.optionalString isGaming ''
            -- GPU pinning — Navi 22 is card1 on the dual-GPU gaming box.
            hl.env("AQ_DRM_DEVICES", "/dev/dri/card1")
            hl.env("DRI_PRIME", "pci-0000_03_00_0")
            ''}

            -- Startup
            hl.on("hyprland.start", function()
              -- Ensure hyprland-session.target starts even if HM's
              -- dbus-update-activation-environment chain fails upstream.
              hl.exec_cmd("systemctl --user start hyprland-session.target")
              -- mako removed; notifications handled by quickshell

              hl.exec_cmd("wl-paste --type text --watch cliphist store")
              hl.exec_cmd("wl-paste --type image --watch cliphist store")
              hl.exec_cmd("hyprctl setcursor Bibata-Modern-Ice 24")
              hl.exec_cmd("swayosd-server")
              ${lib.optionalString isMacbook ''hl.exec_cmd("hypridle")''}
            end)

            -- Animation curve and definitions
            hl.curve("snap", { type = "bezier", points = { {0.05, 0.9}, {0.1, 1.0} } })
            hl.animation({ leaf = "windows",    enabled = true, speed = 1, bezier = "snap" })
            hl.animation({ leaf = "windowsOut", enabled = true, speed = 1, bezier = "snap", style = "popin 80%" })
            hl.animation({ leaf = "layers",     enabled = true, speed = 1, bezier = "snap" })
            hl.animation({ leaf = "border",     enabled = true, speed = 2, bezier = "default" })
            hl.animation({ leaf = "fade",       enabled = true, speed = 1, bezier = "default" })
            hl.animation({ leaf = "workspaces", enabled = true, speed = 1, bezier = "snap" })

            -- Window rules
            -- Battle.net tray icon leaks as a tiny floating XWayland window.
            hl.window_rule({
              match = { class = "steam_app_0", title = "^$", float = true },
              workspace = "special silent",
            })

            -- Binds
            local mod = "SUPER"

            -- Apps
            hl.bind(mod .. " + T", hl.dsp.exec_cmd("ghostty"))
            hl.bind(mod .. " + E", hl.dsp.exec_cmd("nemo"))
            hl.bind(mod .. " + R", hl.dsp.exec_cmd("anyrun close || anyrun"))
            hl.bind(mod .. " + Q", hl.dsp.window.close())
            hl.bind(mod .. " + SHIFT + E", hl.dsp.exit())

            -- Floating / layout
            hl.bind(mod .. " + V", hl.dsp.window.float({ action = "toggle" }))
            hl.bind(mod .. " + F", hl.dsp.window.fullscreen())
            hl.bind(mod .. " + P", hl.dsp.window.pseudo())
            hl.bind(mod .. " + S", hl.dsp.layout("togglesplit"))

            -- Focus
            hl.bind(mod .. " + left",  hl.dsp.focus({ direction = "left" }))
            hl.bind(mod .. " + right", hl.dsp.focus({ direction = "right" }))
            hl.bind(mod .. " + up",    hl.dsp.focus({ direction = "up" }))
            hl.bind(mod .. " + down",  hl.dsp.focus({ direction = "down" }))
            hl.bind(mod .. " + H",     hl.dsp.focus({ direction = "left" }))
            hl.bind(mod .. " + K",     hl.dsp.focus({ direction = "up" }))
            hl.bind(mod .. " + J",     hl.dsp.focus({ direction = "down" }))

            -- Power menu — dismiss launcher if open, then show menu
            hl.bind(mod .. " + L", hl.dsp.exec_cmd("anyrun close 2>/dev/null; ${powerMenu}"))

            -- Move windows
            hl.bind(mod .. " + SHIFT + left",  hl.dsp.window.move({ direction = "left" }))
            hl.bind(mod .. " + SHIFT + right", hl.dsp.window.move({ direction = "right" }))
            hl.bind(mod .. " + SHIFT + up",    hl.dsp.window.move({ direction = "up" }))
            hl.bind(mod .. " + SHIFT + down",  hl.dsp.window.move({ direction = "down" }))

            -- Workspaces
            for i = 0, 9 do
              local workspace_id = tostring((i == 0) and 10 or i)
              hl.bind(mod .. " + " .. i, hl.dsp.focus({ workspace = workspace_id }))
              hl.bind(mod .. " + SHIFT + " .. i, hl.dsp.window.move({ workspace = workspace_id, follow = false }))
            end

            -- Screenshots — Shift+Super+S matches GNOME binding
            hl.bind(mod .. " + SHIFT + S", hl.dsp.exec_cmd("hyprshot -m region --clipboard-only"))
            hl.bind("Print",               hl.dsp.exec_cmd("hyprshot -m output --clipboard-only"))

            -- Settings shortcut — Super+I matches GNOME binding
            hl.bind(mod .. " + I", hl.dsp.exec_cmd("pavucontrol"))

            -- Custom shortcuts
            hl.bind(mod .. " + Z", hl.dsp.exec_cmd("zen-beta"))

            -- Mouse window manipulation
            hl.bind(mod .. " + mouse:272", hl.dsp.window.drag(),   { mouse = true })
            hl.bind(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })

            -- Volume / brightness (repeating)
            hl.bind("XF86AudioRaiseVolume",  hl.dsp.exec_cmd("swayosd-client --output-volume raise"),       { repeating = true })
            hl.bind("XF86AudioLowerVolume",  hl.dsp.exec_cmd("swayosd-client --output-volume lower"),       { repeating = true })
            hl.bind("XF86AudioMute",         hl.dsp.exec_cmd("swayosd-client --output-volume mute-toggle"), { repeating = true })
            hl.bind("XF86MonBrightnessUp",   hl.dsp.exec_cmd("swayosd-client --brightness raise"),          { repeating = true })
            hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("swayosd-client --brightness lower"),          { repeating = true })
            ${lib.optionalString isMacbook ''
            hl.bind("XF86KbdBrightnessUp",   hl.dsp.exec_cmd("${kbdBrightUp}"),   { repeating = true })
            hl.bind("XF86KbdBrightnessDown", hl.dsp.exec_cmd("${kbdBrightDown}"), { repeating = true })
            ''}

            -- Media keys (locked — work through lockscreen)
            hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), { locked = true })
            hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"),        { locked = true })
            hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"),    { locked = true })
          '';
      };

      programs.anyrun = {
        enable = true;
        config = {
          plugins = [ anyrun-pkgs.applications ];
          x.fraction = 0.5;
          y.fraction = 0.25;
          width.absolute = 350;
          height.absolute = 0;
          hideIcons = false;
          ignoreExclusiveZones = false;
          layer = "overlay";
          hidePluginInfo = true;
          closeOnClick = true;
          maxEntries = 8;
        };
        extraCss =
          let c = config.lib.stylix.colors; in
          ''
            * { all: unset; font-family: "FiraMono Nerd Font", monospace; font-size: 13px; }
            window { background: transparent; }
            box.main {
              background: #${c.base00};
              border: 1px solid #${c.base03};
              border-radius: 10px;
              padding: 8px;
              margin: 16px;
            }
            text {
              background: #${c.base01};
              color: #${c.base05};
              caret-color: #${c.base0D};
              padding: 8px 16px;
              border-radius: 6px;
              min-height: 0;
            }
            list.plugin { background: transparent; }
            .matches    { background: transparent; }
            .match {
              padding: 4px 16px;
              border-radius: 6px;
              color: #${c.base05};
              background: transparent;
            }
            .match:selected {
              background: #${c.base02};
              border: none;
            }
            label.match.description { color: #${c.base04}; font-size: 11px; }
          '';
        extraConfigFiles."applications.ron".text = ''
          Config(
            desktop_actions: false,
            max_entries: 8,
            terminal: Some("ghostty"),
          )
        '';
      };

      programs.hyprlock = {
        enable = true;
        settings = {
          general = {
            grace = 5;
            hide_cursor = true;
          };
          background = {
            blur_passes = 3;
            blur_size = 8;
            brightness = 0.6;
          };
          input-field = {
            size = "280, 44";
            outline_thickness = 2;
            dots_size = 0.25;
            dots_spacing = 0.2;
            dots_center = true;
            fade_on_empty = true;
            fade_timeout = 3000;
            placeholder_text = "";
            fail_text = "";
            position = "0, -40";
            halign = "center";
            valign = "center";
            rounding = 8;
          };
          label = [
            {
              text = "$TIME";
              color = "rgb(${c.base05})";
              font_size = 72;
              font_family = "Inter";
              position = "0, 120";
              halign = "center";
              valign = "center";
            }
            {
              text = "cmd[update:60000] date +'%A, %d %B'";
              color = "rgb(${c.base04})";
              font_size = 16;
              font_family = "Inter";
              position = "0, 60";
              halign = "center";
              valign = "center";
            }
          ];
        };
      };

      services.hypridle = lib.mkIf isMacbook {
        enable = true;
        settings = {
          general = {
            lock_cmd         = "pidof hyprlock || hyprlock";
            before_sleep_cmd = "loginctl lock-session";
            after_sleep_cmd  = "hyprctl dispatch dpms on";
          };
          listener = [
            {
              timeout    = 300;   # 5 min — lock
              on-timeout = "loginctl lock-session";
            }
            {
              timeout    = 420;   # 7 min — display off
              on-timeout = "hyprctl dispatch dpms off";
              on-resume  = "hyprctl dispatch dpms on";
            }
            {
              timeout    = 600;   # 10 min — suspend
              on-timeout = "systemctl suspend";
            }
          ];
        };
      };

      # Scope all HM Wayland services (hyprpaper, etc.) to the
      # Hyprland session so they don't crash-loop in a GNOME session.
      wayland.systemd.target = "hyprland-session.target";

      systemd.user.services.quickshell = {
        Unit = {
          Description = "Quickshell desktop shell";
          PartOf = [ "graphical-session.target" ];
          After = [ "graphical-session.target" ];
        };
        Service = {
          ExecStart = "${pkgs.quickshell}/bin/qs";
          Restart = "always";
          RestartSec = 2;
        };
        Install.WantedBy = [ "hyprland-session.target" ];
      };

      xdg.configFile = let
        qsRestart = ''
          ${pkgs.systemd}/bin/systemctl --user restart quickshell.service 2>/dev/null || true
        '';
        wifiConnectScript = pkgs.writeShellScript "wifi-connect" ''
          ssid="$1"
          ${pkgs.networkmanager}/bin/nmcli device wifi connect "$ssid" 2>/dev/null && exit 0
          pw=$(${pkgs.zenity}/bin/zenity --password --title="WiFi Password" 2>/dev/null)
          [ -n "$pw" ] && ${pkgs.networkmanager}/bin/nmcli device wifi connect "$ssid" password "$pw"
        '';
        nmcli = "${pkgs.networkmanager}/bin/nmcli";
        powerprofilesctl = "${pkgs.power-profiles-daemon}/bin/powerprofilesctl";
      in {
        "quickshell/qmldir" = {
          onChange = qsRestart;
          text = ''
            singleton Theme 1.0 Theme.qml
            singleton Commands 1.0 Commands.qml
            Bar 1.0 Bar.qml
            NotificationToast 1.0 NotificationToast.qml
          '';
        };

        "quickshell/Theme.qml" = {
          onChange = qsRestart;
          text = ''
            pragma Singleton
            import QtQuick

            QtObject {
                readonly property color base00: "#${c.base00}"
                readonly property color base01: "#${c.base01}"
                readonly property color base02: "#${c.base02}"
                readonly property color base03: "#${c.base03}"
                readonly property color base04: "#${c.base04}"
                readonly property color base05: "#${c.base05}"
                readonly property color base08: "#${c.base08}"
                readonly property color base0A: "#${c.base0A}"
                readonly property color base0B: "#${c.base0B}"
                readonly property color base0C: "#${c.base0C}"
                readonly property color base0D: "#${c.base0D}"
                readonly property color barBg: "#B3${c.base00}"
                readonly property color toastBg: "#E6${c.base00}"
            }
          '';
        };

        "quickshell/Commands.qml" = {
          onChange = qsRestart;
          text = ''
            pragma Singleton
            import QtQuick

            QtObject {
                readonly property string nmcli: "${nmcli}"
                readonly property string wifiConnect: "${wifiConnectScript}"
                readonly property string powerprofilesctl: "${powerprofilesctl}"
                readonly property string notifSound: "${pkgs.libcanberra-gtk3}/bin/canberra-gtk-play"
            }
          '';
        };

        "quickshell/shell.qml" = {
          onChange = qsRestart;
          text = ''
            //@ pragma UseQApplication
            import Quickshell
            import Quickshell.Services.Notifications
            import QtQuick

            ShellRoot {
                id: root
                property var latestNotification: null
                signal notificationReceived()

                NotificationServer {
                    id: _notifServer
                    bodySupported: true
                    actionsSupported: true
                    imageSupported: true
                    persistenceSupported: true
                    keepOnReload: true
                    onNotification: (notification) => {
                        notification.tracked = true;
                        root.latestNotification = notification;
                        root.notificationReceived();
                    }
                }

                Variants {
                    model: Quickshell.screens

                    Bar {
                        notifServer: _notifServer
                    }
                }

                NotificationToast {
                    shellRoot: root
                }
            }
          '';
        };

        "quickshell/Bar.qml" = {
          onChange = qsRestart;
          text = ''
            import Quickshell
            import Quickshell.Hyprland
            import Quickshell.Wayland
            import Quickshell.Services.SystemTray
            import Quickshell.Services.Notifications
            import Quickshell.Services.Pipewire
            import Quickshell.Widgets
            import Quickshell.Io
            import QtQuick
            import QtQuick.Layouts
            import Qt5Compat.GraphicalEffects

            PanelWindow {
                id: bar
                required property var modelData
                required property NotificationServer notifServer
                screen: modelData
                WlrLayershell.namespace: "quickshell-bar"

                anchors {
                    top: true
                    left: true
                    right: true
                }

                implicitHeight: 30
                color: Theme.barBg

                property var activeDropdown: null

                function closeAllDropdowns() {
                    if (activeDropdown && activeDropdown.visible) {
                        activeDropdown.animateClose();
                    }
                    activeDropdown = null;
                }
                function toggleDropdown(dd, setupFn) {
                    if (dd.visible && !dd.closing) {
                        dd.animateClose();
                        activeDropdown = null;
                    } else {
                        if (activeDropdown && activeDropdown !== dd && activeDropdown.visible) {
                            activeDropdown.animateClose();
                        }
                        if (setupFn) setupFn();
                        if (dd.closing) {
                            dd.closing = false;
                            dd.open = true;
                        } else {
                            dd.visible = true;
                        }
                        activeDropdown = dd;
                    }
                }

                // Left — workspaces
                Row {
                    anchors.left: parent.left
                    anchors.leftMargin: 6
                    anchors.verticalCenter: parent.verticalCenter
                    spacing: 0

                    Repeater {
                        model: Hyprland.workspaces

                        Item {
                            required property var modelData
                            width: 28
                            height: 30

                            Text {
                                anchors.centerIn: parent
                                text: modelData.name
                                color: modelData.focused ? Theme.base05 : Theme.base03
                                font.family: "FiraMono Nerd Font"
                                font.pixelSize: 13
                            }

                            Rectangle {
                                anchors.bottom: parent.bottom
                                anchors.horizontalCenter: parent.horizontalCenter
                                width: parent.width - 8
                                height: 2
                                color: Theme.base05
                                visible: modelData.focused
                            }

                            MouseArea {
                                anchors.fill: parent
                                onClicked: modelData.activate()
                            }
                        }
                    }
                }

                // Center — clock
                Text {
                    id: clockText
                    anchors.centerIn: parent
                    property date now: new Date()
                    text: now.toLocaleTimeString(Qt.locale(), "HH:mm")
                    color: Theme.base05
                    font.family: "FiraMono Nerd Font"
                    font.pixelSize: 13
                    font.weight: Font.Medium

                    Timer {
                        interval: 1000
                        running: true
                        repeat: true
                        onTriggered: clockText.now = new Date()
                    }

                    MouseArea {
                        anchors.fill: parent
                        hoverEnabled: true
                        onClicked: bar.toggleDropdown(calPopup)
                        onEntered: {
                            if (bar.activeDropdown) {
                                if (bar.activeDropdown !== calPopup) bar.toggleDropdown(calPopup);
                                else bar.activeDropdown.resetAutoClose();
                            }
                        }
                    }
                }

                // Right — network, battery, tray
                Row {
                    anchors.right: parent.right
                    anchors.rightMargin: 8
                    anchors.verticalCenter: parent.verticalCenter
                    spacing: 10

                    // Volume
                    Item {
                        id: volWidget
                        width: volText.width
                        height: 30

                        property PwNode sink: Pipewire.defaultAudioSink

                        PwObjectTracker {
                            objects: [volWidget.sink]
                        }

                        property int vol: sink && sink.audio ? Math.round(sink.audio.volume * 100) : 0
                        property bool muted: sink && sink.audio ? sink.audio.muted : false
                        property string volIcon: muted ? "\u{f0581}"
                            : vol > 66 ? "\u{f057e}"
                            : vol > 33 ? "\u{f0580}"
                            : vol > 0  ? "\u{f057f}"
                            : "\u{f0581}"

                        function openVolDropdown() {
                            bar.toggleDropdown(volDropdown, function() {
                                let pos = volWidget.mapToItem(bar.contentItem, volWidget.width / 2, 0);
                                volDropdown.dropdownX = pos.x;
                            });
                        }

                        Text {
                            id: volText
                            anchors.verticalCenter: parent.verticalCenter
                            text: volWidget.volIcon + " " + volWidget.vol + "%"
                            color: volWidget.muted ? Theme.base03 : Theme.base05
                            font.family: "FiraMono Nerd Font"
                            font.pixelSize: 13
                        }

                        MouseArea {
                            anchors.fill: parent
                            hoverEnabled: true
                            acceptedButtons: Qt.LeftButton | Qt.MiddleButton
                            onClicked: (event) => {
                                if (event.button === Qt.MiddleButton) {
                                    if (volWidget.sink && volWidget.sink.audio)
                                        volWidget.sink.audio.muted = !volWidget.sink.audio.muted;
                                } else {
                                    volWidget.openVolDropdown();
                                }
                            }
                            onEntered: {
                                if (bar.activeDropdown) {
                                    if (bar.activeDropdown !== volDropdown) volWidget.openVolDropdown();
                                    else bar.activeDropdown.resetAutoClose();
                                }
                            }
                        }
                    }

                    // Network status
                    Item {
                        id: netWidget
                        width: 16
                        height: 30

                        property string netState: "disconnected"
                        property string netConn: ""
                        property string netType: ""
                        property string netIcon: "\u{f0b0}"
                        property var wifiNetworks: []
                        property string netDevice: ""

                        property string _pendingState: "disconnected"
                        property string _pendingConn: ""
                        property string _pendingType: ""
                        property string _pendingDevice: ""
                        property var _pendingNets: []

                        Timer {
                            interval: 5000
                            running: true
                            repeat: true
                            triggeredOnStart: true
                            onTriggered: netWidget.refreshNet()
                        }

                        function refreshNet() {
                            netWidget._pendingState = "disconnected";
                            netWidget._pendingConn = "";
                            netWidget._pendingType = "";
                            netProc.running = true;
                        }

                        Process {
                            id: netProc
                            command: [Commands.nmcli, "-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device"]
                            stdout: SplitParser {
                                onRead: data => {
                                    let fields = data.split(":");
                                    if (fields.length < 4) return;
                                    let type = fields[1];
                                    let state = fields[2];
                                    let conn = fields[3];
                                    if (type !== "ethernet" && type !== "wifi") return;
                                    if (type === "wifi") {
                                        netWidget._pendingDevice = fields[0];
                                    }
                                    if (state === "connected") {
                                        netWidget._pendingState = "connected";
                                        netWidget._pendingConn = conn;
                                        netWidget._pendingType = type;
                                    }
                                }
                            }
                            onRunningChanged: {
                                if (!running) {
                                    netWidget.netState = netWidget._pendingState;
                                    netWidget.netConn = netWidget._pendingConn;
                                    netWidget.netType = netWidget._pendingType.length > 0 ? netWidget._pendingType : netWidget.netType;
                                    netWidget.netDevice = netWidget._pendingDevice.length > 0 ? netWidget._pendingDevice : netWidget.netDevice;
                                    if (netWidget.netState === "connected") {
                                        netWidget.netIcon = netWidget.netType === "wifi" ? "\u{f05a9}" : "\u{f0200}";
                                    } else {
                                        netWidget.netIcon = netWidget.netType === "wifi" ? "\u{f05aa}" : "\u{f0201}";
                                    }
                                }
                            }
                        }

                        Text {
                            anchors.centerIn: parent
                            text: netWidget.netIcon
                            color: Theme.base05
                            font.family: "FiraMono Nerd Font"
                            font.pixelSize: 14
                        }

                        Timer {
                            id: netRefreshDelay
                            interval: 2000
                            onTriggered: netWidget.refreshNet()
                        }

                        Process {
                            id: wifiScanProc
                            command: [Commands.nmcli, "-t", "-f", "SSID,SIGNAL,SECURITY,IN-USE", "device", "wifi", "list", "--rescan", "auto"]
                            stdout: SplitParser {
                                onRead: data => {
                                    let fields = data.split(":");
                                    if (fields.length < 4 || fields[0] === "") return;
                                    for (let i = 0; i < netWidget._pendingNets.length; i++) {
                                        if (netWidget._pendingNets[i].ssid === fields[0]) return;
                                    }
                                    netWidget._pendingNets.push({
                                        ssid: fields[0],
                                        signal: parseInt(fields[1]) || 0,
                                        security: fields[2],
                                        active: fields[3] === "*"
                                    });
                                }
                            }
                            onRunningChanged: {
                                if (!running) {
                                    netWidget.wifiNetworks = netWidget._pendingNets;
                                    netWidget._pendingNets = [];
                                }
                            }
                        }

                        Process {
                            id: wifiConnectProc
                            property string targetSsid: ""
                            command: [Commands.wifiConnect, targetSsid]
                        }

                        Process {
                            id: netDisconnectProc
                            property string targetDevice: ""
                            command: [Commands.nmcli, "device", "disconnect", targetDevice]
                        }

                        function openNetDropdown() {
                            bar.toggleDropdown(netDropdown, function() {
                                wifiScanProc.running = true;
                                let pos = netWidget.mapToItem(bar.contentItem, netWidget.width / 2, 0);
                                netDropdown.dropdownX = pos.x;
                            });
                        }

                        MouseArea {
                            anchors.fill: parent
                            hoverEnabled: true
                            onClicked: netWidget.openNetDropdown()
                            onEntered: {
                                if (bar.activeDropdown) {
                                    if (bar.activeDropdown !== netDropdown) netWidget.openNetDropdown();
                                    else bar.activeDropdown.resetAutoClose();
                                }
                            }
                        }
                    }

                    ${lib.optionalString isMacbook ''
                    // Battery
                    Item {
                        id: batteryWidget
                        width: batteryText.width + 4 + batteryIconText.width
                        height: 30

                        property int batteryLevel: 0
                        property bool charging: false
                        property string batteryIcon: "\u{f008e}"
                        property real powerDraw: 0.0
                        property real energyNow: 0.0
                        property real energyFull: 0.0
                        property string timeRemaining: ""
                        property string powerProfile: "balanced"

                        function updateIcon() {
                            if (charging) { batteryIcon = "\u{f0084}"; return; }
                            if (batteryLevel >= 90) batteryIcon = "\u{f0079}";
                            else if (batteryLevel >= 70) batteryIcon = "\u{f0082}";
                            else if (batteryLevel >= 50) batteryIcon = "\u{f007f}";
                            else if (batteryLevel >= 30) batteryIcon = "\u{f007c}";
                            else if (batteryLevel >= 15) batteryIcon = "\u{f007a}";
                            else batteryIcon = "\u{f008e}";
                        }

                        Timer {
                            interval: 5000
                            running: true
                            repeat: true
                            triggeredOnStart: true
                            onTriggered: { batteryProc.running = true; profileProc.running = true; }
                        }

                        Process {
                            id: batteryProc
                            command: ["sh", "-c", "cat /sys/class/power_supply/BAT0/capacity; cat /sys/class/power_supply/BAT0/status; cat /sys/class/power_supply/BAT0/power_now 2>/dev/null || echo 0; cat /sys/class/power_supply/BAT0/energy_now 2>/dev/null || echo 0; cat /sys/class/power_supply/BAT0/energy_full 2>/dev/null || echo 0"]
                            stdout: SplitParser {
                                property int lineNum: 0
                                onRead: data => {
                                    let trimmed = data.trim();
                                    let num = parseInt(trimmed);
                                    lineNum++;
                                    if (lineNum === 1) {
                                        if (!isNaN(num)) batteryWidget.batteryLevel = num;
                                    } else if (lineNum === 2) {
                                        batteryWidget.charging = (trimmed === "Charging");
                                    } else if (lineNum === 3) {
                                        if (!isNaN(num)) batteryWidget.powerDraw = num / 1000000.0;
                                    } else if (lineNum === 4) {
                                        if (!isNaN(num)) batteryWidget.energyNow = num / 1000000.0;
                                    } else if (lineNum === 5) {
                                        if (!isNaN(num)) batteryWidget.energyFull = num / 1000000.0;
                                        lineNum = 0;
                                        if (batteryWidget.powerDraw > 0.5) {
                                            let hours;
                                            if (batteryWidget.charging) {
                                                hours = (batteryWidget.energyFull - batteryWidget.energyNow) / batteryWidget.powerDraw;
                                            } else {
                                                hours = batteryWidget.energyNow / batteryWidget.powerDraw;
                                            }
                                            let h = Math.floor(hours);
                                            let m = Math.round((hours - h) * 60);
                                            batteryWidget.timeRemaining = h + "h " + m + "m";
                                        } else {
                                            batteryWidget.timeRemaining = "";
                                        }
                                    }
                                    batteryWidget.updateIcon();
                                }
                            }
                        }

                        Process {
                            id: profileProc
                            command: [Commands.powerprofilesctl, "get"]
                            stdout: SplitParser {
                                onRead: data => {
                                    batteryWidget.powerProfile = data.trim();
                                }
                            }
                        }

                        Process {
                            id: setProfileProc
                            property string target: "balanced"
                            command: [Commands.powerprofilesctl, "set", target]
                        }

                        Row {
                            anchors.verticalCenter: parent.verticalCenter
                            spacing: 4

                            Text {
                                id: batteryText
                                text: batteryWidget.batteryLevel + "%"
                                color: batteryWidget.batteryLevel <= 15 ? Theme.base08
                                     : batteryWidget.batteryLevel <= 30 ? Theme.base0A
                                     : Theme.base05
                                font.family: "FiraMono Nerd Font"
                                font.pixelSize: 13
                            }

                            Text {
                                id: batteryIconText
                                text: batteryWidget.batteryIcon
                                color: batteryWidget.batteryLevel <= 15 ? Theme.base08
                                     : batteryWidget.batteryLevel <= 30 ? Theme.base0A
                                     : Theme.base05
                                font.family: "FiraMono Nerd Font"
                                font.pixelSize: 14
                            }
                        }

                        function openBatteryDropdown() {
                            bar.toggleDropdown(batteryDropdown, function() {
                                batteryProc.running = true;
                                profileProc.running = true;
                                let pos = batteryWidget.mapToItem(bar.contentItem, batteryWidget.width / 2, 0);
                                batteryDropdown.dropdownX = pos.x;
                            });
                        }

                        MouseArea {
                            anchors.fill: parent
                            hoverEnabled: true
                            onClicked: batteryWidget.openBatteryDropdown()
                            onEntered: {
                                if (bar.activeDropdown) {
                                    if (bar.activeDropdown !== batteryDropdown) batteryWidget.openBatteryDropdown();
                                    else bar.activeDropdown.resetAutoClose();
                                }
                            }
                        }
                    }
                    ''}

                    // Tray icons
                    Row {
                        id: trayArea
                        spacing: 8
                        height: 30
                        anchors.verticalCenter: parent.verticalCenter

                        HoverHandler {
                            onHoveredChanged: {
                                if (hovered && bar.activeDropdown) bar.activeDropdown.resetAutoClose();
                            }
                        }

                        Repeater {
                            model: SystemTray.items

                            Item {
                                required property var modelData
                                width: 24
                                height: 30

                                Image {
                                    id: trayIcon
                                    anchors.centerIn: parent
                                    width: 16
                                    height: 16
                                    source: modelData.icon
                                    sourceSize.width: 16
                                    sourceSize.height: 16
                                    smooth: true
                                    mipmap: true
                                    visible: false
                                }

                                ColorOverlay {
                                    anchors.fill: trayIcon
                                    source: trayIcon
                                    color: Theme.base05
                                }

                                MouseArea {
                                    anchors.fill: parent
                                    hoverEnabled: true
                                    acceptedButtons: Qt.NoButton
                                    onEntered: {
                                        if (bar.activeDropdown) {
                                            bar.activeDropdown.resetAutoClose();
                                            if (modelData.hasMenu && !(bar.activeDropdown === contextMenu && contextMenu.trayItem === modelData)) {
                                                if (bar.activeDropdown === contextMenu) {
                                                    // Same dropdown, just switch content
                                                    let pos = parent.mapToItem(bar.contentItem, parent.width / 2, 0);
                                                    contextMenu.dropdownX = pos.x;
                                                    contextMenu.trayItem = modelData;
                                                    menuOpener.menu = modelData.menu;
                                                    contextMenu.resetAutoClose();
                                                } else {
                                                    bar.toggleDropdown(contextMenu, function() {
                                                        let pos = parent.mapToItem(bar.contentItem, parent.width / 2, 0);
                                                        contextMenu.dropdownX = pos.x;
                                                        contextMenu.trayItem = modelData;
                                                        menuOpener.menu = modelData.menu;
                                                    });
                                                }
                                            }
                                        }
                                    }
                                }
                                MouseArea {
                                    anchors.fill: parent
                                    acceptedButtons: Qt.LeftButton | Qt.RightButton
                                    onClicked: (event) => {
                                        if (modelData.hasMenu) {
                                            bar.toggleDropdown(contextMenu, function() {
                                                let pos = parent.mapToItem(bar.contentItem, parent.width / 2, 0);
                                                contextMenu.dropdownX = pos.x;
                                                contextMenu.trayItem = modelData;
                                                menuOpener.menu = modelData.menu;
                                            });
                                        } else {
                                            modelData.activate();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                // Reusable dropdown component
                component BarDropdown: PopupWindow {
                    id: dropdown
                    property bool open: false
                    property bool closing: false
                    property real dropdownX: 0
                    property real fullWidth: 200
                    property real fullHeight: 200
                    property int autoCloseMs: 1500
                    property bool alignRight: false
                    default property alias content: dropdownContent.data

                    function animateClose() {
                        if (!visible || closing) return;
                        closing = true;
                        open = false;
                        _autoClose.stop();
                        _closeDelay.start();
                    }

                    function resetAutoClose() {
                        if (visible && !closing) _autoClose.restart();
                    }

                    anchor.window: bar
                    anchor.rect.x: alignRight ? bar.width - fullWidth - 8 : dropdownX - (fullWidth + 16) / 2
                    anchor.rect.y: bar.height
                    anchor.edges: Edges.Top | Edges.Left
                    anchor.gravity: Edges.Bottom | Edges.Right
                    anchor.adjustment: alignRight ? PopupAdjustment.None : PopupAdjustment.Slide
                    visible: false
                    color: "transparent"
                    implicitWidth: fullWidth + (alignRight ? 8 : 16)
                    implicitHeight: fullHeight + 4 + (alignRight ? 8 : 0)

                    onVisibleChanged: {
                        if (visible) {
                            closing = false;
                            open = true;
                            _autoClose.restart();
                        } else {
                            open = false;
                            closing = false;
                            _autoClose.stop();
                        }
                    }

                    Timer {
                        id: _autoClose
                        interval: dropdown.autoCloseMs
                        onTriggered: bar.closeAllDropdowns()
                    }

                    Timer {
                        id: _closeDelay
                        interval: 230
                        onTriggered: { dropdown.visible = false; dropdown.closing = false; }
                    }

                    HoverHandler {
                        onHoveredChanged: {
                            if (hovered) _autoClose.stop();
                            else _autoClose.restart();
                        }
                    }

                    // Left ear
                    Item {
                        anchors.right: _dropdownRect.left
                        anchors.top: parent.top
                        width: 8
                        height: Math.min(8, _dropdownRect.height)
                        clip: true
                        visible: _dropdownRect.height > 0
                        Canvas {
                            anchors.top: parent.top
                            width: 8; height: 8
                            onPaint: {
                                var ctx = getContext("2d");
                                ctx.clearRect(0, 0, 8, 8);
                                ctx.fillStyle = Theme.barBg;
                                ctx.beginPath();
                                ctx.moveTo(0, 0); ctx.lineTo(8, 0); ctx.lineTo(8, 8);
                                ctx.arc(0, 8, 8, 0, -Math.PI / 2, true);
                                ctx.closePath(); ctx.fill();
                            }
                        }
                    }

                    // Right ear (for centered dropdowns)
                    Item {
                        anchors.left: _dropdownRect.right
                        anchors.top: parent.top
                        width: 8
                        height: Math.min(8, _dropdownRect.height)
                        clip: true
                        visible: _dropdownRect.height > 0 && !dropdown.alignRight
                        Canvas {
                            anchors.top: parent.top
                            width: 8; height: 8
                            onPaint: {
                                var ctx = getContext("2d");
                                ctx.clearRect(0, 0, 8, 8);
                                ctx.fillStyle = Theme.barBg;
                                ctx.beginPath();
                                ctx.moveTo(0, 0); ctx.lineTo(8, 0);
                                ctx.arc(8, 8, 8, -Math.PI / 2, Math.PI, true);
                                ctx.closePath(); ctx.fill();
                            }
                        }
                    }

                    Rectangle {
                        id: _dropdownRect
                        anchors.right: dropdown.alignRight ? parent.right : undefined
                        anchors.horizontalCenter: dropdown.alignRight ? undefined : parent.horizontalCenter
                        anchors.top: parent.top
                        width: dropdown.fullWidth
                        height: dropdown.open ? dropdown.fullHeight : 0
                        color: Theme.barBg
                        radius: 8
                        topLeftRadius: 0
                        topRightRadius: 0
                        bottomRightRadius: dropdown.alignRight ? 0 : 8
                        clip: true

                        Behavior on height {
                            NumberAnimation { duration: 220; easing.type: Easing.OutCubic }
                        }

                        Item {
                            id: dropdownContent
                            anchors.fill: parent
                        }
                    }

                    // Bottom-right concave ear — connects dropdown bottom to right screen edge
                    Item {
                        visible: dropdown.alignRight && _dropdownRect.height > 0
                        anchors.right: _dropdownRect.right
                        anchors.top: _dropdownRect.bottom
                        width: 8
                        height: Math.min(8, _dropdownRect.height)
                        clip: true
                        Canvas {
                            width: 8; height: 8
                            onPaint: {
                                var ctx = getContext("2d");
                                ctx.clearRect(0, 0, 8, 8);
                                ctx.fillStyle = Theme.barBg;
                                ctx.beginPath();
                                ctx.moveTo(0, 0);
                                ctx.lineTo(8, 0);
                                ctx.lineTo(8, 8);
                                ctx.arc(0, 8, 8, 0, -Math.PI / 2, true);
                                ctx.fill();
                            }
                        }
                    }
                }

                // Context menu
                BarDropdown {
                    id: contextMenu
                    alignRight: true
                    property var trayItem: null
                    fullWidth: menuItems.width + 24
                    fullHeight: menuItems.height + 16

                    onVisibleChanged: {
                        if (!visible) menuOpener.menu = null;
                    }

                    QsMenuOpener {
                        id: menuOpener
                    }

                    Column {
                        id: menuItems
                        anchors.centerIn: parent
                        width: 200

                            Repeater {
                                model: menuOpener.children

                                Rectangle {
                                    required property var modelData
                                    width: 200
                                    height: modelData.isSeparator ? 9 : 28
                                    color: !modelData.isSeparator && itemMouse.containsMouse && modelData.enabled
                                           ? Theme.base02 : "transparent"
                                    radius: modelData.isSeparator ? 0 : 4

                                    Rectangle {
                                        visible: modelData.isSeparator
                                        anchors.centerIn: parent
                                        width: parent.width - 20
                                        height: 1
                                        color: Theme.base03
                                    }

                                    RowLayout {
                                        visible: !modelData.isSeparator
                                        anchors.fill: parent
                                        anchors.leftMargin: 10
                                        anchors.rightMargin: 10
                                        spacing: 8

                                        Text {
                                            Layout.fillWidth: true
                                            text: modelData.text ?? ""
                                            color: modelData.enabled ? Theme.base05 : Theme.base03
                                            font.family: "FiraMono Nerd Font"
                                            font.pixelSize: 12
                                            elide: Text.ElideRight
                                        }

                                        Text {
                                            visible: modelData.buttonType !== QsMenuButtonType.None
                                            text: modelData.checkState === Qt.Checked ? "\u2713" : ""
                                            color: Theme.base0D
                                            font.family: "FiraMono Nerd Font"
                                            font.pixelSize: 12
                                        }
                                    }

                                    MouseArea {
                                        id: itemMouse
                                        anchors.fill: parent
                                        hoverEnabled: true
                                        enabled: !modelData.isSeparator && modelData.enabled
                                        onClicked: {
                                            modelData.triggered();
                                            bar.closeAllDropdowns();
                                        }
                                    }
                                }
                            }
                    }
                }

                // Volume dropdown
                BarDropdown {
                    id: volDropdown
                    alignRight: true
                    fullWidth: volDropdownCol.width + 28
                    fullHeight: volDropdownCol.height + 20
                    autoCloseMs: 3000

                    Column {
                        id: volDropdownCol
                        anchors.centerIn: parent
                        width: 260
                            spacing: 8

                            // Master volume
                            Text {
                                text: "\u{f057e}  Master"
                                color: Theme.base05
                                font.family: "FiraMono Nerd Font"
                                font.pixelSize: 13
                                font.weight: Font.Medium
                            }

                            Row {
                                width: parent.width
                                spacing: 8

                                Rectangle {
                                    id: masterSliderBg
                                    width: parent.width - masterVolLabel.width - 8
                                    height: 20
                                    radius: 4
                                    color: Theme.base01
                                    anchors.verticalCenter: parent.verticalCenter

                                    Rectangle {
                                        width: volWidget.sink && volWidget.sink.audio
                                            ? Math.min(1, volWidget.sink.audio.volume) * parent.width : 0
                                        height: parent.height
                                        radius: 4
                                        color: volWidget.muted ? Theme.base03 : Theme.base0D
                                        Behavior on width { NumberAnimation { duration: 80 } }
                                    }

                                    MouseArea {
                                        anchors.fill: parent
                                        onPressed: (mouse) => setVolume(mouse)
                                        onPositionChanged: (mouse) => { if (pressed) setVolume(mouse); }
                                        function setVolume(mouse) {
                                            if (!volWidget.sink || !volWidget.sink.audio) return;
                                            let v = Math.max(0, Math.min(1, mouse.x / width));
                                            volWidget.sink.audio.volume = v;
                                        }
                                    }
                                }

                                Text {
                                    id: masterVolLabel
                                    width: 36
                                    text: volWidget.vol + "%"
                                    color: Theme.base05
                                    font.family: "FiraMono Nerd Font"
                                    font.pixelSize: 11
                                    horizontalAlignment: Text.AlignRight
                                    anchors.verticalCenter: parent.verticalCenter
                                }
                            }

                            // Mute button
                            Rectangle {
                                width: parent.width
                                height: 28
                                color: masterMuteMa.containsMouse ? Theme.base02 : "transparent"
                                radius: 4

                                Text {
                                    anchors.centerIn: parent
                                    text: volWidget.muted ? "\u{f0581}  Unmute" : "\u{f057e}  Mute"
                                    color: Theme.base05
                                    font.family: "FiraMono Nerd Font"
                                    font.pixelSize: 12
                                }
                                MouseArea {
                                    id: masterMuteMa
                                    anchors.fill: parent
                                    hoverEnabled: true
                                    cursorShape: Qt.PointingHandCursor
                                    onClicked: {
                                        if (volWidget.sink && volWidget.sink.audio)
                                            volWidget.sink.audio.muted = !volWidget.sink.audio.muted;
                                    }
                                }
                            }

                            // Separator
                            Rectangle {
                                width: parent.width - 20
                                anchors.horizontalCenter: parent.horizontalCenter
                                height: 1
                                color: Theme.base02
                                visible: appStreamsCol.childrenRect.height > 0
                            }

                            // App streams header
                            Text {
                                visible: appStreamsCol.childrenRect.height > 0
                                text: "\u{f0641}  Applications"
                                color: Theme.base05
                                font.family: "FiraMono Nerd Font"
                                font.pixelSize: 13
                                font.weight: Font.Medium
                            }

                            // Per-app streams
                            Column {
                                id: appStreamsCol
                                width: parent.width
                                spacing: 6

                                Repeater {
                                    id: appStreamsRepeater
                                    model: Pipewire.nodes

                                    Column {
                                        required property var modelData
                                        width: parent.width
                                        spacing: 2
                                        visible: modelData.isStream && modelData.audio !== null

                                        PwObjectTracker {
                                            objects: [modelData]
                                        }

                                        Text {
                                            text: modelData.properties["application.name"] || modelData.name || "Unknown"
                                            color: Theme.base04
                                            font.family: "FiraMono Nerd Font"
                                            font.pixelSize: 11
                                            elide: Text.ElideRight
                                            width: parent.width
                                        }

                                        Row {
                                            width: parent.width
                                            spacing: 8

                                            Rectangle {
                                                width: parent.width - appVolLabel.width - 8
                                                height: 16
                                                radius: 3
                                                color: Theme.base01
                                                anchors.verticalCenter: parent.verticalCenter

                                                Rectangle {
                                                    width: modelData.audio
                                                        ? Math.min(1, modelData.audio.volume) * parent.width : 0
                                                    height: parent.height
                                                    radius: 3
                                                    color: modelData.audio && modelData.audio.muted
                                                        ? Theme.base03 : Theme.base0C
                                                    Behavior on width { NumberAnimation { duration: 80 } }
                                                }

                                                MouseArea {
                                                    anchors.fill: parent
                                                    onPressed: (mouse) => setVol(mouse)
                                                    onPositionChanged: (mouse) => { if (pressed) setVol(mouse); }
                                                    function setVol(mouse) {
                                                        if (!modelData.audio) return;
                                                        let v = Math.max(0, Math.min(1, mouse.x / width));
                                                        modelData.audio.volume = v;
                                                    }
                                                }
                                            }

                                            Text {
                                                id: appVolLabel
                                                width: 36
                                                text: modelData.audio ? Math.round(modelData.audio.volume * 100) + "%" : "0%"
                                                color: Theme.base04
                                                font.family: "FiraMono Nerd Font"
                                                font.pixelSize: 10
                                                horizontalAlignment: Text.AlignRight
                                                anchors.verticalCenter: parent.verticalCenter
                                            }
                                        }
                                    }
                                }
                            }
                    }
                }

                // Network dropdown
                BarDropdown {
                    id: netDropdown
                    alignRight: true
                    fullWidth: netDropdownCol.width + 28
                    fullHeight: netDropdownCol.height + 20

                    Column {
                        id: netDropdownCol
                        anchors.centerIn: parent
                        width: 220
                            spacing: 4

                            Text {
                                width: parent.width
                                text: netWidget.netState === "connected"
                                    ? "\u{f05a9}  " + netWidget.netConn
                                    : "\u{f05aa}  Not connected"
                                color: Theme.base05
                                font.family: "FiraMono Nerd Font"
                                font.pixelSize: 13
                                font.weight: Font.Medium
                                elide: Text.ElideRight
                            }

                            Rectangle {
                                visible: netWidget.netState === "connected"
                                width: parent.width
                                height: 28
                                color: disconnectMouse.containsMouse ? Theme.base02 : "transparent"
                                radius: 4

                                Text {
                                    anchors.centerIn: parent
                                    text: "Disconnect"
                                    color: Theme.base08
                                    font.family: "FiraMono Nerd Font"
                                    font.pixelSize: 12
                                }

                                MouseArea {
                                    id: disconnectMouse
                                    anchors.fill: parent
                                    hoverEnabled: true
                                    onClicked: {
                                        netDisconnectProc.targetDevice = netWidget.netDevice;
                                        netDisconnectProc.running = true;
                                        netWidget.netState = "disconnected";
                                        netWidget.netConn = "";
                                        netWidget.netIcon = "\u{f05aa}";
                                        bar.closeAllDropdowns();
                                        netRefreshDelay.start();
                                    }
                                }
                            }

                            Rectangle {
                                width: parent.width - 20
                                anchors.horizontalCenter: parent.horizontalCenter
                                height: 1
                                color: Theme.base03
                            }

                            Text {
                                text: "Available networks"
                                color: Theme.base03
                                font.family: "FiraMono Nerd Font"
                                font.pixelSize: 11
                                topPadding: 2
                            }

                            Repeater {
                                model: netWidget.wifiNetworks

                                Rectangle {
                                    required property var modelData
                                    width: 220
                                    height: 32
                                    color: netItemMouse.containsMouse ? Theme.base02 : "transparent"
                                    radius: 4

                                    Row {
                                        anchors.verticalCenter: parent.verticalCenter
                                        anchors.left: parent.left
                                        anchors.leftMargin: 8
                                        anchors.right: parent.right
                                        anchors.rightMargin: 8
                                        spacing: 8

                                        Text {
                                            text: {
                                                let s = modelData.signal;
                                                if (s >= 75) return "\u{f05a9}";
                                                if (s >= 50) return "\u{f05a9}";
                                                if (s >= 25) return "\u{f05a9}";
                                                return "\u{f05aa}";
                                            }
                                            color: modelData.active ? Theme.base0B : Theme.base04
                                            font.family: "FiraMono Nerd Font"
                                            font.pixelSize: 13
                                            anchors.verticalCenter: parent.verticalCenter
                                        }

                                        Text {
                                            text: modelData.ssid
                                            color: modelData.active ? Theme.base0B : Theme.base05
                                            font.family: "FiraMono Nerd Font"
                                            font.pixelSize: 12
                                            elide: Text.ElideRight
                                            width: 140
                                            anchors.verticalCenter: parent.verticalCenter
                                        }

                                        Text {
                                            visible: modelData.security !== "" && modelData.security !== "--"
                                            text: "\u{f0341}"
                                            color: Theme.base03
                                            font.family: "FiraMono Nerd Font"
                                            font.pixelSize: 10
                                            anchors.verticalCenter: parent.verticalCenter
                                        }
                                    }

                                    MouseArea {
                                        id: netItemMouse
                                        anchors.fill: parent
                                        hoverEnabled: true
                                        onClicked: {
                                            if (!modelData.active) {
                                                wifiConnectProc.targetSsid = modelData.ssid;
                                                wifiConnectProc.running = true;
                                                netRefreshDelay.start();
                                            }
                                            bar.closeAllDropdowns();
                                        }
                                    }
                                }
                            }
                    }
                }

                ${lib.optionalString isMacbook ''
                // Battery dropdown
                BarDropdown {
                    id: batteryDropdown
                    alignRight: true
                    fullWidth: batteryDropdownCol.width + 28
                    fullHeight: batteryDropdownCol.height + 20

                    Column {
                        id: batteryDropdownCol
                        anchors.centerIn: parent
                        width: 200
                            spacing: 8

                            Row {
                                width: parent.width
                                spacing: 8

                                Text {
                                    text: batteryWidget.batteryIcon
                                    color: Theme.base05
                                    font.family: "FiraMono Nerd Font"
                                    font.pixelSize: 18
                                    anchors.verticalCenter: parent.verticalCenter
                                }

                                Column {
                                    anchors.verticalCenter: parent.verticalCenter
                                    Text {
                                        text: batteryWidget.batteryLevel + "%" + (batteryWidget.charging ? " — Charging" : "")
                                        color: Theme.base05
                                        font.family: "FiraMono Nerd Font"
                                        font.pixelSize: 13
                                        font.weight: Font.Medium
                                    }
                                    Text {
                                        text: batteryWidget.powerDraw.toFixed(1) + " W"
                                            + (batteryWidget.timeRemaining !== "" ? "  \u2022  " + batteryWidget.timeRemaining + (batteryWidget.charging ? " to full" : " left") : "")
                                        color: Theme.base04
                                        font.family: "FiraMono Nerd Font"
                                        font.pixelSize: 11
                                    }
                                }
                            }

                            Rectangle {
                                width: parent.width - 10
                                anchors.horizontalCenter: parent.horizontalCenter
                                height: 1
                                color: Theme.base03
                            }

                            Text {
                                text: "Power Profile"
                                color: Theme.base03
                                font.family: "FiraMono Nerd Font"
                                font.pixelSize: 11
                            }

                            Row {
                                width: parent.width
                                spacing: 4

                                Repeater {
                                    model: [
                                        { name: "power-saver", label: "\u{f0425}", tip: "Saver" },
                                        { name: "balanced", label: "\u{f0376}", tip: "Balanced" },
                                        { name: "performance", label: "\u{f0e0e}", tip: "Performance" }
                                    ]

                                    Rectangle {
                                        required property var modelData
                                        width: (parent.width - 8) / 3
                                        height: 36
                                        radius: 6
                                        color: batteryWidget.powerProfile === modelData.name
                                            ? Theme.base02 : profMouse.containsMouse
                                            ? Theme.base01 : "transparent"
                                        border.width: batteryWidget.powerProfile === modelData.name ? 1 : 0
                                        border.color: Theme.base03

                                        Column {
                                            anchors.centerIn: parent
                                            spacing: 1
                                            Text {
                                                anchors.horizontalCenter: parent.horizontalCenter
                                                text: modelData.label
                                                color: batteryWidget.powerProfile === modelData.name
                                                    ? Theme.base0D : Theme.base05
                                                font.family: "FiraMono Nerd Font"
                                                font.pixelSize: 14
                                            }
                                            Text {
                                                anchors.horizontalCenter: parent.horizontalCenter
                                                text: modelData.tip
                                                color: Theme.base04
                                                font.family: "FiraMono Nerd Font"
                                                font.pixelSize: 9
                                            }
                                        }

                                        MouseArea {
                                            id: profMouse
                                            anchors.fill: parent
                                            hoverEnabled: true
                                            onClicked: {
                                                setProfileProc.target = modelData.name;
                                                setProfileProc.running = true;
                                                batteryWidget.powerProfile = modelData.name;
                                            }
                                        }
                                    }
                                }
                            }
                    }
                }
                ''}

                // Calendar popup
                BarDropdown {
                    id: calPopup
                    dropdownX: bar.width / 2
                    fullWidth: calCol.width + 8
                    fullHeight: calCol.height + 4

                    Column {
                        id: calCol
                        anchors.horizontalCenter: parent.horizontalCenter
                        anchors.top: parent.top

                        spacing: 8
                        opacity: calPopup.open ? 1.0 : 0.0

                        Behavior on opacity {
                            NumberAnimation { duration: 150; easing.type: Easing.OutCubic }
                        }

                        Text {
                            id: calTitle
                            anchors.horizontalCenter: parent.horizontalCenter
                            text: clockText.now.toLocaleDateString(Qt.locale(), "dddd, d MMMM yyyy")
                            color: Theme.base05
                            font.family: "FiraMono Nerd Font"
                            font.pixelSize: 16
                            font.weight: Font.Medium
                        }

                        Row {
                            id: weekdayRow
                            anchors.horizontalCenter: parent.horizontalCenter
                            spacing: 0
                            Repeater {
                                model: ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"]
                                Text {
                                    required property var modelData
                                    width: 32
                                    horizontalAlignment: Text.AlignHCenter
                                    text: modelData
                                    color: Theme.base04
                                    font.family: "FiraMono Nerd Font"
                                    font.pixelSize: 13
                                }
                            }
                        }

                        Grid {
                                            columns: 7
                                            spacing: 0

                                            Repeater {
                                                id: calRepeater
                                                model: 42

                                                Rectangle {
                                                    required property int index
                                                    width: 32
                                                    height: 26
                                                    radius: 4
                                                    color: {
                                                        let d = clockText.now;
                                                        let first = new Date(d.getFullYear(), d.getMonth(), 1);
                                                        let startDay = (first.getDay() + 6) % 7;
                                                        let dayNum = index - startDay + 1;
                                                        let daysInMonth = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
                                                        return (dayNum === d.getDate() && dayNum >= 1 && dayNum <= daysInMonth)
                                                            ? Theme.base03 : "transparent";
                                                    }

                                                    Text {
                                                        anchors.centerIn: parent
                                                        text: {
                                                            let d = clockText.now;
                                                            let first = new Date(d.getFullYear(), d.getMonth(), 1);
                                                            let startDay = (first.getDay() + 6) % 7;
                                                            let dayNum = parent.index - startDay + 1;
                                                            let daysInMonth = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
                                                            return (dayNum >= 1 && dayNum <= daysInMonth) ? dayNum.toString() : "";
                                                        }
                                                        color: {
                                                            let d = clockText.now;
                                                            let first = new Date(d.getFullYear(), d.getMonth(), 1);
                                                            let startDay = (first.getDay() + 6) % 7;
                                                            let dayNum = parent.index - startDay + 1;
                                                            return (dayNum === d.getDate()) ? Theme.base05 : Theme.base04;
                                                        }
                                                        font.family: "FiraMono Nerd Font"
                                                        font.pixelSize: 13
                                                    }
                                                }
                            }
                        }

                        Rectangle {
                            width: 7 * 32 + 8
                            height: 1
                            color: Theme.base02
                            anchors.horizontalCenter: parent.horizontalCenter
                        }

                                Row {
                                    width: 7 * 32 + 8
                                    anchors.horizontalCenter: parent.horizontalCenter
                                    Text {
                                        text: "Notifications"
                                        color: Theme.base05
                                        font.family: "FiraMono Nerd Font"
                                        font.pixelSize: 13
                                        font.weight: Font.Medium
                                    }
                                    Item { Layout.fillWidth: true; width: 10 }
                                    Text {
                                        anchors.right: parent.right
                                        text: bar.notifServer.trackedNotifications.values.length > 0 ? "Clear all" : ""
                                        color: Theme.base04
                                        font.family: "FiraMono Nerd Font"
                                        font.pixelSize: 11
                                        MouseArea {
                                            anchors.fill: parent
                                            cursorShape: Qt.PointingHandCursor
                                            onClicked: {
                                                let notifs = bar.notifServer.trackedNotifications.values;
                                                for (let i = notifs.length - 1; i >= 0; i--) {
                                                    notifs[i].dismiss();
                                                }
                                            }
                                        }
                                    }
                                }

                                Column {
                                    spacing: 4
                                    width: 7 * 32 + 8
                                    anchors.horizontalCenter: parent.horizontalCenter

                                    Text {
                                        visible: bar.notifServer.trackedNotifications.values.length === 0
                                        text: "No notifications"
                                        color: Theme.base03
                                        font.family: "FiraMono Nerd Font"
                                        font.pixelSize: 11
                                        anchors.horizontalCenter: parent.horizontalCenter
                                    }

                                    Repeater {
                                        model: bar.notifServer.trackedNotifications

                                        Rectangle {
                                            id: notifItem
                                            required property var modelData
                                            width: 7 * 32 + 8
                                            height: notifCol.height + 12
                                            radius: 6
                                            color: Theme.base01

                                    Column {
                                        id: notifCol
                                        anchors.left: parent.left
                                        anchors.right: dismissBtn.left
                                        anchors.top: parent.top
                                        anchors.margins: 6
                                        spacing: 2

                                        Text {
                                            width: parent.width
                                            text: notifItem.modelData.summary || notifItem.modelData.appName
                                            color: Theme.base05
                                            font.family: "FiraMono Nerd Font"
                                            font.pixelSize: 11
                                            font.weight: Font.Medium
                                            elide: Text.ElideRight
                                        }

                                        Text {
                                            width: parent.width
                                            text: notifItem.modelData.body || ""
                                            color: Theme.base04
                                            font.family: "FiraMono Nerd Font"
                                            font.pixelSize: 10
                                            elide: Text.ElideRight
                                            maximumLineCount: 2
                                            wrapMode: Text.Wrap
                                            visible: text !== ""
                                        }

                                        Row {
                                            spacing: 4
                                            visible: notifItem.modelData.actions.length > 0
                                            Repeater {
                                                model: notifItem.modelData.actions
                                                Rectangle {
                                                    required property var modelData
                                                    width: actionText.width + 12
                                                    height: actionText.height + 4
                                                    radius: 4
                                                    color: actionMa.containsMouse ? Theme.base02 : Theme.base01
                                                    border.width: 1
                                                    border.color: Theme.base02
                                                    Text {
                                                        id: actionText
                                                        anchors.centerIn: parent
                                                        text: modelData.text
                                                        color: Theme.base05
                                                        font.family: "FiraMono Nerd Font"
                                                        font.pixelSize: 10
                                                    }
                                                    MouseArea {
                                                        id: actionMa
                                                        anchors.fill: parent
                                                        hoverEnabled: true
                                                        cursorShape: Qt.PointingHandCursor
                                                        onClicked: modelData.invoke()
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    Text {
                                        id: dismissBtn
                                        anchors.right: parent.right
                                        anchors.top: parent.top
                                        anchors.margins: 6
                                        text: "\u{f0156}"
                                        color: dismissMa.containsMouse ? Theme.base05 : Theme.base03
                                        font.family: "FiraMono Nerd Font"
                                        font.pixelSize: 12
                                        MouseArea {
                                            id: dismissMa
                                            anchors.fill: parent
                                            hoverEnabled: true
                                            cursorShape: Qt.PointingHandCursor
                                            onClicked: notifItem.modelData.dismiss()
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
          '';
        };

        "quickshell/NotificationToast.qml" = {
          onChange = qsRestart;
          text = ''
            import Quickshell
            import Quickshell.Wayland
            import Quickshell.Io
            import QtQuick

            PanelWindow {
                id: notifToast
                required property var shellRoot
                screen: Quickshell.screens[0]
                property var currentNotif: null
                property bool open: false

                readonly property var mutedApps: ["discord", "Discord", "Spotify", "spotify", "vlc", "mpv"]

                WlrLayershell.layer: WlrLayer.Overlay
                WlrLayershell.namespace: "quickshell-toast"
                exclusionMode: ExclusionMode.Ignore

                Process {
                    id: notifSoundProc
                    command: [Commands.notifSound, "-i", "message"]
                }

                Connections {
                    target: shellRoot
                    function onNotificationReceived() {
                        notifToast.show(shellRoot.latestNotification);
                    }
                }

                function show(notification) {
                    currentNotif = notification;
                    visible = true;
                    open = true;
                    _toastTimer.restart();
                    if (!mutedApps.includes(notification.appName)) {
                        notifSoundProc.running = true;
                    }
                }

                function dismiss() {
                    open = false;
                    _toastCloseDelay.start();
                }

                anchors.top: true
                margins.top: 30
                visible: false
                implicitWidth: 320 + 16
                implicitHeight: _toastRect.height + 4
                color: "transparent"

                Timer {
                    id: _toastTimer
                    interval: 5000
                    onTriggered: notifToast.dismiss()
                }

                Timer {
                    id: _toastCloseDelay
                    interval: 230
                    onTriggered: { notifToast.visible = false; notifToast.open = false; }
                }

                HoverHandler {
                    onHoveredChanged: {
                        if (hovered) _toastTimer.stop();
                        else _toastTimer.restart();
                    }
                }

                // Left inverse corner ear
                Item {
                    anchors.right: _toastRect.left
                    anchors.top: parent.top
                    width: 8
                    height: Math.min(8, _toastRect.height)
                    clip: true
                    visible: _toastRect.height > 0
                    Canvas {
                        anchors.top: parent.top
                        width: 8; height: 8
                        onPaint: {
                            var ctx = getContext("2d");
                            ctx.clearRect(0, 0, 8, 8);
                            ctx.fillStyle = Theme.barBg;
                            ctx.beginPath();
                            ctx.moveTo(0, 0); ctx.lineTo(8, 0); ctx.lineTo(8, 8);
                            ctx.arc(0, 8, 8, 0, -Math.PI / 2, true);
                            ctx.closePath(); ctx.fill();
                        }
                    }
                }

                // Right inverse corner ear
                Item {
                    anchors.left: _toastRect.right
                    anchors.top: parent.top
                    width: 8
                    height: Math.min(8, _toastRect.height)
                    clip: true
                    visible: _toastRect.height > 0
                    Canvas {
                        anchors.top: parent.top
                        width: 8; height: 8
                        onPaint: {
                            var ctx = getContext("2d");
                            ctx.clearRect(0, 0, 8, 8);
                            ctx.fillStyle = Theme.barBg;
                            ctx.beginPath();
                            ctx.moveTo(0, 0); ctx.lineTo(8, 0);
                            ctx.arc(8, 8, 8, -Math.PI / 2, Math.PI, true);
                            ctx.closePath(); ctx.fill();
                        }
                    }
                }

                Rectangle {
                    id: _toastRect
                    anchors.horizontalCenter: parent.horizontalCenter
                    anchors.top: parent.top
                    width: 320
                    height: notifToast.open ? toastCol.height + 16 : 0
                    color: Theme.barBg
                    radius: 8
                    topLeftRadius: 0
                    topRightRadius: 0
                    clip: true

                    Behavior on height {
                        NumberAnimation { duration: 220; easing.type: Easing.OutCubic }
                    }

                    Column {
                        id: toastCol
                        anchors.left: parent.left
                        anchors.right: toastDismiss.left
                        anchors.top: parent.top
                        anchors.margins: 8
                        spacing: 2

                        Text {
                            width: parent.width
                            text: notifToast.currentNotif ? (notifToast.currentNotif.summary || notifToast.currentNotif.appName) : ""
                            color: Theme.base05
                            font.family: "FiraMono Nerd Font"
                            font.pixelSize: 12
                            font.weight: Font.Medium
                            elide: Text.ElideRight
                        }

                        Text {
                            width: parent.width
                            text: notifToast.currentNotif ? (notifToast.currentNotif.body || "") : ""
                            color: Theme.base04
                            font.family: "FiraMono Nerd Font"
                            font.pixelSize: 11
                            elide: Text.ElideRight
                            maximumLineCount: 3
                            wrapMode: Text.Wrap
                            visible: text !== ""
                        }

                        Row {
                            spacing: 4
                            visible: notifToast.currentNotif && notifToast.currentNotif.actions.length > 0
                            Repeater {
                                model: notifToast.currentNotif ? notifToast.currentNotif.actions : []
                                Rectangle {
                                    required property var modelData
                                    width: toastActionText.width + 12
                                    height: toastActionText.height + 6
                                    radius: 4
                                    color: toastActionMa.containsMouse ? Theme.base02 : Theme.base01
                                    border.width: 1
                                    border.color: Theme.base02
                                    Text {
                                        id: toastActionText
                                        anchors.centerIn: parent
                                        text: modelData.text
                                        color: Theme.base05
                                        font.family: "FiraMono Nerd Font"
                                        font.pixelSize: 10
                                    }
                                    MouseArea {
                                        id: toastActionMa
                                        anchors.fill: parent
                                        hoverEnabled: true
                                        cursorShape: Qt.PointingHandCursor
                                        onClicked: { modelData.invoke(); notifToast.dismiss(); }
                                    }
                                }
                            }
                        }
                    }

                    Text {
                        id: toastDismiss
                        anchors.right: parent.right
                        anchors.top: parent.top
                        anchors.margins: 8
                        text: "\u{f0156}"
                        color: toastDismissMa.containsMouse ? Theme.base05 : Theme.base03
                        font.family: "FiraMono Nerd Font"
                        font.pixelSize: 13
                        MouseArea {
                            id: toastDismissMa
                            anchors.fill: parent
                            hoverEnabled: true
                            cursorShape: Qt.PointingHandCursor
                            onClicked: { notifToast.currentNotif.dismiss(); notifToast.dismiss(); }
                        }
                    }
                }
            }
          '';
        };
      };

    };
  };
}
