From 3ba7553f9a03305ecfbff5d0e8a3a3901845e527 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Mon, 30 Sep 2024 13:41:38 +0200
Subject: [PATCH 01/64] add alpine.js

---
 header_layout.ml | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/header_layout.ml b/header_layout.ml
index 39d19620..b33d8d12 100644
--- a/header_layout.ml
+++ b/header_layout.ml
@@ -22,4 +22,12 @@ let header ?(page_title = "Mollymawk") ~icon () =
           ~href:"https://unpkg.com/aos@2.3.1/dist/aos.css" ();
         script ~a:[ a_src "https://unpkg.com/aos@2.3.1/dist/aos.js" ] (txt "");
         link ~rel:[ `Icon ] ~href:icon ();
+        script
+          ~a:
+            [
+              a_defer ();
+              a_src
+                "https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js";
+            ]
+          (txt "");
       ])

From 4ee6373193835367aa169cd4a583efb5366ae717 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Mon, 30 Sep 2024 13:42:05 +0200
Subject: [PATCH 02/64] edit and update policies

---
 unikernel.ml     | 141 +++++++++++++++
 update_policy.ml | 456 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 597 insertions(+)
 create mode 100644 update_policy.ml

diff --git a/unikernel.ml b/unikernel.ml
index bcc2bc0f..46b7d936 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -779,6 +779,135 @@ struct
                 ~icon:"/images/robur.png" ())
              `Not_found)
 
+  let edit_policy albatross store uuid reqd (user : User_model.user) =
+    let users = User_model.create_user_uuid_map (snd store).Storage.users in
+    match User_model.find_user_by_key uuid users with
+    | Some u ->
+        let policy =
+          match Albatross.policy albatross u.name with
+          | Ok p -> p
+          | Error _ -> None
+        in
+        (Albatross.query albatross ~domain:"." (`Policy_cmd `Policy_info)
+         >|= function
+         | Error msg ->
+             Logs.err (fun m ->
+                 m "error while communicating with albatross: %s" msg);
+             []
+         | Ok (_hdr, `Success (`Policies policies)) -> policies
+         | Ok reply ->
+             Logs.err (fun m ->
+                 m "expected a policy info reply, received %a"
+                   (Vmm_commands.pp_wire ~verbose:false)
+                   reply);
+             [])
+        >>= fun root_policy ->
+        Lwt.return
+          (reply reqd ~content_type:"text/html"
+             (Dashboard.dashboard_layout user
+                ~page_title:(String.capitalize_ascii u.name ^ " | Mollymawk")
+                ~content:
+                  (Update_policy.update_policy_layout u ~user_policy:policy
+                     ~root_policy)
+                ~icon:"/images/robur.png" ())
+             `OK)
+    | None ->
+        let status =
+          {
+            Utils.Status.code = 404;
+            title = "Error";
+            data = "Couldn't find account with uuid: " ^ uuid;
+            success = false;
+          }
+        in
+        Lwt.return
+          (reply reqd ~content_type:"text/html"
+             (Guest_layout.guest_layout ~page_title:"404 | Mollymawk"
+                ~content:(Error_page.error_layout status)
+                ~icon:"/images/robur.png" ())
+             `Not_found)
+
+  let update_policy store albatross reqd _user =
+    decode_request_body reqd >>= fun data ->
+    let json =
+      try Ok (Yojson.Basic.from_string data)
+      with Yojson.Json_error s -> Error (`Msg s)
+    in
+    match json with
+    | Error (`Msg err) ->
+        Logs.warn (fun m -> m "Failed to parse JSON: %s" err);
+        http_response reqd ~title:"Error" ~data:(String.escaped err)
+          `Bad_request
+    | Ok json -> (
+        match json with
+        | `Assoc xs -> (
+            match
+              Utils.Json.
+                ( get "vms" xs,
+                  get "memory" xs,
+                  get "block" xs,
+                  get "cpuids" xs,
+                  get "bridges" xs,
+                  get "user_uuid" xs )
+            with
+            | ( Some (`Int vms),
+                Some (`Int memory),
+                Some (`Int block),
+                Some (`String cpuids),
+                Some (`String bridges),
+                Some (`String user_uuid) ) -> (
+                let users =
+                  User_model.create_user_uuid_map (snd store).Storage.users
+                in
+                match User_model.find_user_by_key user_uuid users with
+                | Some u ->
+                    let policy_data =
+                      {
+                        Vmm_core.Policy.vms;
+                        memory;
+                        block = Some block;
+                        cpuids =
+                          Vmm_core.IS.of_list
+                            (List.map int_of_string
+                               (String.split_on_char ',' cpuids));
+                        bridges =
+                          Vmm_core.String_set.of_list
+                            (String.split_on_char ',' bridges);
+                      }
+                    in
+                    (Albatross.query albatross ~domain:u.name
+                       (`Policy_cmd (`Policy_add policy_data))
+                     >|= function
+                     | Error msg ->
+                         Logs.err (fun m ->
+                             m "error while communicating with albatross: %s"
+                               msg);
+                         []
+                     | Ok (_hdr, `Success (`Policies policies)) -> policies
+                     | Ok reply ->
+                         Logs.err (fun m ->
+                             m "expected a policy info reply, received %a"
+                               (Vmm_commands.pp_wire ~verbose:false)
+                               reply);
+                         [])
+                    >>= fun _policies ->
+                    http_response reqd ~title:"Success"
+                      ~data:"Policy updated succesfully" `OK
+                | None ->
+                    Logs.warn (fun m ->
+                        m "Failed to find user with uuid: %s" user_uuid);
+                    http_response reqd ~title:"Error" ~data:"User not found"
+                      `Not_found)
+            | _ ->
+                http_response reqd ~title:"Error"
+                  ~data:
+                    (Fmt.str "policy: unexpected types, got %s"
+                       (Yojson.Basic.to_string (`Assoc xs)))
+                  `Bad_request)
+        | _ ->
+            http_response reqd ~title:"Error" ~data:"Expected a dictionary"
+              `Bad_request)
+
   let request_handler stack albatross js_file css_file imgs store
       (_ipaddr, _port) reqd =
     Lwt.async (fun () ->
@@ -855,6 +984,14 @@ struct
                 let uuid = String.sub path 12 (String.length path - 12) in
                 authenticate ~check_admin:true !store reqd
                   (view_user !albatross !store uuid reqd))
+        | path
+          when String.(
+                 length path >= 21 && sub path 0 21 = "/admin/u/policy/edit/")
+          ->
+            check_meth `GET (fun () ->
+                let uuid = String.sub path 21 (String.length path - 21) in
+                authenticate ~check_admin:true !store reqd
+                  (edit_policy !albatross !store uuid reqd))
         | "/admin/settings" ->
             check_meth `GET (fun () ->
                 authenticate ~check_admin:true !store reqd
@@ -863,6 +1000,10 @@ struct
             check_meth `POST (fun () ->
                 authenticate ~check_admin:true ~api_meth:true !store reqd
                   (update_settings stack store albatross reqd))
+        | "/api/admin/u/policy/update" ->
+            check_meth `POST (fun () ->
+                authenticate ~check_admin:true ~api_meth:true !store reqd
+                  (update_policy !store !albatross reqd))
         | "/api/admin/user/activate/toggle" ->
             check_meth `POST (fun () ->
                 authenticate ~check_admin:true ~api_meth:true !store reqd
diff --git a/update_policy.ml b/update_policy.ml
new file mode 100644
index 00000000..28c7b582
--- /dev/null
+++ b/update_policy.ml
@@ -0,0 +1,456 @@
+let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
+  let empty_policy =
+    Vmm_core.Policy.
+      {
+        vms = 0;
+        cpuids = Vmm_core.IS.of_list [];
+        memory = 0;
+        block = Some 0;
+        bridges = Vmm_core.String_set.of_list [];
+      }
+  in
+  let root_policy =
+    match root_policy with (_, hd) :: _ -> hd | _ -> empty_policy
+  in
+  let policy =
+    match user_policy with None -> empty_policy | Some policy -> policy
+  in
+  Tyxml_html.(
+    section
+      ~a:[ a_id "policy-form" ]
+      [
+        h2
+          ~a:[ a_class [ "font-semibold text-xl" ] ]
+          [ txt ("Set Policy for " ^ user.name) ];
+        p ~a:[ a_id "form-alert"; a_class [ "my-4" ] ] [];
+        p ~a:[ a_id "user_id"; a_class [ "hidden" ] ] [ txt user.uuid ];
+        div
+          ~a:[ a_class [ "my-4" ] ]
+          [
+            label
+              ~a:[ a_class [ "block text-sm font-medium" ] ]
+              [ txt "Allowed VMs" ];
+            p
+              [
+                txt ("total: " ^ string_of_int root_policy.Vmm_core.Policy.vms);
+              ];
+            div
+              ~a:
+                [
+                  a_class [ "space-x-5 my-4" ];
+                  Unsafe.string_attrib "x-data"
+                    ("{count : "
+                    ^ string_of_int policy.Vmm_core.Policy.vms
+                    ^ "}");
+                ]
+              [
+                button
+                  ~a:
+                    [
+                      a_class
+                        [
+                          "border py-2 px-3 border-primary-500 \
+                           hover:bg-primary-100 rounded-md";
+                        ];
+                      Unsafe.string_attrib "x-on:click" "count++";
+                    ]
+                  [ i ~a:[ a_class [ "fa-solid fa-plus" ] ] [] ];
+                span
+                  ~a:
+                    [
+                      a_id "f_allowed_vms";
+                      a_contenteditable true;
+                      a_class [ "text-4xl border px-4" ];
+                      a_user_data "x-on:keydown.enter.prevent" "";
+                      Unsafe.string_attrib "x-on:input"
+                        "let value = \
+                         $event.target.innerText.replace(/[^0-9]/g,'');\n\
+                        \                                     \
+                         $event.target.innerText = value;\n\
+                        \                                     count = \
+                         parseInt(value) || 0;";
+                      Unsafe.string_attrib "x-text" "count";
+                      Unsafe.string_attrib "x-on:blur"
+                        "$event.target.innerText = count;";
+                    ]
+                  [];
+                button
+                  ~a:
+                    [
+                      a_class
+                        [
+                          "border py-2 px-3 border-secondary-500 \
+                           hover:bg-secondary-100 rounded-md";
+                        ];
+                      Unsafe.string_attrib "x-on:click" "if (count > 0) count--";
+                    ]
+                  [ i ~a:[ a_class [ "fa-solid fa-minus" ] ] [] ];
+              ];
+          ];
+        hr ();
+        div
+          ~a:[ a_class [ "my-4" ] ]
+          [
+            label
+              ~a:[ a_class [ "block text-sm font-medium" ] ]
+              [ txt "Allowed Memory" ];
+            div
+              ~a:
+                [
+                  a_class [ "space-x-5 my-4" ];
+                  Unsafe.string_attrib "x-data"
+                    ("{count : "
+                    ^ string_of_int policy.Vmm_core.Policy.memory
+                    ^ "}");
+                ]
+              [
+                button
+                  ~a:
+                    [
+                      a_class
+                        [
+                          "border py-2 px-3 border-primary-500 \
+                           hover:bg-primary-100 rounded-md";
+                        ];
+                      Unsafe.string_attrib "x-on:click" "count = count + 50";
+                    ]
+                  [ i ~a:[ a_class [ "fa-solid fa-plus" ] ] [] ];
+                span
+                  ~a:
+                    [
+                      a_id "f_allowed_memory";
+                      a_contenteditable true;
+                      a_class [ "text-4xl border px-4" ];
+                      a_user_data "x-on:keydown.enter.prevent" "";
+                      Unsafe.string_attrib "x-on:input"
+                        "let value = \
+                         $event.target.innerText.replace(/[^0-9]/g,'');\n\
+                        \                                     \
+                         $event.target.innerText = value;\n\
+                        \                                     count = \
+                         parseInt(value) || 0;";
+                      Unsafe.string_attrib "x-text" "count";
+                      Unsafe.string_attrib "x-on:blur"
+                        "$event.target.innerText = count;";
+                    ]
+                  [];
+                span ~a:[ a_class [ "text-4xl" ] ] [ txt "MB" ];
+                button
+                  ~a:
+                    [
+                      a_class
+                        [
+                          "border py-2 px-3 border-secondary-500 \
+                           hover:bg-secondary-100 rounded-md";
+                        ];
+                      Unsafe.string_attrib "x-on:click"
+                        "if (count > 0) count = count - 50";
+                    ]
+                  [ i ~a:[ a_class [ "fa-solid fa-minus" ] ] [] ];
+              ];
+          ];
+        hr ();
+        div
+          ~a:[ a_class [ "my-4" ] ]
+          [
+            label
+              ~a:[ a_class [ "block text-sm font-medium" ] ]
+              [ txt "Allowed Storage" ];
+            div
+              ~a:
+                [
+                  a_class [ "space-x-5 my-4" ];
+                  Unsafe.string_attrib "x-data"
+                    ("{count : "
+                    ^ (match policy.Vmm_core.Policy.block with
+                      | None -> string_of_int 0
+                      | Some x -> string_of_int x)
+                    ^ "}");
+                ]
+              [
+                button
+                  ~a:
+                    [
+                      a_class
+                        [
+                          "border py-2 px-3 border-primary-500 \
+                           hover:bg-primary-100 rounded-md";
+                        ];
+                      Unsafe.string_attrib "x-on:click" "count = count + 50";
+                    ]
+                  [ i ~a:[ a_class [ "fa-solid fa-plus" ] ] [] ];
+                span
+                  ~a:
+                    [
+                      a_id "f_allowed_storage";
+                      a_contenteditable true;
+                      a_class [ "text-4xl border px-4" ];
+                      a_user_data "x-on:keydown.enter.prevent" "";
+                      Unsafe.string_attrib "x-on:input"
+                        "let value = \
+                         $event.target.innerText.replace(/[^0-9]/g,'');\n\
+                        \                                     \
+                         $event.target.innerText = value;\n\
+                        \                                     count = \
+                         parseInt(value) || 0;";
+                      Unsafe.string_attrib "x-text" "count";
+                      Unsafe.string_attrib "x-on:blur"
+                        "$event.target.innerText = count;";
+                    ]
+                  [];
+                span ~a:[ a_class [ "text-4xl" ] ] [ txt "MB" ];
+                button
+                  ~a:
+                    [
+                      a_class
+                        [
+                          "border py-2 px-3 border-secondary-500 \
+                           hover:bg-secondary-100 rounded-md";
+                        ];
+                      Unsafe.string_attrib "x-on:click"
+                        "if (count > 0) count = count - 50";
+                    ]
+                  [ i ~a:[ a_class [ "fa-solid fa-minus" ] ] [] ];
+              ];
+          ];
+        div
+          ~a:[ a_class [ "my-4" ] ]
+          [
+            label
+              ~a:[ a_class [ "block text-sm font-medium" ] ]
+              [ txt "CPU IDs" ];
+            div
+              ~a:
+                [
+                  Unsafe.string_attrib "x-data"
+                    "multiselect([\n                                    ])";
+                  a_class [ "multiselect border my-4 p-4 rounded" ];
+                ]
+              [
+                div
+                  ~a:[ a_class [ "selected-items" ] ]
+                  [
+                    template
+                      ~a:
+                        [
+                          Unsafe.string_attrib "x-for"
+                            "(item, index) in selected";
+                          Unsafe.string_attrib ":key" "item";
+                        ]
+                      [
+                        span
+                          ~a:
+                            [
+                              a_class
+                                [
+                                  "selected-tag rounded bg-primary-100 p-1 ml-1";
+                                ];
+                            ]
+                          [
+                            span ~a:[ Unsafe.string_attrib "x-text" "item" ] [];
+                            button
+                              ~a:
+                                [
+                                  Unsafe.string_attrib "x-on:click"
+                                    "removeItem(index)";
+                                  a_class
+                                    [
+                                      "rounded-full bg-secondary-300 \
+                                       text-secondary-700 w-6 h-6 text-center";
+                                    ];
+                                ]
+                              [ txt "x" ];
+                          ];
+                      ];
+                  ];
+                div
+                  ~a:
+                    [
+                      Unsafe.string_attrib "x-on:click" "toggleDropdown";
+                      a_class [ "dropdown my-3" ];
+                    ]
+                  [
+                    button
+                      ~a:
+                        [
+                          a_class
+                            [ "dropdown-button flex justify-between space-x-4" ];
+                        ]
+                      [
+                        span [ txt "Assign CPUs" ];
+                        i ~a:[ a_class [ "fa-solid fa-caret-down" ] ] [];
+                      ];
+                  ];
+                div
+                  ~a:
+                    [
+                      Unsafe.string_attrib "x-show" "isOpen";
+                      a_class [ "dropdown-list" ];
+                    ]
+                  [
+                    template
+                      ~a:
+                        [
+                          Unsafe.string_attrib "x-for"
+                            "(option, index) in options";
+                          Unsafe.string_attrib ":key" "option";
+                        ]
+                      [
+                        p
+                          ~a:[ a_class [ "py-2" ] ]
+                          [
+                            input
+                              ~a:
+                                [
+                                  a_input_type `Checkbox;
+                                  a_class [ "text-primary-500 bg-primary-500" ];
+                                  Unsafe.string_attrib ":value" "option";
+                                  Unsafe.string_attrib "x-on:change"
+                                    "updateSelection($event, option)";
+                                  Unsafe.string_attrib ":checked"
+                                    "selected.includes(option)";
+                                ]
+                              ();
+                            span [ txt "CPU " ];
+                            span
+                              ~a:[ Unsafe.string_attrib "x-text" "option" ]
+                              [];
+                          ];
+                      ];
+                  ];
+                input
+                  ~a:
+                    [
+                      a_input_type `Hidden;
+                      Unsafe.string_attrib ":value" "selected.join(',')";
+                      a_id "selectedCPUs";
+                    ]
+                  ();
+              ];
+          ];
+        div
+          ~a:[ a_class [ "my-4" ] ]
+          [
+            label
+              ~a:[ a_class [ "block text-sm font-medium" ] ]
+              [ txt "Bridges" ];
+            div
+              ~a:
+                [
+                  Unsafe.string_attrib "x-data" "multiselect()";
+                  a_class [ "multiselect border my-4 p-4 rounded" ];
+                ]
+              [
+                div
+                  ~a:[ a_class [ "selected-items" ] ]
+                  [
+                    template
+                      ~a:
+                        [
+                          Unsafe.string_attrib "x-for"
+                            "(item, index) in selected";
+                          Unsafe.string_attrib ":key" "item";
+                        ]
+                      [
+                        span
+                          ~a:
+                            [
+                              a_class
+                                [
+                                  "selected-tag rounded bg-primary-100 p-1 ml-1";
+                                ];
+                            ]
+                          [
+                            span ~a:[ Unsafe.string_attrib "x-text" "item" ] [];
+                            button
+                              ~a:
+                                [
+                                  Unsafe.string_attrib "x-on:click"
+                                    "removeItem(index)";
+                                  a_class
+                                    [
+                                      "rounded-full bg-secondary-300 \
+                                       text-secondary-700 w-6 h-6 text-center";
+                                    ];
+                                ]
+                              [ txt "x" ];
+                          ];
+                      ];
+                  ];
+                div
+                  ~a:
+                    [
+                      Unsafe.string_attrib "x-on:click" "toggleDropdown";
+                      a_class [ "dropdown my-3" ];
+                    ]
+                  [
+                    button
+                      ~a:
+                        [
+                          a_class
+                            [ "dropdown-button flex justify-between space-x-4" ];
+                        ]
+                      [
+                        span [ txt "Assign Bridges" ];
+                        i ~a:[ a_class [ "fa-solid fa-caret-down" ] ] [];
+                      ];
+                  ];
+                div
+                  ~a:
+                    [
+                      Unsafe.string_attrib "x-show" "isOpen";
+                      a_class [ "dropdown-list" ];
+                    ]
+                  [
+                    template
+                      ~a:
+                        [
+                          Unsafe.string_attrib "x-for"
+                            "(option, index) in options";
+                          Unsafe.string_attrib ":key" "option";
+                        ]
+                      [
+                        p
+                          ~a:[ a_class [ "py-2" ] ]
+                          [
+                            input
+                              ~a:
+                                [
+                                  a_input_type `Checkbox;
+                                  a_class [ "text-primary-500 bg-primary-500" ];
+                                  Unsafe.string_attrib ":value" "option";
+                                  Unsafe.string_attrib "x-on:change"
+                                    "updateSelection($event, option)";
+                                  Unsafe.string_attrib ":checked"
+                                    "selected.includes(option)";
+                                ]
+                              ();
+                            span
+                              ~a:[ Unsafe.string_attrib "x-text" "option" ]
+                              [];
+                          ];
+                      ];
+                  ];
+                input
+                  ~a:
+                    [
+                      a_input_type `Hidden;
+                      Unsafe.string_attrib ":value" "selected.join(',')";
+                      a_id "selectedBridges";
+                    ]
+                  ();
+              ];
+          ];
+        hr ();
+        div
+          ~a:[ a_class [ "my-4" ] ]
+          [
+            button
+              ~a:
+                [
+                  a_onclick "updatePolicy()";
+                  a_class [ "bg-primary-500 py-1 px-2 text-primary-50 rounded" ];
+                ]
+              [ txt "Set Policy" ];
+          ];
+      ])

From b1bb8a37bd0432174b4c49a8a20c15fe02596133 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Mon, 30 Sep 2024 13:42:12 +0200
Subject: [PATCH 03/64] lint

---
 user_single.ml | 361 ++++++++++++++++++++++++++-----------------------
 1 file changed, 194 insertions(+), 167 deletions(-)

diff --git a/user_single.ml b/user_single.ml
index 009026a3..a6bdf913 100644
--- a/user_single.ml
+++ b/user_single.ml
@@ -238,183 +238,210 @@ let user_single_layout (user : User_model.user) unikernels policy current_time =
             section
               ~a:[ a_id "settings"; a_class [ "my-5 tab-pane hidden" ] ]
               [
-                (match policy with
-                | None -> h2 [ txt "No policy" ]
-                | Some policy ->
-                    table
-                      ~a:
-                        [
-                          a_class
-                            [ "table-auto min-w-full divide-y divide-gray-200" ];
-                        ]
-                      ~thead:
-                        (thead
-                           [
-                             tr
-                               [
-                                 th
-                                   ~a:
-                                     [
-                                       a_class
-                                         [
-                                           "px-6 py-2 text-start text-xs \
-                                            font-bold text-primary-600 \
-                                            uppercase";
-                                         ];
-                                     ]
-                                   [ txt "Allowed VMs" ];
-                                 th
-                                   ~a:
-                                     [
-                                       a_class
-                                         [
-                                           "px-6 py-2 text-start text-xs \
-                                            font-bold text-primary-600 \
-                                            uppercase";
-                                         ];
-                                     ]
-                                   [ txt "Allowed Memory" ];
-                                 th
-                                   ~a:
-                                     [
-                                       a_class
-                                         [
-                                           "px-6 py-2 text-start text-xs \
-                                            font-bold text-primary-600 \
-                                            uppercase";
-                                         ];
-                                     ]
-                                   [ txt "Allowed Storage" ];
-                                 th
-                                   ~a:
-                                     [
-                                       a_class
-                                         [
-                                           "px-6 py-2 text-start text-xs \
-                                            font-bold text-primary-600 \
-                                            uppercase";
-                                         ];
-                                     ]
-                                   [ txt "CPU IDs" ];
-                                 th
-                                   ~a:
-                                     [
-                                       a_class
-                                         [
-                                           "px-6 py-2 text-start text-xs \
-                                            font-bold text-primary-600 \
-                                            uppercase";
-                                         ];
-                                     ]
-                                   [ txt "Network Bridges" ];
-                                 th
-                                   ~a:
-                                     [
-                                       a_class
-                                         [
-                                           "px-6 py-2 text-start text-xs \
-                                            font-bold text-primary-600 \
-                                            uppercase";
-                                         ];
-                                     ]
-                                   [ txt "Action" ];
-                               ];
-                           ])
-                      [
-                        tr
+                section
+                  ~a:[ a_id "policy-table" ]
+                  [
+                    (match policy with
+                    | None -> h2 [ txt "No policy" ]
+                    | Some policy ->
+                        div
                           [
-                            td
-                              ~a:
-                                [
-                                  a_class
-                                    [
-                                      "px-6 py-1 whitespace-nowrap text-sm \
-                                       font-medium text-gray-800";
-                                    ];
-                                ]
-                              [ txt (string_of_int policy.Vmm_core.Policy.vms) ];
-                            td
+                            table
                               ~a:
                                 [
                                   a_class
                                     [
-                                      "px-6 py-1 whitespace-normal text-sm \
-                                       font-medium text-gray-800";
-                                    ];
-                                ]
-                              [ txt (string_of_int policy.memory ^ " MB") ];
-                            td
-                              ~a:
-                                [
-                                  a_class
-                                    [
-                                      "px-6 py-1 whitespace-normal text-sm \
-                                       font-medium text-gray-800";
-                                    ];
-                                ]
-                              [
-                                txt
-                                  (string_of_int
-                                     (Option.value policy.block ~default:0)
-                                  ^ " MB");
-                              ];
-                            td
-                              ~a:
-                                [
-                                  a_class
-                                    [
-                                      "px-6 py-1 whitespace-normal text-sm \
-                                       font-medium text-gray-800";
-                                    ];
-                                ]
-                              [
-                                txt
-                                  (String.concat ", "
-                                     (List.map string_of_int
-                                        (Vmm_core.IS.elements policy.cpuids)));
-                              ];
-                            td
-                              ~a:
-                                [
-                                  a_class
-                                    [
-                                      "px-6 py-1 whitespace-normal text-sm \
-                                       font-medium text-gray-800";
-                                    ];
-                                ]
-                              [
-                                txt
-                                  (String.concat ", "
-                                     (List.map string_of_uri
-                                        (Vmm_core.String_set.elements
-                                           policy.bridges)));
-                              ];
-                            td
-                              ~a:
-                                [
-                                  a_class
-                                    [
-                                      "px-6 py-4 whitespace-nowrap text-sm \
-                                       font-medium text-gray-800";
+                                      "table-auto min-w-full divide-y \
+                                       divide-gray-200";
                                     ];
                                 ]
+                              ~thead:
+                                (thead
+                                   [
+                                     tr
+                                       [
+                                         th
+                                           ~a:
+                                             [
+                                               a_class
+                                                 [
+                                                   "px-6 py-2 text-start \
+                                                    text-xs font-bold \
+                                                    text-primary-600 uppercase";
+                                                 ];
+                                             ]
+                                           [ txt "Allowed VMs" ];
+                                         th
+                                           ~a:
+                                             [
+                                               a_class
+                                                 [
+                                                   "px-6 py-2 text-start \
+                                                    text-xs font-bold \
+                                                    text-primary-600 uppercase";
+                                                 ];
+                                             ]
+                                           [ txt "Allowed Memory" ];
+                                         th
+                                           ~a:
+                                             [
+                                               a_class
+                                                 [
+                                                   "px-6 py-2 text-start \
+                                                    text-xs font-bold \
+                                                    text-primary-600 uppercase";
+                                                 ];
+                                             ]
+                                           [ txt "Allowed Storage" ];
+                                         th
+                                           ~a:
+                                             [
+                                               a_class
+                                                 [
+                                                   "px-6 py-2 text-start \
+                                                    text-xs font-bold \
+                                                    text-primary-600 uppercase";
+                                                 ];
+                                             ]
+                                           [ txt "CPU IDs" ];
+                                         th
+                                           ~a:
+                                             [
+                                               a_class
+                                                 [
+                                                   "px-6 py-2 text-start \
+                                                    text-xs font-bold \
+                                                    text-primary-600 uppercase";
+                                                 ];
+                                             ]
+                                           [ txt "Network Bridges" ];
+                                         th
+                                           ~a:
+                                             [
+                                               a_class
+                                                 [
+                                                   "px-6 py-2 text-start \
+                                                    text-xs font-bold \
+                                                    text-primary-600 uppercase";
+                                                 ];
+                                             ]
+                                           [ txt "Action" ];
+                                       ];
+                                   ])
                               [
-                                a
-                                  ~a:
-                                    [
-                                      a_href "";
-                                      a_class
+                                tr
+                                  [
+                                    td
+                                      ~a:
                                         [
-                                          "border border-primary-500 \
-                                           hover:bg-primary-700 px-2 py-1 \
-                                           text-primary-800 \
-                                           hover:text-primary-50 rounded";
-                                        ];
-                                    ]
-                                  [ txt "Edit" ];
+                                          a_class
+                                            [
+                                              "px-6 py-1 whitespace-nowrap \
+                                               text-sm font-medium \
+                                               text-gray-800";
+                                            ];
+                                        ]
+                                      [
+                                        txt
+                                          (string_of_int
+                                             policy.Vmm_core.Policy.vms);
+                                      ];
+                                    td
+                                      ~a:
+                                        [
+                                          a_class
+                                            [
+                                              "px-6 py-1 whitespace-normal \
+                                               text-sm font-medium \
+                                               text-gray-800";
+                                            ];
+                                        ]
+                                      [
+                                        txt (string_of_int policy.memory ^ " MB");
+                                      ];
+                                    td
+                                      ~a:
+                                        [
+                                          a_class
+                                            [
+                                              "px-6 py-1 whitespace-normal \
+                                               text-sm font-medium \
+                                               text-gray-800";
+                                            ];
+                                        ]
+                                      [
+                                        txt
+                                          (string_of_int
+                                             (Option.value policy.block
+                                                ~default:0)
+                                          ^ " MB");
+                                      ];
+                                    td
+                                      ~a:
+                                        [
+                                          a_class
+                                            [
+                                              "px-6 py-1 whitespace-normal \
+                                               text-sm font-medium \
+                                               text-gray-800";
+                                            ];
+                                        ]
+                                      [
+                                        txt
+                                          (String.concat ", "
+                                             (List.map string_of_int
+                                                (Vmm_core.IS.elements
+                                                   policy.cpuids)));
+                                      ];
+                                    td
+                                      ~a:
+                                        [
+                                          a_class
+                                            [
+                                              "px-6 py-1 whitespace-normal \
+                                               text-sm font-medium \
+                                               text-gray-800";
+                                            ];
+                                        ]
+                                      [
+                                        txt
+                                          (String.concat ", "
+                                             (List.map string_of_uri
+                                                (Vmm_core.String_set.elements
+                                                   policy.bridges)));
+                                      ];
+                                    td
+                                      ~a:
+                                        [
+                                          a_class
+                                            [
+                                              "px-6 py-4 whitespace-nowrap \
+                                               text-sm font-medium \
+                                               text-gray-800";
+                                            ];
+                                        ]
+                                      [
+                                        a
+                                          ~a:
+                                            [
+                                              a_href
+                                                ("/admin/u/policy/edit/"
+                                               ^ user.uuid ^ "");
+                                              a_class
+                                                [
+                                                  "border border-primary-500 \
+                                                   hover:bg-primary-700 px-2 \
+                                                   py-1 text-primary-800 \
+                                                   hover:text-primary-50 \
+                                                   rounded";
+                                                ];
+                                            ]
+                                          [ txt "Edit" ];
+                                      ];
+                                  ];
                               ];
-                          ];
-                      ]);
+                          ]);
+                  ];
               ];
           ];
       ])

From 7e6ea1103c59ded7e43e7166804d26ae57491728 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Mon, 30 Sep 2024 13:43:20 +0200
Subject: [PATCH 04/64] add functions work with policies

---
 assets/main.js | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)

diff --git a/assets/main.js b/assets/main.js
index 636d1f7e..a210e6f3 100644
--- a/assets/main.js
+++ b/assets/main.js
@@ -250,3 +250,71 @@ async function toggleUserActiveStatus(uuid) {
 async function toggleUserAdminStatus(uuid) {
 	await toggleUserStatus(uuid, "/api/admin/user/admin/toggle");
 }
+
+
+function multiselect() {
+	return {
+		isOpen: false,
+		selected: [1, 2, 3], // TODO: replace with variable
+		options: [1, 2, 3, 4, 5, 6, 7], // TODO: replace with variable
+		toggleDropdown() {
+			this.isOpen = !this.isOpen;
+		},
+		updateSelection(event, option) {
+			if (event.target.checked) {
+				this.selected.push(option);
+			} else {
+				this.selected = this.selected.filter(item => item !== option);
+			}
+		},
+		removeItem(index) {
+			this.selected.splice(index, 1);
+		}
+	};
+}
+
+async function updatePolicy() {
+	const vm_count = document.getElementById("f_allowed_vms").innerText;
+	const mem_size = document.getElementById("f_allowed_memory").innerText;
+	const storage_size = document.getElementById("f_allowed_storage").innerText;
+	const cpuids = document.getElementById("selectedCPUs").value;
+	const bridges = document.getElementById("selectedBridges").value;
+	const formAlert = document.getElementById("form-alert");
+	const user_id = document.getElementById("user_id").innerText;
+	console.log(vm_count, mem_size, storage_size, cpuids, bridges);
+	try {
+		const response = await fetch("/api/admin/u/policy/update", {
+			method: 'POST',
+			headers: {
+				"Content-Type": "application/json",
+			},
+			body: JSON.stringify(
+				{
+					"vms": Number(vm_count),
+					"memory": Number(mem_size),
+					"block": Number(storage_size),
+					"cpuids": cpuids,
+					"bridges": bridges,
+					"user_uuid": user_id
+				})
+		})
+		const data = await response.json();
+		if (data.status === 200) {
+			formAlert.classList.remove("hidden", "text-secondary-500");
+			formAlert.classList.add("text-primary-500");
+			formAlert.textContent = "Succesfully updated";
+			postAlert("bg-primary-300", data.data);
+			setTimeout(function () {
+				window.location.reload();
+			}, 2000);
+		} else {
+			formAlert.classList.remove("hidden", "text-primary-500");
+			formAlert.classList.add("text-secondary-500");
+			formAlert.textContent = data.data
+		}
+	} catch (error) {
+		formAlert.classList.remove("hidden", "text-primary-500");
+		formAlert.classList.add("text-secondary-500");
+		formAlert.textContent = error
+	}
+}

From eb3fa13146e136ef1249ea3aae4ff59914bd9a1e Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Mon, 30 Sep 2024 13:43:27 +0200
Subject: [PATCH 05/64] update styles

---
 assets/style.css | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/assets/style.css b/assets/style.css
index fd60abe7..81de7543 100755
--- a/assets/style.css
+++ b/assets/style.css
@@ -1 +1 @@
-/*! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-1{inset:.25rem}.-inset-x-12{left:-3rem;right:-3rem}.inset-x-1{left:.25rem;right:.25rem}.inset-x-4{left:1rem;right:1rem}.-bottom-0{bottom:0}.-bottom-0\.5{bottom:-.125rem}.-bottom-2{bottom:-.5rem}.-bottom-6{bottom:-1.5rem}.-left-1\/4{left:-25%}.-left-2{left:-.5rem}.-top-1\/4{top:-25%}.-top-4{top:-1rem}.-top-6{top:-1.5rem}.bottom-0{bottom:0}.bottom-10{bottom:2.5rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.left-8{left:2rem}.right-4{right:1rem}.top-0{top:0}.top-1\/4{top:25%}.z-50{z-index:50}.z-\[-1\]{z-index:-1}.z-\[500\]{z-index:500}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-7{grid-column:span 7/span 7}.-m-1{margin:-.25rem}.-m-1\.5{margin:-.375rem}.m-auto{margin:auto}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.-mb-12{margin-bottom:-3rem}.-mb-6{margin-bottom:-1.5rem}.-ml-2{margin-left:-.5rem}.-mr-2{margin-right:-.5rem}.-mr-2\.5{margin-right:-.625rem}.-mt-1{margin-top:-.25rem}.-mt-24{margin-top:-6rem}.-mt-3{margin-top:-.75rem}.mb-0{margin-bottom:0}.mb-14{margin-bottom:3.5rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-7{margin-bottom:1.75rem}.mb-8{margin-bottom:2rem}.mb-9{margin-bottom:2.25rem}.mb-auto{margin-bottom:auto}.mb-px{margin-bottom:1px}.ml-5{margin-left:1.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.size-2{width:.5rem;height:.5rem}.size-2\.5{width:.625rem;height:.625rem}.h-12{height:3rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-24{height:6rem}.h-32{height:8rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[150\%\]{height:150%}.h-full{height:100%}.h-px{height:1px}.max-h-screen{max-height:100vh}.min-h-screen{min-height:100vh}.w-0{width:0}.w-14{width:3.5rem}.w-16{width:4rem}.w-32{width:8rem}.w-60{width:15rem}.w-\[150\%\]{width:150%}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.min-w-full{min-width:100%}.max-w-3xl{max-width:48rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[94\%\]{max-width:94%}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-none{flex:none}.shrink-0{flex-shrink:0}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.origin-bottom{transform-origin:bottom}.-translate-x-1\/2{--tw-translate-x:-50%}.-rotate-3,.-translate-x-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-3{--tw-rotate:-3deg}.rotate-3{--tw-rotate:3deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-0{--tw-scale-x:0;--tw-scale-y:0}.scale-0,.scale-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-x-0{--tw-scale-x:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform-gpu{transform:translate3d(var(--tw-translate-x),var(--tw-translate-y),0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-0{gap:0}.gap-0\.5{gap:.125rem}.gap-10{gap:2.5rem}.gap-12{gap:3rem}.gap-20{gap:5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.gap-y-16{row-gap:4rem}.gap-y-4{row-gap:1rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-20>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(5rem*var(--tw-space-x-reverse));margin-left:calc(5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.25rem*var(--tw-space-x-reverse));margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.justify-self-start{justify-self:start}.justify-self-end{justify-self:end}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.text-wrap{text-wrap:wrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-l-\[20px\]{border-top-left-radius:20px;border-bottom-left-radius:20px}.rounded-r-\[20px\]{border-top-right-radius:20px;border-bottom-right-radius:20px}.rounded-r-\[8px\]{border-top-right-radius:8px;border-bottom-right-radius:8px}.rounded-t-full{border-top-left-radius:9999px;border-top-right-radius:9999px}.border{border-width:1px}.border-x-0{border-left-width:0;border-right-width:0}.border-b-0{border-bottom-width:0}.border-b-2{border-bottom-width:2px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-t-0{border-top-width:0}.border-none{border-style:none}.border-\[\#D7DFE9\]{--tw-border-opacity:1;border-color:rgb(215 223 233/var(--tw-border-opacity))}.border-primary-200{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.border-primary-400{--tw-border-opacity:1;border-color:rgb(78 179 161/var(--tw-border-opacity))}.border-primary-500{--tw-border-opacity:1;border-color:rgb(54 156 140/var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity:1;border-color:rgb(40 121 110/var(--tw-border-opacity))}.border-primary-700{--tw-border-opacity:1;border-color:rgb(35 98 90/var(--tw-border-opacity))}.border-transparent{border-color:#0000}.border-y-secondary-300{--tw-border-opacity:1;border-top-color:rgb(255 170 157/var(--tw-border-opacity));border-bottom-color:rgb(255 170 157/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-500\/25{background-color:#6b728040}.bg-primary-100{--tw-bg-opacity:1;background-color:rgb(213 242 235/var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity:1;background-color:rgb(171 228 214/var(--tw-bg-opacity))}.bg-primary-300{--tw-bg-opacity:1;background-color:rgb(122 206 189/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(243 250 249/var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(54 156 140/var(--tw-bg-opacity))}.bg-primary-800{--tw-bg-opacity:1;background-color:rgb(32 79 74/var(--tw-bg-opacity))}.bg-primary-950{--tw-bg-opacity:1;background-color:rgb(13 38 37/var(--tw-bg-opacity))}.bg-secondary-300{--tw-bg-opacity:1;background-color:rgb(255 170 157/var(--tw-bg-opacity))}.bg-secondary-500{--tw-bg-opacity:1;background-color:rgb(255 78 51/var(--tw-bg-opacity))}.bg-secondary-700{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.bg-transparent{background-color:initial}.bg-opacity-0{--tw-bg-opacity:0}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-gray-100{--tw-gradient-from:#f3f4f6 var(--tw-gradient-from-position);--tw-gradient-to:#f3f4f600 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-900\/50{--tw-gradient-from:#1f423e80 var(--tw-gradient-from-position);--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-950{--tw-gradient-from:#0d2625 var(--tw-gradient-from-position);--tw-gradient-to:#0d262500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-10\%{--tw-gradient-from-position:10%}.via-primary-900{--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f423e var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-primary-900\/50{--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f423e80 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-10\%{--tw-gradient-via-position:10%}.to-primary-950{--tw-gradient-to:#0d2625 var(--tw-gradient-to-position)}.to-primary-950\/50{--tw-gradient-to:#0d262580 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:#0000 var(--tw-gradient-to-position)}.to-90\%{--tw-gradient-to-position:90%}.bg-\[length\:100px_auto\]{background-size:100px auto}.bg-cover{background-size:cover}.bg-clip-padding{background-clip:padding-box}.bg-center{background-position:50%}.fill-primary-400{fill:#4eb3a1}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.object-bottom{-o-object-position:bottom;object-position:bottom}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-10{padding:2.5rem}.p-16{padding:4rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-40{padding-left:10rem;padding-right:10rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-7{padding-left:1.75rem;padding-right:1.75rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-12{padding-bottom:3rem}.pb-16{padding-bottom:4rem}.pb-3{padding-bottom:.75rem}.pb-3\.5{padding-bottom:.875rem}.pb-32{padding-bottom:8rem}.pb-8{padding-bottom:2rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pr-7{padding-right:1.75rem}.pr-\[10px\]{padding-right:10px}.pt-12{padding-top:3rem}.pt-24{padding-top:6rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-start{text-align:start}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-7xl{font-size:4.5rem;line-height:1}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-\[450\]{font-weight:450}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-primary-300{--tw-text-opacity:1;color:rgb(122 206 189/var(--tw-text-opacity))}.text-primary-400{--tw-text-opacity:1;color:rgb(78 179 161/var(--tw-text-opacity))}.text-primary-50{--tw-text-opacity:1;color:rgb(243 250 249/var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity:1;color:rgb(54 156 140/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(40 121 110/var(--tw-text-opacity))}.text-primary-800{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(31 66 62/var(--tw-text-opacity))}.text-primary-950{--tw-text-opacity:1;color:rgb(13 38 37/var(--tw-text-opacity))}.text-secondary-300{--tw-text-opacity:1;color:rgb(255 170 157/var(--tw-text-opacity))}.text-secondary-50{--tw-text-opacity:1;color:rgb(255 243 241/var(--tw-text-opacity))}.text-secondary-500{--tw-text-opacity:1;color:rgb(255 78 51/var(--tw-text-opacity))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-gray-800\/5{--tw-shadow-color:#1f29370d;--tw-shadow:var(--tw-shadow-colored)}.outline-0{outline-width:0}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-gray-800\/\[\.075\]{--tw-ring-color:rgba(31,41,55,.075)}.ring-primary-100{--tw-ring-opacity:1;--tw-ring-color:rgb(213 242 235/var(--tw-ring-opacity))}.ring-primary-200{--tw-ring-opacity:1;--tw-ring-color:rgb(171 228 214/var(--tw-ring-opacity))}.blur{--tw-blur:blur(8px)}.blur,.blur-3xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-3xl{--tw-blur:blur(64px)}.brightness-0{--tw-brightness:brightness(0)}.brightness-0,.drop-shadow{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f)}.drop-shadow-sm{--tw-drop-shadow:drop-shadow(0 1px 1px #0000000d)}.drop-shadow-sm,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.backdrop-blur-sm,.backdrop-blur-xl{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[-webkit-mask-image\:linear-gradient\(to_bottom\2c rgba\(255\2c 255\2c 255\2c 1\)_75\%\2c rgba\(255\2c 255\2c 255\2c 0\)\)\]{-webkit-mask-image:linear-gradient(180deg,#fff 75%,#fff0)}.\[-webkit-mask-image\:linear-gradient\(to_top\2c rgba\(255\2c 255\2c 255\2c 1\)_75\%\2c rgba\(255\2c 255\2c 255\2c 0\)\)\]{-webkit-mask-image:linear-gradient(0deg,#fff 75%,#fff0)}.hover\:isolate:hover{isolation:isolate}.hover\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.hover\:border-transparent:hover{border-color:#0000}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-primary-700:hover{--tw-bg-opacity:1;background-color:rgb(35 98 90/var(--tw-bg-opacity))}.hover\:bg-primary-800:hover{--tw-bg-opacity:1;background-color:rgb(32 79 74/var(--tw-bg-opacity))}.hover\:bg-secondary-700:hover{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.hover\:bg-opacity-50:hover{--tw-bg-opacity:0.5}.hover\:font-bold:hover{font-weight:700}.hover\:text-gray-50:hover{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.hover\:text-primary-400:hover{--tw-text-opacity:1;color:rgb(78 179 161/var(--tw-text-opacity))}.hover\:text-primary-50:hover{--tw-text-opacity:1;color:rgb(243 250 249/var(--tw-text-opacity))}.hover\:text-primary-500:hover{--tw-text-opacity:1;color:rgb(54 156 140/var(--tw-text-opacity))}.hover\:text-primary-700:hover{--tw-text-opacity:1;color:rgb(35 98 90/var(--tw-text-opacity))}.hover\:text-primary-800:hover{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.focus\:isolate:focus{isolation:isolate}.focus\:border-primary-300:focus{--tw-border-opacity:1;border-color:rgb(122 206 189/var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgb(54 156 140/var(--tw-border-opacity))}.focus\:border-transparent:focus{border-color:#0000}.focus\:bg-opacity-50:focus{--tw-bg-opacity:0.5}.focus\:text-primary-800:focus{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-\[1px\]:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-\[1px\]:focus,.focus\:ring-\[3px\]:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-\[3px\]:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-primary-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(171 228 214/var(--tw-ring-opacity))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group\/btn:hover .group-hover\/btn\:w-2{width:.5rem}.group\/btn:hover .group-hover\/btn\:w-2\.5{width:.625rem}.group:hover .group-hover\:scale-100{--tw-scale-x:1;--tw-scale-y:1}.group:hover .group-hover\:scale-100,.group:hover .group-hover\:scale-x-100{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-x-100{--tw-scale-x:1}.group:hover .group-hover\:opacity-100,.group\/btn:hover .group-hover\/btn\:opacity-100{opacity:1}@media (min-width:640px){.sm\:left-14{left:3.5rem}.sm\:w-auto{width:auto}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pb-40{padding-bottom:10rem}.sm\:pl-20{padding-left:5rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-5xl{font-size:3rem;line-height:1}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:block{display:block}.md\:h-screen{height:100vh}.md\:w-16{width:4rem}.md\:w-20{width:5rem}.md\:w-24{width:6rem}.md\:max-w-2xl{max-width:42rem}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:gap-16{gap:4rem}.md\:px-14{padding-left:3.5rem;padding-right:3.5rem}.md\:py-10{padding-top:2.5rem;padding-bottom:2.5rem}.md\:pb-52{padding-bottom:13rem}.md\:text-3xl{font-size:1.875rem;line-height:2.25rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1024px){.lg\:left-20{left:5rem}.lg\:order-last{order:9999}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:-my-11{margin-top:-2.75rem;margin-bottom:-2.75rem}.lg\:-mr-9{margin-right:-2.25rem}.lg\:mb-0{margin-bottom:0}.lg\:mb-16{margin-bottom:4rem}.lg\:mt-0{margin-top:0}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:max-w-4xl{max-width:56rem}.lg\:max-w-7xl{max-width:80rem}.lg\:max-w-max{max-width:-moz-max-content;max-width:max-content}.lg\:max-w-xl{max-width:36rem}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:gap-5{gap:1.25rem}.lg\:p-10{padding:2.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-12{padding-top:3rem;padding-bottom:3rem}.lg\:py-24{padding-top:6rem;padding-bottom:6rem}.lg\:pb-20{padding-bottom:5rem}.lg\:pb-60{padding-bottom:15rem}.lg\:pl-28{padding-left:7rem}.lg\:pt-20{padding-top:5rem}.lg\:pt-40{padding-top:10rem}.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}.lg\:text-5xl{font-size:3rem;line-height:1}.lg\:text-6xl{font-size:3.75rem;line-height:1}.lg\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media (min-width:1280px){.xl\:col-span-3{grid-column:span 3/span 3}.xl\:mx-auto{margin-left:auto;margin-right:auto}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:w-auto{width:auto}.xl\:w-full{width:100%}.xl\:max-w-lg{max-width:32rem}.xl\:max-w-xl{max-width:36rem}.xl\:gap-16{gap:4rem}.xl\:gap-x-16{-moz-column-gap:4rem;column-gap:4rem}.xl\:px-20{padding-left:5rem;padding-right:5rem}.xl\:py-20{padding-top:5rem;padding-bottom:5rem}.xl\:py-32{padding-top:8rem}.xl\:pb-32,.xl\:py-32{padding-bottom:8rem}.xl\:pb-\[16\.5rem\]{padding-bottom:16.5rem}.xl\:text-xl{font-size:1.25rem;line-height:1.75rem}}
+/*! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-1{inset:.25rem}.-inset-x-12{left:-3rem;right:-3rem}.inset-x-1{left:.25rem;right:.25rem}.inset-x-4{left:1rem;right:1rem}.-bottom-0{bottom:0}.-bottom-0\.5{bottom:-.125rem}.-bottom-2{bottom:-.5rem}.-bottom-6{bottom:-1.5rem}.-left-1\/4{left:-25%}.-left-2{left:-.5rem}.-top-1\/4{top:-25%}.-top-4{top:-1rem}.-top-6{top:-1.5rem}.bottom-0{bottom:0}.bottom-10{bottom:2.5rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.left-8{left:2rem}.right-4{right:1rem}.top-0{top:0}.top-1\/4{top:25%}.right-0{right:0}.top-1{top:.25rem}.z-50{z-index:50}.z-\[-1\]{z-index:-1}.z-\[500\]{z-index:500}.z-10{z-index:10}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-7{grid-column:span 7/span 7}.-m-1{margin:-.25rem}.-m-1\.5{margin:-.375rem}.m-auto{margin:auto}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.-mx-3{margin-left:-.75rem;margin-right:-.75rem}.-mb-12{margin-bottom:-3rem}.-mb-6{margin-bottom:-1.5rem}.-ml-2{margin-left:-.5rem}.-mr-2{margin-right:-.5rem}.-mr-2\.5{margin-right:-.625rem}.-mt-1{margin-top:-.25rem}.-mt-24{margin-top:-6rem}.-mt-3{margin-top:-.75rem}.mb-0{margin-bottom:0}.mb-14{margin-bottom:3.5rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-7{margin-bottom:1.75rem}.mb-8{margin-bottom:2rem}.mb-9{margin-bottom:2.25rem}.mb-auto{margin-bottom:auto}.mb-px{margin-bottom:1px}.ml-5{margin-left:1.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.\!mt-0{margin-top:0!important}.\!mt-3{margin-top:.75rem!important}.ml-2{margin-left:.5rem}.mt-3{margin-top:.75rem}.mb-1{margin-bottom:.25rem}.ml-1{margin-left:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.size-2{width:.5rem;height:.5rem}.size-2\.5{width:.625rem;height:.625rem}.h-12{height:3rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-24{height:6rem}.h-32{height:8rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[150\%\]{height:150%}.h-full{height:100%}.h-px{height:1px}.h-4{height:1rem}.h-6{height:1.5rem}.h-3{height:.75rem}.max-h-screen{max-height:100vh}.max-h-52{max-height:13rem}.min-h-screen{min-height:100vh}.w-0{width:0}.w-14{width:3.5rem}.w-16{width:4rem}.w-32{width:8rem}.w-60{width:15rem}.w-\[150\%\]{width:150%}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.w-4{width:1rem}.w-64{width:16rem}.w-6{width:1.5rem}.w-3{width:.75rem}.min-w-full{min-width:100%}.max-w-3xl{max-width:48rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[94\%\]{max-width:94%}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-none{flex:none}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.origin-bottom{transform-origin:bottom}.-translate-x-1\/2{--tw-translate-x:-50%}.-rotate-3,.-translate-x-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-3{--tw-rotate:-3deg}.rotate-3{--tw-rotate:3deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-0{--tw-scale-x:0;--tw-scale-y:0}.scale-0,.scale-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-x-0{--tw-scale-x:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform-gpu{transform:translate3d(var(--tw-translate-x),var(--tw-translate-y),0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-0{gap:0}.gap-0\.5{gap:.125rem}.gap-10{gap:2.5rem}.gap-12{gap:3rem}.gap-20{gap:5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-2{gap:.5rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.gap-y-16{row-gap:4rem}.gap-y-4{row-gap:1rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-20>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(5rem*var(--tw-space-x-reverse));margin-left:calc(5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.25rem*var(--tw-space-x-reverse));margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.justify-self-start{justify-self:start}.justify-self-end{justify-self:end}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;white-space:nowrap}.text-ellipsis,.truncate{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.text-wrap{text-wrap:wrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-l-\[20px\]{border-top-left-radius:20px;border-bottom-left-radius:20px}.rounded-r-\[20px\]{border-top-right-radius:20px;border-bottom-right-radius:20px}.rounded-r-\[8px\]{border-top-right-radius:8px;border-bottom-right-radius:8px}.rounded-t-full{border-top-left-radius:9999px;border-top-right-radius:9999px}.border{border-width:1px}.border-0{border-width:0}.border-x-0{border-left-width:0;border-right-width:0}.border-b-0{border-bottom-width:0}.border-b-2{border-bottom-width:2px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-t-0{border-top-width:0}.border-b{border-bottom-width:1px}.border-none{border-style:none}.border-\[\#D7DFE9\]{--tw-border-opacity:1;border-color:rgb(215 223 233/var(--tw-border-opacity))}.border-primary-200{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.border-primary-400{--tw-border-opacity:1;border-color:rgb(78 179 161/var(--tw-border-opacity))}.border-primary-500{--tw-border-opacity:1;border-color:rgb(54 156 140/var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity:1;border-color:rgb(40 121 110/var(--tw-border-opacity))}.border-primary-700{--tw-border-opacity:1;border-color:rgb(35 98 90/var(--tw-border-opacity))}.border-secondary-500{--tw-border-opacity:1;border-color:rgb(255 78 51/var(--tw-border-opacity))}.border-transparent{border-color:#0000}.border-secondary-200{--tw-border-opacity:1;border-color:rgb(255 205 197/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-y-secondary-300{--tw-border-opacity:1;border-top-color:rgb(255 170 157/var(--tw-border-opacity));border-bottom-color:rgb(255 170 157/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-500\/25{background-color:#6b728040}.bg-primary-100{--tw-bg-opacity:1;background-color:rgb(213 242 235/var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity:1;background-color:rgb(171 228 214/var(--tw-bg-opacity))}.bg-primary-300{--tw-bg-opacity:1;background-color:rgb(122 206 189/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(243 250 249/var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(54 156 140/var(--tw-bg-opacity))}.bg-primary-800{--tw-bg-opacity:1;background-color:rgb(32 79 74/var(--tw-bg-opacity))}.bg-primary-950{--tw-bg-opacity:1;background-color:rgb(13 38 37/var(--tw-bg-opacity))}.bg-secondary-300{--tw-bg-opacity:1;background-color:rgb(255 170 157/var(--tw-bg-opacity))}.bg-secondary-500{--tw-bg-opacity:1;background-color:rgb(255 78 51/var(--tw-bg-opacity))}.bg-secondary-700{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.bg-transparent{background-color:initial}.bg-secondary-200{--tw-bg-opacity:1;background-color:rgb(255 205 197/var(--tw-bg-opacity))}.bg-opacity-0{--tw-bg-opacity:0}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-gray-100{--tw-gradient-from:#f3f4f6 var(--tw-gradient-from-position);--tw-gradient-to:#f3f4f600 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-900\/50{--tw-gradient-from:#1f423e80 var(--tw-gradient-from-position);--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-950{--tw-gradient-from:#0d2625 var(--tw-gradient-from-position);--tw-gradient-to:#0d262500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-10\%{--tw-gradient-from-position:10%}.via-primary-900{--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f423e var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-primary-900\/50{--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f423e80 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-10\%{--tw-gradient-via-position:10%}.to-primary-950{--tw-gradient-to:#0d2625 var(--tw-gradient-to-position)}.to-primary-950\/50{--tw-gradient-to:#0d262580 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:#0000 var(--tw-gradient-to-position)}.to-90\%{--tw-gradient-to-position:90%}.bg-\[length\:100px_auto\]{background-size:100px auto}.bg-cover{background-size:cover}.bg-clip-padding{background-clip:padding-box}.bg-center{background-position:50%}.fill-primary-400{fill:#4eb3a1}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.object-bottom{-o-object-position:bottom;object-position:bottom}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-10{padding:2.5rem}.p-16{padding:4rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-3{padding:.75rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-40{padding-left:10rem;padding-right:10rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-7{padding-left:1.75rem;padding-right:1.75rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pb-12{padding-bottom:3rem}.pb-16{padding-bottom:4rem}.pb-3{padding-bottom:.75rem}.pb-3\.5{padding-bottom:.875rem}.pb-32{padding-bottom:8rem}.pb-8{padding-bottom:2rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pr-7{padding-right:1.75rem}.pr-\[10px\]{padding-right:10px}.pt-12{padding-top:3rem}.pt-24{padding-top:6rem}.pt-4{padding-top:1rem}.pt-0{padding-top:0}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-start{text-align:start}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-7xl{font-size:4.5rem;line-height:1}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-\[450\]{font-weight:450}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-primary-300{--tw-text-opacity:1;color:rgb(122 206 189/var(--tw-text-opacity))}.text-primary-400{--tw-text-opacity:1;color:rgb(78 179 161/var(--tw-text-opacity))}.text-primary-50{--tw-text-opacity:1;color:rgb(243 250 249/var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity:1;color:rgb(54 156 140/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(40 121 110/var(--tw-text-opacity))}.text-primary-800{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(31 66 62/var(--tw-text-opacity))}.text-primary-950{--tw-text-opacity:1;color:rgb(13 38 37/var(--tw-text-opacity))}.text-secondary-300{--tw-text-opacity:1;color:rgb(255 170 157/var(--tw-text-opacity))}.text-secondary-50{--tw-text-opacity:1;color:rgb(255 243 241/var(--tw-text-opacity))}.text-secondary-500{--tw-text-opacity:1;color:rgb(255 78 51/var(--tw-text-opacity))}.text-secondary-700{--tw-text-opacity:1;color:rgb(200 38 13/var(--tw-text-opacity))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.\!opacity-0{opacity:0!important}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-gray-800\/5{--tw-shadow-color:#1f29370d;--tw-shadow:var(--tw-shadow-colored)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline-0{outline-width:0}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-gray-800\/\[\.075\]{--tw-ring-color:rgba(31,41,55,.075)}.ring-primary-100{--tw-ring-opacity:1;--tw-ring-color:rgb(213 242 235/var(--tw-ring-opacity))}.ring-primary-200{--tw-ring-opacity:1;--tw-ring-color:rgb(171 228 214/var(--tw-ring-opacity))}.blur{--tw-blur:blur(8px)}.blur,.blur-3xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-3xl{--tw-blur:blur(64px)}.brightness-0{--tw-brightness:brightness(0)}.brightness-0,.drop-shadow{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f)}.drop-shadow-sm{--tw-drop-shadow:drop-shadow(0 1px 1px #0000000d)}.drop-shadow-sm,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.backdrop-blur-sm,.backdrop-blur-xl{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.ease-\[cubic-bezier\(\.3\2c 2\.3\2c \.6\2c 1\)\]{transition-timing-function:cubic-bezier(.3,2.3,.6,1)}.\[-webkit-mask-image\:linear-gradient\(to_bottom\2c rgba\(255\2c 255\2c 255\2c 1\)_75\%\2c rgba\(255\2c 255\2c 255\2c 0\)\)\]{-webkit-mask-image:linear-gradient(180deg,#fff 75%,#fff0)}.\[-webkit-mask-image\:linear-gradient\(to_top\2c rgba\(255\2c 255\2c 255\2c 1\)_75\%\2c rgba\(255\2c 255\2c 255\2c 0\)\)\]{-webkit-mask-image:linear-gradient(0deg,#fff 75%,#fff0)}.hover\:isolate:hover{isolation:isolate}.hover\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.hover\:border-transparent:hover{border-color:#0000}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-primary-700:hover{--tw-bg-opacity:1;background-color:rgb(35 98 90/var(--tw-bg-opacity))}.hover\:bg-primary-800:hover{--tw-bg-opacity:1;background-color:rgb(32 79 74/var(--tw-bg-opacity))}.hover\:bg-secondary-700:hover{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.hover\:bg-primary-500:hover{--tw-bg-opacity:1;background-color:rgb(54 156 140/var(--tw-bg-opacity))}.hover\:bg-secondary-500:hover{--tw-bg-opacity:1;background-color:rgb(255 78 51/var(--tw-bg-opacity))}.hover\:bg-secondary-100:hover{--tw-bg-opacity:1;background-color:rgb(255 227 223/var(--tw-bg-opacity))}.hover\:bg-primary-100:hover{--tw-bg-opacity:1;background-color:rgb(213 242 235/var(--tw-bg-opacity))}.hover\:bg-secondary-200:hover{--tw-bg-opacity:1;background-color:rgb(255 205 197/var(--tw-bg-opacity))}.hover\:bg-opacity-50:hover{--tw-bg-opacity:0.5}.hover\:font-bold:hover{font-weight:700}.hover\:text-gray-50:hover{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.hover\:text-primary-400:hover{--tw-text-opacity:1;color:rgb(78 179 161/var(--tw-text-opacity))}.hover\:text-primary-50:hover{--tw-text-opacity:1;color:rgb(243 250 249/var(--tw-text-opacity))}.hover\:text-primary-500:hover{--tw-text-opacity:1;color:rgb(54 156 140/var(--tw-text-opacity))}.hover\:text-primary-700:hover{--tw-text-opacity:1;color:rgb(35 98 90/var(--tw-text-opacity))}.hover\:text-primary-800:hover{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.focus\:isolate:focus{isolation:isolate}.focus\:border-primary-300:focus{--tw-border-opacity:1;border-color:rgb(122 206 189/var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgb(54 156 140/var(--tw-border-opacity))}.focus\:border-transparent:focus{border-color:#0000}.focus\:bg-opacity-50:focus{--tw-bg-opacity:0.5}.focus\:text-primary-800:focus{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-\[1px\]:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-\[1px\]:focus,.focus\:ring-\[3px\]:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-\[3px\]:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-primary-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(171 228 214/var(--tw-ring-opacity))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group\/btn:hover .group-hover\/btn\:w-2{width:.5rem}.group\/btn:hover .group-hover\/btn\:w-2\.5{width:.625rem}.group:hover .group-hover\:scale-100{--tw-scale-x:1;--tw-scale-y:1}.group:hover .group-hover\:scale-100,.group:hover .group-hover\:scale-x-100{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-x-100{--tw-scale-x:1}.group:hover .group-hover\:opacity-100,.group\/btn:hover .group-hover\/btn\:opacity-100{opacity:1}@media (min-width:640px){.sm\:left-14{left:3.5rem}.sm\:w-auto{width:auto}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pb-40{padding-bottom:10rem}.sm\:pl-20{padding-left:5rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-5xl{font-size:3rem;line-height:1}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:block{display:block}.md\:h-screen{height:100vh}.md\:w-16{width:4rem}.md\:w-20{width:5rem}.md\:w-24{width:6rem}.md\:max-w-2xl{max-width:42rem}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:gap-16{gap:4rem}.md\:px-14{padding-left:3.5rem;padding-right:3.5rem}.md\:py-10{padding-top:2.5rem;padding-bottom:2.5rem}.md\:pb-52{padding-bottom:13rem}.md\:text-3xl{font-size:1.875rem;line-height:2.25rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1024px){.lg\:left-20{left:5rem}.lg\:order-last{order:9999}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:-my-11{margin-top:-2.75rem;margin-bottom:-2.75rem}.lg\:-mr-9{margin-right:-2.25rem}.lg\:mb-0{margin-bottom:0}.lg\:mb-16{margin-bottom:4rem}.lg\:mt-0{margin-top:0}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:max-w-4xl{max-width:56rem}.lg\:max-w-7xl{max-width:80rem}.lg\:max-w-max{max-width:-moz-max-content;max-width:max-content}.lg\:max-w-xl{max-width:36rem}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:gap-5{gap:1.25rem}.lg\:p-10{padding:2.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-12{padding-top:3rem;padding-bottom:3rem}.lg\:py-24{padding-top:6rem;padding-bottom:6rem}.lg\:pb-20{padding-bottom:5rem}.lg\:pb-60{padding-bottom:15rem}.lg\:pl-28{padding-left:7rem}.lg\:pt-20{padding-top:5rem}.lg\:pt-40{padding-top:10rem}.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}.lg\:text-5xl{font-size:3rem;line-height:1}.lg\:text-6xl{font-size:3.75rem;line-height:1}.lg\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media (min-width:1280px){.xl\:col-span-3{grid-column:span 3/span 3}.xl\:mx-auto{margin-left:auto;margin-right:auto}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:w-auto{width:auto}.xl\:w-full{width:100%}.xl\:max-w-lg{max-width:32rem}.xl\:max-w-xl{max-width:36rem}.xl\:gap-16{gap:4rem}.xl\:gap-x-16{-moz-column-gap:4rem;column-gap:4rem}.xl\:px-20{padding-left:5rem;padding-right:5rem}.xl\:py-20{padding-top:5rem;padding-bottom:5rem}.xl\:py-32{padding-top:8rem}.xl\:pb-32,.xl\:py-32{padding-bottom:8rem}.xl\:pb-\[16\.5rem\]{padding-bottom:16.5rem}.xl\:text-xl{font-size:1.25rem;line-height:1.75rem}}

From f3dd1778fd4801bc61a3149f04dde3de18a3694e Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Mon, 30 Sep 2024 14:38:44 +0200
Subject: [PATCH 06/64] use root policy

---
 unikernel.ml     | 19 ++++---------------
 update_policy.ml |  2 +-
 2 files changed, 5 insertions(+), 16 deletions(-)

diff --git a/unikernel.ml b/unikernel.ml
index 573426e1..b4084b3b 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -784,24 +784,13 @@ struct
     match User_model.find_user_by_key uuid users with
     | Some u ->
         let policy =
-          match Albatross.policy albatross u.name with
+          match Albatross.policy albatross ~domain:u.name with
           | Ok p -> p
           | Error _ -> None
         in
-        (Albatross.query albatross ~domain:"." (`Policy_cmd `Policy_info)
-         >|= function
-         | Error msg ->
-             Logs.err (fun m ->
-                 m "error while communicating with albatross: %s" msg);
-             []
-         | Ok (_hdr, `Success (`Policies policies)) -> policies
-         | Ok reply ->
-             Logs.err (fun m ->
-                 m "expected a policy info reply, received %a"
-                   (Vmm_commands.pp_wire ~verbose:false)
-                   reply);
-             [])
-        >>= fun root_policy ->
+        let root_policy =
+          match Albatross.policy albatross with Ok p -> p | Error _ -> None
+        in
         Lwt.return
           (reply reqd ~content_type:"text/html"
              (Dashboard.dashboard_layout user
diff --git a/update_policy.ml b/update_policy.ml
index 28c7b582..482bb125 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -10,7 +10,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
       }
   in
   let root_policy =
-    match root_policy with (_, hd) :: _ -> hd | _ -> empty_policy
+    match root_policy with Some p -> p | None -> empty_policy
   in
   let policy =
     match user_policy with None -> empty_policy | Some policy -> policy

From 96ed1cc43e118e8900d3118ab27e176ec605d43e Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 11:07:58 +0200
Subject: [PATCH 07/64] get available resources

---
 albatross.ml | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/albatross.ml b/albatross.ml
index 84044ab2..f790c08e 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -10,6 +10,16 @@ struct
     mutable policies : Vmm_core.Policy.t Vmm_trie.t;
   }
 
+  let empty_policy =
+    Vmm_core.Policy.
+      {
+        vms = 0;
+        cpuids = Vmm_core.IS.of_list [];
+        memory = 0;
+        block = Some 0;
+        bridges = Vmm_core.String_set.of_list [];
+      }
+
   let policy ?domain t =
     let ( let* ) = Result.bind in
     let* path =
@@ -38,6 +48,35 @@ struct
     in
     Ok (Vmm_trie.fold path t.policies (fun name p acc -> (name, p) :: acc) [])
 
+  let policy_resource_used () t =
+    let root_policy =
+      match policy t with
+      | Ok p -> ( match p with Some p -> p | None -> empty_policy)
+      | Error _ -> empty_policy
+    in
+    let policies = match policies t with Ok p -> p | Error _err -> [] in
+    let vms_used, memory_used, storage_used =
+      List.fold_left
+        (fun (total_vms, total_memory, total_block) (name, policy) ->
+          if name <> Vmm_core.Name.root then
+            ( total_vms + policy.Vmm_core.Policy.vms,
+              total_memory + policy.memory,
+              total_block + match policy.block with Some b -> b | None -> 0 )
+          else (total_vms, total_memory, total_block))
+        (0, 0, 0) policies
+    in
+    Vmm_core.Policy.
+      {
+        vms = root_policy.vms - vms_used;
+        cpuids = root_policy.cpuids;
+        memory = root_policy.memory - memory_used;
+        block =
+          (match root_policy.block with
+          | Some b -> if b > 0 then Some (b - storage_used) else None
+          | None -> None);
+        bridges = root_policy.bridges;
+      }
+
   let key_ids exts pub issuer =
     let open X509 in
     let auth = (Some (Public_key.id issuer), General_name.empty, None) in

From cb442ff4ee5ca574018289998c4774e74ae1827f Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 11:08:09 +0200
Subject: [PATCH 08/64] display available

---
 update_policy.ml | 43 +++++++++++++++++++++++--------------------
 1 file changed, 23 insertions(+), 20 deletions(-)

diff --git a/update_policy.ml b/update_policy.ml
index 482bb125..5c3426f7 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -1,20 +1,4 @@
 let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
-  let empty_policy =
-    Vmm_core.Policy.
-      {
-        vms = 0;
-        cpuids = Vmm_core.IS.of_list [];
-        memory = 0;
-        block = Some 0;
-        bridges = Vmm_core.String_set.of_list [];
-      }
-  in
-  let root_policy =
-    match root_policy with Some p -> p | None -> empty_policy
-  in
-  let policy =
-    match user_policy with None -> empty_policy | Some policy -> policy
-  in
   Tyxml_html.(
     section
       ~a:[ a_id "policy-form" ]
@@ -32,7 +16,9 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
               [ txt "Allowed VMs" ];
             p
               [
-                txt ("total: " ^ string_of_int root_policy.Vmm_core.Policy.vms);
+                txt
+                  ("total available: "
+                  ^ string_of_int root_policy.Vmm_core.Policy.vms);
               ];
             div
               ~a:
@@ -40,7 +26,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
                   a_class [ "space-x-5 my-4" ];
                   Unsafe.string_attrib "x-data"
                     ("{count : "
-                    ^ string_of_int policy.Vmm_core.Policy.vms
+                    ^ string_of_int user_policy.Vmm_core.Policy.vms
                     ^ "}");
                 ]
               [
@@ -94,13 +80,20 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
               [ txt "Allowed Memory" ];
+            p
+              [
+                txt
+                  ("total available: "
+                  ^ string_of_int root_policy.Vmm_core.Policy.memory
+                  ^ " MB");
+              ];
             div
               ~a:
                 [
                   a_class [ "space-x-5 my-4" ];
                   Unsafe.string_attrib "x-data"
                     ("{count : "
-                    ^ string_of_int policy.Vmm_core.Policy.memory
+                    ^ string_of_int user_policy.Vmm_core.Policy.memory
                     ^ "}");
                 ]
               [
@@ -156,13 +149,23 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
               [ txt "Allowed Storage" ];
+            p
+              [
+                txt
+                  ("total available: "
+                  ^ string_of_int
+                      (match root_policy.Vmm_core.Policy.block with
+                      | None -> 0
+                      | Some x -> x)
+                  ^ " MB");
+              ];
             div
               ~a:
                 [
                   a_class [ "space-x-5 my-4" ];
                   Unsafe.string_attrib "x-data"
                     ("{count : "
-                    ^ (match policy.Vmm_core.Policy.block with
+                    ^ (match user_policy.Vmm_core.Policy.block with
                       | None -> string_of_int 0
                       | Some x -> string_of_int x)
                     ^ "}");

From c6720ef5ce329cf586fc8dcb5ddac616b42f0f30 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 11:08:36 +0200
Subject: [PATCH 09/64] user existing policy and remain resources

---
 unikernel.ml | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/unikernel.ml b/unikernel.ml
index b4084b3b..ffcb1bd4 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -783,21 +783,20 @@ struct
     let users = User_model.create_user_uuid_map (snd store).Storage.users in
     match User_model.find_user_by_key uuid users with
     | Some u ->
-        let policy =
+        let user_policy =
           match Albatross.policy albatross ~domain:u.name with
-          | Ok p -> p
-          | Error _ -> None
-        in
-        let root_policy =
-          match Albatross.policy albatross with Ok p -> p | Error _ -> None
+          | Ok p -> (
+              match p with Some p -> p | None -> Albatross.empty_policy)
+          | Error _ -> Albatross.empty_policy
         in
+        let policy_avalaible = Albatross.policy_resource_used () albatross in
         Lwt.return
           (reply reqd ~content_type:"text/html"
              (Dashboard.dashboard_layout user
                 ~page_title:(String.capitalize_ascii u.name ^ " | Mollymawk")
                 ~content:
-                  (Update_policy.update_policy_layout u ~user_policy:policy
-                     ~root_policy)
+                  (Update_policy.update_policy_layout u ~user_policy
+                     ~root_policy:policy_avalaible)
                 ~icon:"/images/robur.png" ())
              `OK)
     | None ->

From 46a5c2ea2e7752fbd272bafb72238ebf14f3ed37 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 11:23:45 +0200
Subject: [PATCH 10/64] pass values to the multiselect

---
 assets/main.js   |  6 +++---
 update_policy.ml | 19 +++++++++++++++++--
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/assets/main.js b/assets/main.js
index a210e6f3..d479708e 100644
--- a/assets/main.js
+++ b/assets/main.js
@@ -252,11 +252,11 @@ async function toggleUserAdminStatus(uuid) {
 }
 
 
-function multiselect() {
+function multiselect(selected, options) {
 	return {
 		isOpen: false,
-		selected: [1, 2, 3], // TODO: replace with variable
-		options: [1, 2, 3, 4, 5, 6, 7], // TODO: replace with variable
+		selected: selected,
+		options: options,
 		toggleDropdown() {
 			this.isOpen = !this.isOpen;
 		},
diff --git a/update_policy.ml b/update_policy.ml
index 5c3426f7..a836d202 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -226,7 +226,15 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
               ~a:
                 [
                   Unsafe.string_attrib "x-data"
-                    "multiselect([\n                                    ])";
+                    ("multiselect(" ^ "[\""
+                    ^ String.concat "\", \""
+                        (List.map string_of_int
+                           (Vmm_core.IS.elements user_policy.cpuids))
+                    ^ "\"]" ^ "," ^ "[\""
+                    ^ String.concat "\", \""
+                        (List.map string_of_int
+                           (Vmm_core.IS.elements root_policy.cpuids))
+                    ^ "\"]" ^ ")");
                   a_class [ "multiselect border my-4 p-4 rounded" ];
                 ]
               [
@@ -340,7 +348,14 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
             div
               ~a:
                 [
-                  Unsafe.string_attrib "x-data" "multiselect()";
+                  Unsafe.string_attrib "x-data"
+                    ("multiselect(" ^ "[\""
+                    ^ String.concat "\", \""
+                        (Vmm_core.String_set.elements user_policy.bridges)
+                    ^ "\"]" ^ "," ^ "[\""
+                    ^ String.concat "\", \""
+                        (Vmm_core.String_set.elements root_policy.bridges)
+                    ^ "\"]" ^ ")");
                   a_class [ "multiselect border my-4 p-4 rounded" ];
                 ]
               [

From 8ce420df3ff1f752b7434ee3726888770389dcd7 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 11:37:26 +0200
Subject: [PATCH 11/64] update styles

---
 assets/style.css | 2 +-
 update_policy.ml | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/assets/style.css b/assets/style.css
index 81de7543..0a1f7f74 100755
--- a/assets/style.css
+++ b/assets/style.css
@@ -1 +1 @@
-/*! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-1{inset:.25rem}.-inset-x-12{left:-3rem;right:-3rem}.inset-x-1{left:.25rem;right:.25rem}.inset-x-4{left:1rem;right:1rem}.-bottom-0{bottom:0}.-bottom-0\.5{bottom:-.125rem}.-bottom-2{bottom:-.5rem}.-bottom-6{bottom:-1.5rem}.-left-1\/4{left:-25%}.-left-2{left:-.5rem}.-top-1\/4{top:-25%}.-top-4{top:-1rem}.-top-6{top:-1.5rem}.bottom-0{bottom:0}.bottom-10{bottom:2.5rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.left-8{left:2rem}.right-4{right:1rem}.top-0{top:0}.top-1\/4{top:25%}.right-0{right:0}.top-1{top:.25rem}.z-50{z-index:50}.z-\[-1\]{z-index:-1}.z-\[500\]{z-index:500}.z-10{z-index:10}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-7{grid-column:span 7/span 7}.-m-1{margin:-.25rem}.-m-1\.5{margin:-.375rem}.m-auto{margin:auto}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.-mx-3{margin-left:-.75rem;margin-right:-.75rem}.-mb-12{margin-bottom:-3rem}.-mb-6{margin-bottom:-1.5rem}.-ml-2{margin-left:-.5rem}.-mr-2{margin-right:-.5rem}.-mr-2\.5{margin-right:-.625rem}.-mt-1{margin-top:-.25rem}.-mt-24{margin-top:-6rem}.-mt-3{margin-top:-.75rem}.mb-0{margin-bottom:0}.mb-14{margin-bottom:3.5rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-7{margin-bottom:1.75rem}.mb-8{margin-bottom:2rem}.mb-9{margin-bottom:2.25rem}.mb-auto{margin-bottom:auto}.mb-px{margin-bottom:1px}.ml-5{margin-left:1.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.\!mt-0{margin-top:0!important}.\!mt-3{margin-top:.75rem!important}.ml-2{margin-left:.5rem}.mt-3{margin-top:.75rem}.mb-1{margin-bottom:.25rem}.ml-1{margin-left:.25rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.size-2{width:.5rem;height:.5rem}.size-2\.5{width:.625rem;height:.625rem}.h-12{height:3rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-24{height:6rem}.h-32{height:8rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[150\%\]{height:150%}.h-full{height:100%}.h-px{height:1px}.h-4{height:1rem}.h-6{height:1.5rem}.h-3{height:.75rem}.max-h-screen{max-height:100vh}.max-h-52{max-height:13rem}.min-h-screen{min-height:100vh}.w-0{width:0}.w-14{width:3.5rem}.w-16{width:4rem}.w-32{width:8rem}.w-60{width:15rem}.w-\[150\%\]{width:150%}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.w-4{width:1rem}.w-64{width:16rem}.w-6{width:1.5rem}.w-3{width:.75rem}.min-w-full{min-width:100%}.max-w-3xl{max-width:48rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[94\%\]{max-width:94%}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-none{flex:none}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.origin-bottom{transform-origin:bottom}.-translate-x-1\/2{--tw-translate-x:-50%}.-rotate-3,.-translate-x-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-3{--tw-rotate:-3deg}.rotate-3{--tw-rotate:3deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-0{--tw-scale-x:0;--tw-scale-y:0}.scale-0,.scale-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-x-0{--tw-scale-x:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform-gpu{transform:translate3d(var(--tw-translate-x),var(--tw-translate-y),0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-0{gap:0}.gap-0\.5{gap:.125rem}.gap-10{gap:2.5rem}.gap-12{gap:3rem}.gap-20{gap:5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-2{gap:.5rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.gap-y-16{row-gap:4rem}.gap-y-4{row-gap:1rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-20>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(5rem*var(--tw-space-x-reverse));margin-left:calc(5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.25rem*var(--tw-space-x-reverse));margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.justify-self-start{justify-self:start}.justify-self-end{justify-self:end}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;white-space:nowrap}.text-ellipsis,.truncate{text-overflow:ellipsis}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.text-wrap{text-wrap:wrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-l-\[20px\]{border-top-left-radius:20px;border-bottom-left-radius:20px}.rounded-r-\[20px\]{border-top-right-radius:20px;border-bottom-right-radius:20px}.rounded-r-\[8px\]{border-top-right-radius:8px;border-bottom-right-radius:8px}.rounded-t-full{border-top-left-radius:9999px;border-top-right-radius:9999px}.border{border-width:1px}.border-0{border-width:0}.border-x-0{border-left-width:0;border-right-width:0}.border-b-0{border-bottom-width:0}.border-b-2{border-bottom-width:2px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-t-0{border-top-width:0}.border-b{border-bottom-width:1px}.border-none{border-style:none}.border-\[\#D7DFE9\]{--tw-border-opacity:1;border-color:rgb(215 223 233/var(--tw-border-opacity))}.border-primary-200{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.border-primary-400{--tw-border-opacity:1;border-color:rgb(78 179 161/var(--tw-border-opacity))}.border-primary-500{--tw-border-opacity:1;border-color:rgb(54 156 140/var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity:1;border-color:rgb(40 121 110/var(--tw-border-opacity))}.border-primary-700{--tw-border-opacity:1;border-color:rgb(35 98 90/var(--tw-border-opacity))}.border-secondary-500{--tw-border-opacity:1;border-color:rgb(255 78 51/var(--tw-border-opacity))}.border-transparent{border-color:#0000}.border-secondary-200{--tw-border-opacity:1;border-color:rgb(255 205 197/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-y-secondary-300{--tw-border-opacity:1;border-top-color:rgb(255 170 157/var(--tw-border-opacity));border-bottom-color:rgb(255 170 157/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-500\/25{background-color:#6b728040}.bg-primary-100{--tw-bg-opacity:1;background-color:rgb(213 242 235/var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity:1;background-color:rgb(171 228 214/var(--tw-bg-opacity))}.bg-primary-300{--tw-bg-opacity:1;background-color:rgb(122 206 189/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(243 250 249/var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(54 156 140/var(--tw-bg-opacity))}.bg-primary-800{--tw-bg-opacity:1;background-color:rgb(32 79 74/var(--tw-bg-opacity))}.bg-primary-950{--tw-bg-opacity:1;background-color:rgb(13 38 37/var(--tw-bg-opacity))}.bg-secondary-300{--tw-bg-opacity:1;background-color:rgb(255 170 157/var(--tw-bg-opacity))}.bg-secondary-500{--tw-bg-opacity:1;background-color:rgb(255 78 51/var(--tw-bg-opacity))}.bg-secondary-700{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.bg-transparent{background-color:initial}.bg-secondary-200{--tw-bg-opacity:1;background-color:rgb(255 205 197/var(--tw-bg-opacity))}.bg-opacity-0{--tw-bg-opacity:0}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-gray-100{--tw-gradient-from:#f3f4f6 var(--tw-gradient-from-position);--tw-gradient-to:#f3f4f600 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-900\/50{--tw-gradient-from:#1f423e80 var(--tw-gradient-from-position);--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-950{--tw-gradient-from:#0d2625 var(--tw-gradient-from-position);--tw-gradient-to:#0d262500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-10\%{--tw-gradient-from-position:10%}.via-primary-900{--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f423e var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-primary-900\/50{--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f423e80 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-10\%{--tw-gradient-via-position:10%}.to-primary-950{--tw-gradient-to:#0d2625 var(--tw-gradient-to-position)}.to-primary-950\/50{--tw-gradient-to:#0d262580 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:#0000 var(--tw-gradient-to-position)}.to-90\%{--tw-gradient-to-position:90%}.bg-\[length\:100px_auto\]{background-size:100px auto}.bg-cover{background-size:cover}.bg-clip-padding{background-clip:padding-box}.bg-center{background-position:50%}.fill-primary-400{fill:#4eb3a1}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.object-bottom{-o-object-position:bottom;object-position:bottom}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-10{padding:2.5rem}.p-16{padding:4rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-3{padding:.75rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-40{padding-left:10rem;padding-right:10rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-7{padding-left:1.75rem;padding-right:1.75rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.px-1{padding-left:.25rem;padding-right:.25rem}.pb-12{padding-bottom:3rem}.pb-16{padding-bottom:4rem}.pb-3{padding-bottom:.75rem}.pb-3\.5{padding-bottom:.875rem}.pb-32{padding-bottom:8rem}.pb-8{padding-bottom:2rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pr-7{padding-right:1.75rem}.pr-\[10px\]{padding-right:10px}.pt-12{padding-top:3rem}.pt-24{padding-top:6rem}.pt-4{padding-top:1rem}.pt-0{padding-top:0}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-start{text-align:start}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-7xl{font-size:4.5rem;line-height:1}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-\[450\]{font-weight:450}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-primary-300{--tw-text-opacity:1;color:rgb(122 206 189/var(--tw-text-opacity))}.text-primary-400{--tw-text-opacity:1;color:rgb(78 179 161/var(--tw-text-opacity))}.text-primary-50{--tw-text-opacity:1;color:rgb(243 250 249/var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity:1;color:rgb(54 156 140/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(40 121 110/var(--tw-text-opacity))}.text-primary-800{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(31 66 62/var(--tw-text-opacity))}.text-primary-950{--tw-text-opacity:1;color:rgb(13 38 37/var(--tw-text-opacity))}.text-secondary-300{--tw-text-opacity:1;color:rgb(255 170 157/var(--tw-text-opacity))}.text-secondary-50{--tw-text-opacity:1;color:rgb(255 243 241/var(--tw-text-opacity))}.text-secondary-500{--tw-text-opacity:1;color:rgb(255 78 51/var(--tw-text-opacity))}.text-secondary-700{--tw-text-opacity:1;color:rgb(200 38 13/var(--tw-text-opacity))}.opacity-0{opacity:0}.opacity-50{opacity:.5}.\!opacity-0{opacity:0!important}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-gray-800\/5{--tw-shadow-color:#1f29370d;--tw-shadow:var(--tw-shadow-colored)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline-0{outline-width:0}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-gray-800\/\[\.075\]{--tw-ring-color:rgba(31,41,55,.075)}.ring-primary-100{--tw-ring-opacity:1;--tw-ring-color:rgb(213 242 235/var(--tw-ring-opacity))}.ring-primary-200{--tw-ring-opacity:1;--tw-ring-color:rgb(171 228 214/var(--tw-ring-opacity))}.blur{--tw-blur:blur(8px)}.blur,.blur-3xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-3xl{--tw-blur:blur(64px)}.brightness-0{--tw-brightness:brightness(0)}.brightness-0,.drop-shadow{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f)}.drop-shadow-sm{--tw-drop-shadow:drop-shadow(0 1px 1px #0000000d)}.drop-shadow-sm,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.backdrop-blur-sm,.backdrop-blur-xl{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.ease-\[cubic-bezier\(\.3\2c 2\.3\2c \.6\2c 1\)\]{transition-timing-function:cubic-bezier(.3,2.3,.6,1)}.\[-webkit-mask-image\:linear-gradient\(to_bottom\2c rgba\(255\2c 255\2c 255\2c 1\)_75\%\2c rgba\(255\2c 255\2c 255\2c 0\)\)\]{-webkit-mask-image:linear-gradient(180deg,#fff 75%,#fff0)}.\[-webkit-mask-image\:linear-gradient\(to_top\2c rgba\(255\2c 255\2c 255\2c 1\)_75\%\2c rgba\(255\2c 255\2c 255\2c 0\)\)\]{-webkit-mask-image:linear-gradient(0deg,#fff 75%,#fff0)}.hover\:isolate:hover{isolation:isolate}.hover\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.hover\:border-transparent:hover{border-color:#0000}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-primary-700:hover{--tw-bg-opacity:1;background-color:rgb(35 98 90/var(--tw-bg-opacity))}.hover\:bg-primary-800:hover{--tw-bg-opacity:1;background-color:rgb(32 79 74/var(--tw-bg-opacity))}.hover\:bg-secondary-700:hover{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.hover\:bg-primary-500:hover{--tw-bg-opacity:1;background-color:rgb(54 156 140/var(--tw-bg-opacity))}.hover\:bg-secondary-500:hover{--tw-bg-opacity:1;background-color:rgb(255 78 51/var(--tw-bg-opacity))}.hover\:bg-secondary-100:hover{--tw-bg-opacity:1;background-color:rgb(255 227 223/var(--tw-bg-opacity))}.hover\:bg-primary-100:hover{--tw-bg-opacity:1;background-color:rgb(213 242 235/var(--tw-bg-opacity))}.hover\:bg-secondary-200:hover{--tw-bg-opacity:1;background-color:rgb(255 205 197/var(--tw-bg-opacity))}.hover\:bg-opacity-50:hover{--tw-bg-opacity:0.5}.hover\:font-bold:hover{font-weight:700}.hover\:text-gray-50:hover{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.hover\:text-primary-400:hover{--tw-text-opacity:1;color:rgb(78 179 161/var(--tw-text-opacity))}.hover\:text-primary-50:hover{--tw-text-opacity:1;color:rgb(243 250 249/var(--tw-text-opacity))}.hover\:text-primary-500:hover{--tw-text-opacity:1;color:rgb(54 156 140/var(--tw-text-opacity))}.hover\:text-primary-700:hover{--tw-text-opacity:1;color:rgb(35 98 90/var(--tw-text-opacity))}.hover\:text-primary-800:hover{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.focus\:isolate:focus{isolation:isolate}.focus\:border-primary-300:focus{--tw-border-opacity:1;border-color:rgb(122 206 189/var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgb(54 156 140/var(--tw-border-opacity))}.focus\:border-transparent:focus{border-color:#0000}.focus\:bg-opacity-50:focus{--tw-bg-opacity:0.5}.focus\:text-primary-800:focus{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-\[1px\]:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-\[1px\]:focus,.focus\:ring-\[3px\]:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-\[3px\]:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-primary-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(171 228 214/var(--tw-ring-opacity))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group\/btn:hover .group-hover\/btn\:w-2{width:.5rem}.group\/btn:hover .group-hover\/btn\:w-2\.5{width:.625rem}.group:hover .group-hover\:scale-100{--tw-scale-x:1;--tw-scale-y:1}.group:hover .group-hover\:scale-100,.group:hover .group-hover\:scale-x-100{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-x-100{--tw-scale-x:1}.group:hover .group-hover\:opacity-100,.group\/btn:hover .group-hover\/btn\:opacity-100{opacity:1}@media (min-width:640px){.sm\:left-14{left:3.5rem}.sm\:w-auto{width:auto}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pb-40{padding-bottom:10rem}.sm\:pl-20{padding-left:5rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-5xl{font-size:3rem;line-height:1}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:block{display:block}.md\:h-screen{height:100vh}.md\:w-16{width:4rem}.md\:w-20{width:5rem}.md\:w-24{width:6rem}.md\:max-w-2xl{max-width:42rem}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:gap-16{gap:4rem}.md\:px-14{padding-left:3.5rem;padding-right:3.5rem}.md\:py-10{padding-top:2.5rem;padding-bottom:2.5rem}.md\:pb-52{padding-bottom:13rem}.md\:text-3xl{font-size:1.875rem;line-height:2.25rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1024px){.lg\:left-20{left:5rem}.lg\:order-last{order:9999}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:-my-11{margin-top:-2.75rem;margin-bottom:-2.75rem}.lg\:-mr-9{margin-right:-2.25rem}.lg\:mb-0{margin-bottom:0}.lg\:mb-16{margin-bottom:4rem}.lg\:mt-0{margin-top:0}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:max-w-4xl{max-width:56rem}.lg\:max-w-7xl{max-width:80rem}.lg\:max-w-max{max-width:-moz-max-content;max-width:max-content}.lg\:max-w-xl{max-width:36rem}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:gap-5{gap:1.25rem}.lg\:p-10{padding:2.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-12{padding-top:3rem;padding-bottom:3rem}.lg\:py-24{padding-top:6rem;padding-bottom:6rem}.lg\:pb-20{padding-bottom:5rem}.lg\:pb-60{padding-bottom:15rem}.lg\:pl-28{padding-left:7rem}.lg\:pt-20{padding-top:5rem}.lg\:pt-40{padding-top:10rem}.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}.lg\:text-5xl{font-size:3rem;line-height:1}.lg\:text-6xl{font-size:3.75rem;line-height:1}.lg\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media (min-width:1280px){.xl\:col-span-3{grid-column:span 3/span 3}.xl\:mx-auto{margin-left:auto;margin-right:auto}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:w-auto{width:auto}.xl\:w-full{width:100%}.xl\:max-w-lg{max-width:32rem}.xl\:max-w-xl{max-width:36rem}.xl\:gap-16{gap:4rem}.xl\:gap-x-16{-moz-column-gap:4rem;column-gap:4rem}.xl\:px-20{padding-left:5rem;padding-right:5rem}.xl\:py-20{padding-top:5rem;padding-bottom:5rem}.xl\:py-32{padding-top:8rem}.xl\:pb-32,.xl\:py-32{padding-bottom:8rem}.xl\:pb-\[16\.5rem\]{padding-bottom:16.5rem}.xl\:text-xl{font-size:1.25rem;line-height:1.75rem}}
+/*! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-1{inset:.25rem}.-inset-x-12{left:-3rem;right:-3rem}.inset-x-1{left:.25rem;right:.25rem}.inset-x-4{left:1rem;right:1rem}.-bottom-0{bottom:0}.-bottom-0\.5{bottom:-.125rem}.-bottom-2{bottom:-.5rem}.-bottom-6{bottom:-1.5rem}.-left-1\/4{left:-25%}.-left-2{left:-.5rem}.-top-1\/4{top:-25%}.-top-4{top:-1rem}.-top-6{top:-1.5rem}.bottom-0{bottom:0}.bottom-10{bottom:2.5rem}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.left-8{left:2rem}.right-4{right:1rem}.top-0{top:0}.top-1\/4{top:25%}.z-50{z-index:50}.z-\[-1\]{z-index:-1}.z-\[500\]{z-index:500}.col-span-1{grid-column:span 1/span 1}.col-span-10{grid-column:span 10/span 10}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.col-span-7{grid-column:span 7/span 7}.-m-1{margin:-.25rem}.-m-1\.5{margin:-.375rem}.m-auto{margin:auto}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-10{margin-top:2.5rem;margin-bottom:2.5rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.-mb-12{margin-bottom:-3rem}.-mb-6{margin-bottom:-1.5rem}.-ml-2{margin-left:-.5rem}.-mr-2{margin-right:-.5rem}.-mr-2\.5{margin-right:-.625rem}.-mt-1{margin-top:-.25rem}.-mt-24{margin-top:-6rem}.-mt-3{margin-top:-.75rem}.mb-0{margin-bottom:0}.mb-14{margin-bottom:3.5rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-7{margin-bottom:1.75rem}.mb-8{margin-bottom:2rem}.mb-9{margin-bottom:2.25rem}.mb-auto{margin-bottom:auto}.mb-px{margin-bottom:1px}.ml-1{margin-left:.25rem}.ml-5{margin-left:1.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-10{margin-top:2.5rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.size-2{width:.5rem;height:.5rem}.size-2\.5{width:.625rem;height:.625rem}.h-12{height:3rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-24{height:6rem}.h-32{height:8rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-96{height:24rem}.h-\[150\%\]{height:150%}.h-full{height:100%}.h-px{height:1px}.h-4{height:1rem}.h-3{height:.75rem}.max-h-screen{max-height:100vh}.min-h-screen{min-height:100vh}.w-0{width:0}.w-14{width:3.5rem}.w-16{width:4rem}.w-32{width:8rem}.w-6{width:1.5rem}.w-60{width:15rem}.w-\[150\%\]{width:150%}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.w-4{width:1rem}.w-3{width:.75rem}.min-w-full{min-width:100%}.max-w-3xl{max-width:48rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[94\%\]{max-width:94%}.max-w-full{max-width:100%}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.flex-none{flex:none}.shrink-0{flex-shrink:0}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.origin-bottom{transform-origin:bottom}.-translate-x-1\/2{--tw-translate-x:-50%}.-rotate-3,.-translate-x-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-rotate-3{--tw-rotate:-3deg}.rotate-3{--tw-rotate:3deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-0{--tw-scale-x:0;--tw-scale-y:0}.scale-0,.scale-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-x-0{--tw-scale-x:0}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform-gpu{transform:translate3d(var(--tw-translate-x),var(--tw-translate-y),0) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-0{gap:0}.gap-0\.5{gap:.125rem}.gap-10{gap:2.5rem}.gap-12{gap:3rem}.gap-20{gap:5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-8{gap:2rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.gap-y-16{row-gap:4rem}.gap-y-4{row-gap:1rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-20>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(5rem*var(--tw-space-x-reverse));margin-left:calc(5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.25rem*var(--tw-space-x-reverse));margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity))}.justify-self-start{justify-self:start}.justify-self-end{justify-self:end}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.whitespace-normal{white-space:normal}.whitespace-nowrap{white-space:nowrap}.text-wrap{text-wrap:wrap}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-l-\[20px\]{border-top-left-radius:20px;border-bottom-left-radius:20px}.rounded-r-\[20px\]{border-top-right-radius:20px;border-bottom-right-radius:20px}.rounded-r-\[8px\]{border-top-right-radius:8px;border-bottom-right-radius:8px}.rounded-t-full{border-top-left-radius:9999px;border-top-right-radius:9999px}.border{border-width:1px}.border-x-0{border-left-width:0;border-right-width:0}.border-b-0{border-bottom-width:0}.border-b-2{border-bottom-width:2px}.border-l-0{border-left-width:0}.border-r-0{border-right-width:0}.border-t-0{border-top-width:0}.border-none{border-style:none}.border-\[\#D7DFE9\]{--tw-border-opacity:1;border-color:rgb(215 223 233/var(--tw-border-opacity))}.border-primary-200{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.border-primary-400{--tw-border-opacity:1;border-color:rgb(78 179 161/var(--tw-border-opacity))}.border-primary-500{--tw-border-opacity:1;border-color:rgb(54 156 140/var(--tw-border-opacity))}.border-primary-600{--tw-border-opacity:1;border-color:rgb(40 121 110/var(--tw-border-opacity))}.border-primary-700{--tw-border-opacity:1;border-color:rgb(35 98 90/var(--tw-border-opacity))}.border-secondary-500{--tw-border-opacity:1;border-color:rgb(255 78 51/var(--tw-border-opacity))}.border-transparent{border-color:#0000}.border-y-secondary-300{--tw-border-opacity:1;border-top-color:rgb(255 170 157/var(--tw-border-opacity));border-bottom-color:rgb(255 170 157/var(--tw-border-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-500\/25{background-color:#6b728040}.bg-primary-100{--tw-bg-opacity:1;background-color:rgb(213 242 235/var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity:1;background-color:rgb(171 228 214/var(--tw-bg-opacity))}.bg-primary-300{--tw-bg-opacity:1;background-color:rgb(122 206 189/var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(243 250 249/var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(54 156 140/var(--tw-bg-opacity))}.bg-primary-800{--tw-bg-opacity:1;background-color:rgb(32 79 74/var(--tw-bg-opacity))}.bg-primary-950{--tw-bg-opacity:1;background-color:rgb(13 38 37/var(--tw-bg-opacity))}.bg-secondary-300{--tw-bg-opacity:1;background-color:rgb(255 170 157/var(--tw-bg-opacity))}.bg-secondary-500{--tw-bg-opacity:1;background-color:rgb(255 78 51/var(--tw-bg-opacity))}.bg-secondary-700{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.bg-transparent{background-color:initial}.bg-opacity-0{--tw-bg-opacity:0}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-gray-100{--tw-gradient-from:#f3f4f6 var(--tw-gradient-from-position);--tw-gradient-to:#f3f4f600 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-900\/50{--tw-gradient-from:#1f423e80 var(--tw-gradient-from-position);--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-950{--tw-gradient-from:#0d2625 var(--tw-gradient-from-position);--tw-gradient-to:#0d262500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:#0000 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-10\%{--tw-gradient-from-position:10%}.via-primary-900{--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f423e var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-primary-900\/50{--tw-gradient-to:#1f423e00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f423e80 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-10\%{--tw-gradient-via-position:10%}.to-primary-950{--tw-gradient-to:#0d2625 var(--tw-gradient-to-position)}.to-primary-950\/50{--tw-gradient-to:#0d262580 var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:#0000 var(--tw-gradient-to-position)}.to-90\%{--tw-gradient-to-position:90%}.bg-\[length\:100px_auto\]{background-size:100px auto}.bg-cover{background-size:cover}.bg-clip-padding{background-clip:padding-box}.bg-center{background-position:50%}.fill-primary-400{fill:#4eb3a1}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.object-bottom{-o-object-position:bottom;object-position:bottom}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-10{padding:2.5rem}.p-16{padding:4rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-40{padding-left:10rem;padding-right:10rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-7{padding-left:1.75rem;padding-right:1.75rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-12{padding-bottom:3rem}.pb-16{padding-bottom:4rem}.pb-3{padding-bottom:.75rem}.pb-3\.5{padding-bottom:.875rem}.pb-32{padding-bottom:8rem}.pb-8{padding-bottom:2rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-3{padding-right:.75rem}.pr-7{padding-right:1.75rem}.pr-\[10px\]{padding-right:10px}.pt-12{padding-top:3rem}.pt-24{padding-top:6rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-start{text-align:start}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-7xl{font-size:4.5rem;line-height:1}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-\[450\]{font-weight:450}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-primary-300{--tw-text-opacity:1;color:rgb(122 206 189/var(--tw-text-opacity))}.text-primary-400{--tw-text-opacity:1;color:rgb(78 179 161/var(--tw-text-opacity))}.text-primary-50{--tw-text-opacity:1;color:rgb(243 250 249/var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity:1;color:rgb(54 156 140/var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgb(40 121 110/var(--tw-text-opacity))}.text-primary-800{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgb(31 66 62/var(--tw-text-opacity))}.text-primary-950{--tw-text-opacity:1;color:rgb(13 38 37/var(--tw-text-opacity))}.text-secondary-300{--tw-text-opacity:1;color:rgb(255 170 157/var(--tw-text-opacity))}.text-secondary-50{--tw-text-opacity:1;color:rgb(255 243 241/var(--tw-text-opacity))}.text-secondary-500{--tw-text-opacity:1;color:rgb(255 78 51/var(--tw-text-opacity))}.text-secondary-700{--tw-text-opacity:1;color:rgb(200 38 13/var(--tw-text-opacity))}.accent-primary-500{accent-color:#369c8c}.opacity-0{opacity:0}.opacity-50{opacity:.5}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-gray-800\/5{--tw-shadow-color:#1f29370d;--tw-shadow:var(--tw-shadow-colored)}.outline-0{outline-width:0}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-gray-800\/\[\.075\]{--tw-ring-color:rgba(31,41,55,.075)}.ring-primary-100{--tw-ring-opacity:1;--tw-ring-color:rgb(213 242 235/var(--tw-ring-opacity))}.ring-primary-200{--tw-ring-opacity:1;--tw-ring-color:rgb(171 228 214/var(--tw-ring-opacity))}.blur{--tw-blur:blur(8px)}.blur,.blur-3xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-3xl{--tw-blur:blur(64px)}.brightness-0{--tw-brightness:brightness(0)}.brightness-0,.drop-shadow{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f)}.drop-shadow-sm{--tw-drop-shadow:drop-shadow(0 1px 1px #0000000d)}.drop-shadow-sm,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.backdrop-blur-sm,.backdrop-blur-xl{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.\[-webkit-mask-image\:linear-gradient\(to_bottom\2c rgba\(255\2c 255\2c 255\2c 1\)_75\%\2c rgba\(255\2c 255\2c 255\2c 0\)\)\]{-webkit-mask-image:linear-gradient(180deg,#fff 75%,#fff0)}.\[-webkit-mask-image\:linear-gradient\(to_top\2c rgba\(255\2c 255\2c 255\2c 1\)_75\%\2c rgba\(255\2c 255\2c 255\2c 0\)\)\]{-webkit-mask-image:linear-gradient(0deg,#fff 75%,#fff0)}.hover\:isolate:hover{isolation:isolate}.hover\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.hover\:border-transparent:hover{border-color:#0000}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-primary-100:hover{--tw-bg-opacity:1;background-color:rgb(213 242 235/var(--tw-bg-opacity))}.hover\:bg-primary-700:hover{--tw-bg-opacity:1;background-color:rgb(35 98 90/var(--tw-bg-opacity))}.hover\:bg-primary-800:hover{--tw-bg-opacity:1;background-color:rgb(32 79 74/var(--tw-bg-opacity))}.hover\:bg-secondary-100:hover{--tw-bg-opacity:1;background-color:rgb(255 227 223/var(--tw-bg-opacity))}.hover\:bg-secondary-700:hover{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.hover\:bg-opacity-50:hover{--tw-bg-opacity:0.5}.hover\:font-bold:hover{font-weight:700}.hover\:text-gray-50:hover{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.hover\:text-primary-400:hover{--tw-text-opacity:1;color:rgb(78 179 161/var(--tw-text-opacity))}.hover\:text-primary-50:hover{--tw-text-opacity:1;color:rgb(243 250 249/var(--tw-text-opacity))}.hover\:text-primary-500:hover{--tw-text-opacity:1;color:rgb(54 156 140/var(--tw-text-opacity))}.hover\:text-primary-700:hover{--tw-text-opacity:1;color:rgb(35 98 90/var(--tw-text-opacity))}.hover\:text-primary-800:hover{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.focus\:isolate:focus{isolation:isolate}.focus\:border-primary-300:focus{--tw-border-opacity:1;border-color:rgb(122 206 189/var(--tw-border-opacity))}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgb(54 156 140/var(--tw-border-opacity))}.focus\:border-transparent:focus{border-color:#0000}.focus\:bg-opacity-50:focus{--tw-bg-opacity:0.5}.focus\:text-primary-800:focus{--tw-text-opacity:1;color:rgb(32 79 74/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-\[1px\]:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-\[1px\]:focus,.focus\:ring-\[3px\]:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-\[3px\]:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-primary-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(171 228 214/var(--tw-ring-opacity))}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group\/btn:hover .group-hover\/btn\:w-2{width:.5rem}.group\/btn:hover .group-hover\/btn\:w-2\.5{width:.625rem}.group:hover .group-hover\:scale-100{--tw-scale-x:1;--tw-scale-y:1}.group:hover .group-hover\:scale-100,.group:hover .group-hover\:scale-x-100{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-x-100{--tw-scale-x:1}.group:hover .group-hover\:opacity-100,.group\/btn:hover .group-hover\/btn\:opacity-100{opacity:1}@media (min-width:640px){.sm\:left-14{left:3.5rem}.sm\:w-auto{width:auto}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pb-40{padding-bottom:10rem}.sm\:pl-20{padding-left:5rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-5xl{font-size:3rem;line-height:1}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:block{display:block}.md\:h-screen{height:100vh}.md\:w-16{width:4rem}.md\:w-20{width:5rem}.md\:w-24{width:6rem}.md\:max-w-2xl{max-width:42rem}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:gap-16{gap:4rem}.md\:px-14{padding-left:3.5rem;padding-right:3.5rem}.md\:py-10{padding-top:2.5rem;padding-bottom:2.5rem}.md\:pb-52{padding-bottom:13rem}.md\:text-3xl{font-size:1.875rem;line-height:2.25rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1024px){.lg\:left-20{left:5rem}.lg\:order-last{order:9999}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:col-span-4{grid-column:span 4/span 4}.lg\:-my-11{margin-top:-2.75rem;margin-bottom:-2.75rem}.lg\:-mr-9{margin-right:-2.25rem}.lg\:mb-0{margin-bottom:0}.lg\:mb-16{margin-bottom:4rem}.lg\:mt-0{margin-top:0}.lg\:block{display:block}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:max-w-4xl{max-width:56rem}.lg\:max-w-7xl{max-width:80rem}.lg\:max-w-max{max-width:-moz-max-content;max-width:max-content}.lg\:max-w-xl{max-width:36rem}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:gap-5{gap:1.25rem}.lg\:p-10{padding:2.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:py-12{padding-top:3rem;padding-bottom:3rem}.lg\:py-24{padding-top:6rem;padding-bottom:6rem}.lg\:pb-20{padding-bottom:5rem}.lg\:pb-60{padding-bottom:15rem}.lg\:pl-28{padding-left:7rem}.lg\:pt-20{padding-top:5rem}.lg\:pt-40{padding-top:10rem}.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}.lg\:text-5xl{font-size:3rem;line-height:1}.lg\:text-6xl{font-size:3.75rem;line-height:1}.lg\:text-lg{font-size:1.125rem;line-height:1.75rem}}@media (min-width:1280px){.xl\:col-span-3{grid-column:span 3/span 3}.xl\:mx-auto{margin-left:auto;margin-right:auto}.xl\:flex{display:flex}.xl\:hidden{display:none}.xl\:w-auto{width:auto}.xl\:w-full{width:100%}.xl\:max-w-lg{max-width:32rem}.xl\:max-w-xl{max-width:36rem}.xl\:gap-16{gap:4rem}.xl\:gap-x-16{-moz-column-gap:4rem;column-gap:4rem}.xl\:px-20{padding-left:5rem;padding-right:5rem}.xl\:py-20{padding-top:5rem;padding-bottom:5rem}.xl\:py-32{padding-top:8rem}.xl\:pb-32,.xl\:py-32{padding-bottom:8rem}.xl\:pb-\[16\.5rem\]{padding-bottom:16.5rem}.xl\:text-xl{font-size:1.25rem;line-height:1.75rem}}
diff --git a/update_policy.ml b/update_policy.ml
index a836d202..5b882ce6 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -314,7 +314,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
                               ~a:
                                 [
                                   a_input_type `Checkbox;
-                                  a_class [ "text-primary-500 bg-primary-500" ];
+                                  a_class [ "accent-primary-500 mr-2" ];
                                   Unsafe.string_attrib ":value" "option";
                                   Unsafe.string_attrib "x-on:change"
                                     "updateSelection($event, option)";
@@ -344,7 +344,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
           [
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
-              [ txt "Bridges" ];
+              [ txt "Network interfaces" ];
             div
               ~a:
                 [
@@ -435,7 +435,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
                               ~a:
                                 [
                                   a_input_type `Checkbox;
-                                  a_class [ "text-primary-500 bg-primary-500" ];
+                                  a_class [ "accent-primary-500 mr-2" ];
                                   Unsafe.string_attrib ":value" "option";
                                   Unsafe.string_attrib "x-on:change"
                                     "updateSelection($event, option)";

From a639bc933f88964e306e8ad34dec4daf9223f3be Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 12:00:59 +0200
Subject: [PATCH 12/64] fix minor bugs

---
 assets/main.js   |  8 ++++++--
 unikernel.ml     | 14 +++++++++-----
 update_policy.ml | 13 +++++++------
 3 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/assets/main.js b/assets/main.js
index d479708e..ac2ecfd0 100644
--- a/assets/main.js
+++ b/assets/main.js
@@ -281,8 +281,9 @@ async function updatePolicy() {
 	const bridges = document.getElementById("selectedBridges").value;
 	const formAlert = document.getElementById("form-alert");
 	const user_id = document.getElementById("user_id").innerText;
-	console.log(vm_count, mem_size, storage_size, cpuids, bridges);
+	const policyButton = document.getElementById("set-policy-btn");
 	try {
+		buttonLoading(policyButton, true, "Processing...")
 		const response = await fetch("/api/admin/u/policy/update", {
 			method: 'POST',
 			headers: {
@@ -305,16 +306,19 @@ async function updatePolicy() {
 			formAlert.textContent = "Succesfully updated";
 			postAlert("bg-primary-300", data.data);
 			setTimeout(function () {
-				window.location.reload();
+				window.history.back();
 			}, 2000);
+			buttonLoading(policyButton, false, "Set Policy")
 		} else {
 			formAlert.classList.remove("hidden", "text-primary-500");
 			formAlert.classList.add("text-secondary-500");
 			formAlert.textContent = data.data
+			buttonLoading(policyButton, false, "Set Policy")
 		}
 	} catch (error) {
 		formAlert.classList.remove("hidden", "text-primary-500");
 		formAlert.classList.add("text-secondary-500");
 		formAlert.textContent = error
+		buttonLoading(policyButton, false, "Set Policy")
 	}
 }
diff --git a/unikernel.ml b/unikernel.ml
index ffcb1bd4..7058196d 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -855,12 +855,16 @@ struct
                         memory;
                         block = Some block;
                         cpuids =
-                          Vmm_core.IS.of_list
-                            (List.map int_of_string
-                               (String.split_on_char ',' cpuids));
+                          (if cpuids = "" then Vmm_core.IS.empty
+                           else
+                             Vmm_core.IS.of_list
+                               (List.map int_of_string
+                                  (String.split_on_char ',' cpuids)));
                         bridges =
-                          Vmm_core.String_set.of_list
-                            (String.split_on_char ',' bridges);
+                          (if bridges = "" then Vmm_core.String_set.empty
+                           else
+                             Vmm_core.String_set.of_list
+                               (String.split_on_char ',' bridges));
                       }
                     in
                     (Albatross.query albatross ~domain:u.name
diff --git a/update_policy.ml b/update_policy.ml
index 5b882ce6..eb70a911 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -9,7 +9,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
         p ~a:[ a_id "form-alert"; a_class [ "my-4" ] ] [];
         p ~a:[ a_id "user_id"; a_class [ "hidden" ] ] [ txt user.uuid ];
         div
-          ~a:[ a_class [ "my-4" ] ]
+          ~a:[ a_class [ "my-3" ] ]
           [
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
@@ -75,7 +75,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
           ];
         hr ();
         div
-          ~a:[ a_class [ "my-4" ] ]
+          ~a:[ a_class [ "my-3" ] ]
           [
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
@@ -144,7 +144,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
           ];
         hr ();
         div
-          ~a:[ a_class [ "my-4" ] ]
+          ~a:[ a_class [ "my-3" ] ]
           [
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
@@ -217,7 +217,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
               ];
           ];
         div
-          ~a:[ a_class [ "my-4" ] ]
+          ~a:[ a_class [ "my-3" ] ]
           [
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
@@ -340,7 +340,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
               ];
           ];
         div
-          ~a:[ a_class [ "my-4" ] ]
+          ~a:[ a_class [ "my-3" ] ]
           [
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
@@ -461,12 +461,13 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
           ];
         hr ();
         div
-          ~a:[ a_class [ "my-4" ] ]
+          ~a:[ a_class [ "my-3" ] ]
           [
             button
               ~a:
                 [
                   a_onclick "updatePolicy()";
+                  a_id "set-policy-btn";
                   a_class [ "bg-primary-500 py-1 px-2 text-primary-50 rounded" ];
                 ]
               [ txt "Set Policy" ];

From 8da5473b340b543509076cc52546e805fb81108f Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 12:08:27 +0200
Subject: [PATCH 13/64] empty arrays instead of array of empty string

---
 update_policy.ml | 60 +++++++++++++++++++++++++++++-------------------
 1 file changed, 36 insertions(+), 24 deletions(-)

diff --git a/update_policy.ml b/update_policy.ml
index eb70a911..b44d4185 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -224,19 +224,25 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
               [ txt "CPU IDs" ];
             div
               ~a:
-                [
-                  Unsafe.string_attrib "x-data"
-                    ("multiselect(" ^ "[\""
-                    ^ String.concat "\", \""
-                        (List.map string_of_int
-                           (Vmm_core.IS.elements user_policy.cpuids))
-                    ^ "\"]" ^ "," ^ "[\""
-                    ^ String.concat "\", \""
-                        (List.map string_of_int
-                           (Vmm_core.IS.elements root_policy.cpuids))
-                    ^ "\"]" ^ ")");
-                  a_class [ "multiselect border my-4 p-4 rounded" ];
-                ]
+                (let cpuid_to_array_string lst =
+                   if lst = [] then "[]"
+                   else
+                     "[\""
+                     ^ String.concat "\", \"" (List.map string_of_int lst)
+                     ^ "\"]"
+                 in
+
+                 [
+                   Unsafe.string_attrib "x-data"
+                     ("multiselect("
+                     ^ cpuid_to_array_string
+                         (Vmm_core.IS.elements user_policy.cpuids)
+                     ^ ", "
+                     ^ cpuid_to_array_string
+                         (Vmm_core.IS.elements root_policy.cpuids)
+                     ^ ")");
+                   a_class [ "multiselect border my-4 p-4 rounded" ];
+                 ])
               [
                 div
                   ~a:[ a_class [ "selected-items" ] ]
@@ -347,17 +353,23 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
               [ txt "Network interfaces" ];
             div
               ~a:
-                [
-                  Unsafe.string_attrib "x-data"
-                    ("multiselect(" ^ "[\""
-                    ^ String.concat "\", \""
-                        (Vmm_core.String_set.elements user_policy.bridges)
-                    ^ "\"]" ^ "," ^ "[\""
-                    ^ String.concat "\", \""
-                        (Vmm_core.String_set.elements root_policy.bridges)
-                    ^ "\"]" ^ ")");
-                  a_class [ "multiselect border my-4 p-4 rounded" ];
-                ]
+                (let bridges_to_array_string set =
+                   if Vmm_core.String_set.is_empty set then "[]"
+                   else
+                     "[\""
+                     ^ String.concat "\", \"" (Vmm_core.String_set.elements set)
+                     ^ "\"]"
+                 in
+
+                 [
+                   Unsafe.string_attrib "x-data"
+                     ("multiselect("
+                     ^ bridges_to_array_string user_policy.bridges
+                     ^ ", "
+                     ^ bridges_to_array_string root_policy.bridges
+                     ^ ")");
+                   a_class [ "multiselect border my-4 p-4 rounded" ];
+                 ])
               [
                 div
                   ~a:[ a_class [ "selected-items" ] ]

From b9fa898421f95809571f338a0a0c80141d51a825 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Tue, 1 Oct 2024 12:15:48 +0200
Subject: [PATCH 14/64] Update albatross.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross.ml | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index f790c08e..18864c8a 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -70,10 +70,7 @@ struct
         vms = root_policy.vms - vms_used;
         cpuids = root_policy.cpuids;
         memory = root_policy.memory - memory_used;
-        block =
-          (match root_policy.block with
-          | Some b -> if b > 0 then Some (b - storage_used) else None
-          | None -> None);
+        block = Option.map (fun b -> b - storage_used) root_policy.block;
         bridges = root_policy.bridges;
       }
 

From cb4fc27a3f2d1a90983c02535f9621bd5a2101f4 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 12:16:08 +0200
Subject: [PATCH 15/64] query albatross properly

---
 assets/main.js |  2 +-
 unikernel.ml   | 39 ++++++++++++++++++++-------------------
 2 files changed, 21 insertions(+), 20 deletions(-)

diff --git a/assets/main.js b/assets/main.js
index ac2ecfd0..4c09f887 100644
--- a/assets/main.js
+++ b/assets/main.js
@@ -304,7 +304,7 @@ async function updatePolicy() {
 			formAlert.classList.remove("hidden", "text-secondary-500");
 			formAlert.classList.add("text-primary-500");
 			formAlert.textContent = "Succesfully updated";
-			postAlert("bg-primary-300", data.data);
+			postAlert("bg-primary-300", "Policy updated succesfully");
 			setTimeout(function () {
 				window.history.back();
 			}, 2000);
diff --git a/unikernel.ml b/unikernel.ml
index 7058196d..ec6dd729 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -848,7 +848,7 @@ struct
                   User_model.create_user_uuid_map (snd store).Storage.users
                 in
                 match User_model.find_user_by_key user_uuid users with
-                | Some u ->
+                | Some u -> (
                     let policy_data =
                       {
                         Vmm_core.Policy.vms;
@@ -867,24 +867,25 @@ struct
                                (String.split_on_char ',' bridges));
                       }
                     in
-                    (Albatross.query albatross ~domain:u.name
-                       (`Policy_cmd (`Policy_add policy_data))
-                     >|= function
-                     | Error msg ->
-                         Logs.err (fun m ->
-                             m "error while communicating with albatross: %s"
-                               msg);
-                         []
-                     | Ok (_hdr, `Success (`Policies policies)) -> policies
-                     | Ok reply ->
-                         Logs.err (fun m ->
-                             m "expected a policy info reply, received %a"
-                               (Vmm_commands.pp_wire ~verbose:false)
-                               reply);
-                         [])
-                    >>= fun _policies ->
-                    http_response reqd ~title:"Success"
-                      ~data:"Policy updated succesfully" `OK
+                    Albatross.query albatross ~domain:u.name
+                      (`Policy_cmd (`Policy_add policy_data))
+                    >>= function
+                    | Error err ->
+                        Logs.warn (fun m ->
+                            m "Error querying albatross: %s" err);
+                        http_response reqd ~title:"Error"
+                          ~data:("Error while querying Albatross: " ^ err)
+                          `Internal_server_error
+                    | Ok (_hdr, res) -> (
+                        match Albatross_json.res res with
+                        | Ok res ->
+                            http_response reqd ~title:"Success"
+                              ~data:(Yojson.Safe.to_string res)
+                              `OK
+                        | Error (`String res) ->
+                            http_response reqd ~title:"Error"
+                              ~data:(Yojson.Safe.to_string (`String res))
+                              `Internal_server_error))
                 | None ->
                     Logs.warn (fun m ->
                         m "Failed to find user with uuid: %s" user_uuid);

From 20a9c1b0ad69c57341b8f57a3dfe0221659090fb Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Tue, 1 Oct 2024 12:16:58 +0200
Subject: [PATCH 16/64] Update albatross.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross.ml b/albatross.ml
index 18864c8a..a716e1cd 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -61,7 +61,7 @@ struct
           if name <> Vmm_core.Name.root then
             ( total_vms + policy.Vmm_core.Policy.vms,
               total_memory + policy.memory,
-              total_block + match policy.block with Some b -> b | None -> 0 )
+              total_block + Option.value ~default:0 policy.block)
           else (total_vms, total_memory, total_block))
         (0, 0, 0) policies
     in

From 46a29fc5e99b39311aa68c1b573ccde13b7a7157 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Tue, 1 Oct 2024 12:17:07 +0200
Subject: [PATCH 17/64] Update albatross.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross.ml b/albatross.ml
index a716e1cd..b8b00adb 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -58,7 +58,7 @@ struct
     let vms_used, memory_used, storage_used =
       List.fold_left
         (fun (total_vms, total_memory, total_block) (name, policy) ->
-          if name <> Vmm_core.Name.root then
+          if not Vmm_core.Name.(equal name root) then
             ( total_vms + policy.Vmm_core.Policy.vms,
               total_memory + policy.memory,
               total_block + Option.value ~default:0 policy.block)

From a0434c084037acda2423a2ddeb8d58632f10cd57 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Tue, 1 Oct 2024 12:17:18 +0200
Subject: [PATCH 18/64] Update albatross.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross.ml b/albatross.ml
index b8b00adb..497a1019 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -14,7 +14,7 @@ struct
     Vmm_core.Policy.
       {
         vms = 0;
-        cpuids = Vmm_core.IS.of_list [];
+        cpuids = Vmm_core.IS.empty;
         memory = 0;
         block = Some 0;
         bridges = Vmm_core.String_set.of_list [];

From 3addb8201f7b4dd6d1d3636253d98a89ce29597c Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Tue, 1 Oct 2024 12:17:25 +0200
Subject: [PATCH 19/64] Update albatross.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross.ml b/albatross.ml
index 497a1019..08f461b9 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -17,7 +17,7 @@ struct
         cpuids = Vmm_core.IS.empty;
         memory = 0;
         block = Some 0;
-        bridges = Vmm_core.String_set.of_list [];
+        bridges = Vmm_core.String_set.empty;
       }
 
   let policy ?domain t =

From 5310a1c274714f5ed64f9b46439204065672d69f Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Tue, 1 Oct 2024 12:17:33 +0200
Subject: [PATCH 20/64] Update albatross.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross.ml b/albatross.ml
index 08f461b9..b56404b8 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -16,7 +16,7 @@ struct
         vms = 0;
         cpuids = Vmm_core.IS.empty;
         memory = 0;
-        block = Some 0;
+        block = None;
         bridges = Vmm_core.String_set.empty;
       }
 

From 3c54ec4c71af1e37d0bb6698b1e3856979b7c07b Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Tue, 1 Oct 2024 12:18:24 +0200
Subject: [PATCH 21/64] Update albatross.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross.ml b/albatross.ml
index b56404b8..fe25e2a4 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -48,7 +48,7 @@ struct
     in
     Ok (Vmm_trie.fold path t.policies (fun name p acc -> (name, p) :: acc) [])
 
-  let policy_resource_used () t =
+  let policy_resource_used t =
     let root_policy =
       match policy t with
       | Ok p -> ( match p with Some p -> p | None -> empty_policy)

From da92b25e1bae62f4cb6041cf1486445ccced5a59 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 12:20:28 +0200
Subject: [PATCH 22/64] add comments

---
 header_layout.ml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/header_layout.ml b/header_layout.ml
index b33d8d12..28ba984e 100644
--- a/header_layout.ml
+++ b/header_layout.ml
@@ -20,8 +20,10 @@ let header ?(page_title = "Mollymawk") ~icon () =
         script ~a:[ a_src "https://kit.fontawesome.com/d1697f2fa9.js" ] (txt "");
         link ~rel:[ `Stylesheet ]
           ~href:"https://unpkg.com/aos@2.3.1/dist/aos.css" ();
-        script ~a:[ a_src "https://unpkg.com/aos@2.3.1/dist/aos.js" ] (txt "");
+        (*aos is animate-on-scroll, adds bouncy effects to html elements*)
+          script ~a:[ a_src "https://unpkg.com/aos@2.3.1/dist/aos.js" ] (txt "");
         link ~rel:[ `Icon ] ~href:icon ();
+        (*https://alpinejs.dev/ is a lightweight js library and we use it for multiselect form elements*)
         script
           ~a:
             [

From c7cf1fe0cc7ebcc8075b1a0a475eb8724f97eb3e Mon Sep 17 00:00:00 2001
From: Auto-OCamlformat <auto@ocaml.format>
Date: Tue, 1 Oct 2024 10:23:27 +0000
Subject: [PATCH 23/64] formatted code

---
 albatross.ml     | 2 +-
 header_layout.ml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index fe25e2a4..d7f73299 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -61,7 +61,7 @@ struct
           if not Vmm_core.Name.(equal name root) then
             ( total_vms + policy.Vmm_core.Policy.vms,
               total_memory + policy.memory,
-              total_block + Option.value ~default:0 policy.block)
+              total_block + Option.value ~default:0 policy.block )
           else (total_vms, total_memory, total_block))
         (0, 0, 0) policies
     in
diff --git a/header_layout.ml b/header_layout.ml
index 28ba984e..602f2ff5 100644
--- a/header_layout.ml
+++ b/header_layout.ml
@@ -21,7 +21,7 @@ let header ?(page_title = "Mollymawk") ~icon () =
         link ~rel:[ `Stylesheet ]
           ~href:"https://unpkg.com/aos@2.3.1/dist/aos.css" ();
         (*aos is animate-on-scroll, adds bouncy effects to html elements*)
-          script ~a:[ a_src "https://unpkg.com/aos@2.3.1/dist/aos.js" ] (txt "");
+        script ~a:[ a_src "https://unpkg.com/aos@2.3.1/dist/aos.js" ] (txt "");
         link ~rel:[ `Icon ] ~href:icon ();
         (*https://alpinejs.dev/ is a lightweight js library and we use it for multiselect form elements*)
         script

From 1c8b252f31aee93b862ffb82d4c2894554bf5ddd Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 14:06:07 +0200
Subject: [PATCH 24/64] review changes

---
 albatross.ml     | 17 ++---------------
 header_layout.ml |  2 +-
 2 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index fe25e2a4..62e07843 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -61,7 +61,7 @@ struct
           if not Vmm_core.Name.(equal name root) then
             ( total_vms + policy.Vmm_core.Policy.vms,
               total_memory + policy.memory,
-              total_block + Option.value ~default:0 policy.block)
+              total_block + Option.value ~default:0 policy.block )
           else (total_vms, total_memory, total_block))
         (0, 0, 0) policies
     in
@@ -147,20 +147,7 @@ struct
       | None -> Ok (t.key, t.cert, [])
       | Some domain ->
           let* policy = policy ~domain t in
-          let policy =
-            Option.value
-              ~default:
-                Vmm_core.(
-                  Policy.
-                    {
-                      vms = 0;
-                      cpuids = IS.empty;
-                      memory = 0;
-                      block = None;
-                      bridges = String_set.empty;
-                    })
-              policy
-          in
+          let policy = Option.value ~default:empty_policy policy in
           let cmd = `Policy_cmd (`Policy_add policy) in
           let* key, cert =
             key_cert ~is_ca:true ~cmd t.key domain
diff --git a/header_layout.ml b/header_layout.ml
index 28ba984e..602f2ff5 100644
--- a/header_layout.ml
+++ b/header_layout.ml
@@ -21,7 +21,7 @@ let header ?(page_title = "Mollymawk") ~icon () =
         link ~rel:[ `Stylesheet ]
           ~href:"https://unpkg.com/aos@2.3.1/dist/aos.css" ();
         (*aos is animate-on-scroll, adds bouncy effects to html elements*)
-          script ~a:[ a_src "https://unpkg.com/aos@2.3.1/dist/aos.js" ] (txt "");
+        script ~a:[ a_src "https://unpkg.com/aos@2.3.1/dist/aos.js" ] (txt "");
         link ~rel:[ `Icon ] ~href:icon ();
         (*https://alpinejs.dev/ is a lightweight js library and we use it for multiselect form elements*)
         script

From 93d7948ceff60c037e2868de28065b6577b939fa Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 15:18:07 +0200
Subject: [PATCH 25/64] move json to albatross_json

---
 albatross.ml      |  6 ++--
 albatross_json.ml | 39 ++++++++++++++++++++++
 unikernel.ml      | 82 +++++------------------------------------------
 3 files changed, 50 insertions(+), 77 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index 62e07843..64a4d5cb 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -48,7 +48,7 @@ struct
     in
     Ok (Vmm_trie.fold path t.policies (fun name p acc -> (name, p) :: acc) [])
 
-  let policy_resource_used t =
+  let policy_resource_avalaible t =
     let root_policy =
       match policy t with
       | Ok p -> ( match p with Some p -> p | None -> empty_policy)
@@ -57,8 +57,8 @@ struct
     let policies = match policies t with Ok p -> p | Error _err -> [] in
     let vms_used, memory_used, storage_used =
       List.fold_left
-        (fun (total_vms, total_memory, total_block) (name, policy) ->
-          if not Vmm_core.Name.(equal name root) then
+        (fun (total_vms, total_memory, total_block) (name_, policy) ->
+          if not Vmm_core.Name.(equal name_ root) then
             ( total_vms + policy.Vmm_core.Policy.vms,
               total_memory + policy.memory,
               total_block + Option.value ~default:0 policy.block )
diff --git a/albatross_json.ml b/albatross_json.ml
index 02e40f8c..d48f4a8f 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -173,6 +173,45 @@ let block_device_of_json js =
       | _, _, _ -> Error (`Msg "couldn't decode json"))
   | _ -> Error (`Msg "bad json, either string or assoc")
 
+let policy_of_json js =
+  match js with
+  | `Assoc xs -> (
+      match
+        Utils.Json.
+          ( get "vms" xs,
+            get "memory" xs,
+            get "block" xs,
+            get "cpuids" xs,
+            get "bridges" xs )
+      with
+      | ( Some (`Int vms),
+          Some (`Int memory),
+          Some (`Int block),
+          Some (`String cpuids),
+          Some (`String bridges) ) ->
+          Ok
+            {
+              Vmm_core.Policy.vms;
+              memory;
+              block = Some block;
+              cpuids =
+                (if cpuids = "" then Vmm_core.IS.empty
+                 else
+                   Vmm_core.IS.of_list
+                     (List.map int_of_string (String.split_on_char ',' cpuids)));
+              bridges =
+                (if bridges = "" then Vmm_core.String_set.empty
+                 else
+                   Vmm_core.String_set.of_list
+                     (String.split_on_char ',' bridges));
+            }
+      | _ ->
+          Error
+            (`Msg
+              (Fmt.str "policy: unexpected types, got %s"
+                 (Yojson.Basic.to_string (`Assoc xs)))))
+  | _ -> Error (`Msg "policy: Expected a dictionary")
+
 let config_of_json str =
   let ( let* ) = Result.bind in
   let* json =
diff --git a/unikernel.ml b/unikernel.ml
index ec6dd729..79bd99c5 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -789,7 +789,7 @@ struct
               match p with Some p -> p | None -> Albatross.empty_policy)
           | Error _ -> Albatross.empty_policy
         in
-        let policy_avalaible = Albatross.policy_resource_used () albatross in
+        let policy_avalaible = Albatross.policy_resource_avalaible albatross in
         Lwt.return
           (reply reqd ~content_type:"text/html"
              (Dashboard.dashboard_layout user
@@ -827,79 +827,13 @@ struct
         http_response reqd ~title:"Error" ~data:(String.escaped err)
           `Bad_request
     | Ok json -> (
-        match json with
-        | `Assoc xs -> (
-            match
-              Utils.Json.
-                ( get "vms" xs,
-                  get "memory" xs,
-                  get "block" xs,
-                  get "cpuids" xs,
-                  get "bridges" xs,
-                  get "user_uuid" xs )
-            with
-            | ( Some (`Int vms),
-                Some (`Int memory),
-                Some (`Int block),
-                Some (`String cpuids),
-                Some (`String bridges),
-                Some (`String user_uuid) ) -> (
-                let users =
-                  User_model.create_user_uuid_map (snd store).Storage.users
-                in
-                match User_model.find_user_by_key user_uuid users with
-                | Some u -> (
-                    let policy_data =
-                      {
-                        Vmm_core.Policy.vms;
-                        memory;
-                        block = Some block;
-                        cpuids =
-                          (if cpuids = "" then Vmm_core.IS.empty
-                           else
-                             Vmm_core.IS.of_list
-                               (List.map int_of_string
-                                  (String.split_on_char ',' cpuids)));
-                        bridges =
-                          (if bridges = "" then Vmm_core.String_set.empty
-                           else
-                             Vmm_core.String_set.of_list
-                               (String.split_on_char ',' bridges));
-                      }
-                    in
-                    Albatross.query albatross ~domain:u.name
-                      (`Policy_cmd (`Policy_add policy_data))
-                    >>= function
-                    | Error err ->
-                        Logs.warn (fun m ->
-                            m "Error querying albatross: %s" err);
-                        http_response reqd ~title:"Error"
-                          ~data:("Error while querying Albatross: " ^ err)
-                          `Internal_server_error
-                    | Ok (_hdr, res) -> (
-                        match Albatross_json.res res with
-                        | Ok res ->
-                            http_response reqd ~title:"Success"
-                              ~data:(Yojson.Safe.to_string res)
-                              `OK
-                        | Error (`String res) ->
-                            http_response reqd ~title:"Error"
-                              ~data:(Yojson.Safe.to_string (`String res))
-                              `Internal_server_error))
-                | None ->
-                    Logs.warn (fun m ->
-                        m "Failed to find user with uuid: %s" user_uuid);
-                    http_response reqd ~title:"Error" ~data:"User not found"
-                      `Not_found)
-            | _ ->
-                http_response reqd ~title:"Error"
-                  ~data:
-                    (Fmt.str "policy: unexpected types, got %s"
-                       (Yojson.Basic.to_string (`Assoc xs)))
-                  `Bad_request)
-        | _ ->
-            http_response reqd ~title:"Error" ~data:"Expected a dictionary"
-              `Bad_request)
+        let user_uuid =
+          Yojson.Basic.(to_string (json |> Util.member "user_uuid"))
+        in
+        match Albatross_json.policy_of_json json with
+        | Ok policy -> http_response reqd ~title:"Error" ~data:"" `Bad_request
+        | Error (`Msg err) ->
+            http_response reqd ~title:"Error" ~data:err `Bad_request)
 
   let request_handler stack albatross js_file css_file imgs store
       (_ipaddr, _port) reqd =

From 1905f75748ec51903a682b710430b5488be1ba10 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 15:36:08 +0200
Subject: [PATCH 26/64] review changes

---
 albatross_json.ml | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index d48f4a8f..531724c0 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -188,23 +188,28 @@ let policy_of_json js =
           Some (`Int memory),
           Some (`Int block),
           Some (`String cpuids),
-          Some (`String bridges) ) ->
-          Ok
+          Some (`String bridges) ) -> (
+          let policy =
             {
               Vmm_core.Policy.vms;
               memory;
-              block = Some block;
+              block = (if block = 0 then None else Some block);
               cpuids =
                 (if cpuids = "" then Vmm_core.IS.empty
                  else
                    Vmm_core.IS.of_list
-                     (List.map int_of_string (String.split_on_char ',' cpuids)));
+                     (List.filter_map int_of_string_opt
+                        (String.split_on_char ',' cpuids)));
               bridges =
                 (if bridges = "" then Vmm_core.String_set.empty
                  else
                    Vmm_core.String_set.of_list
                      (String.split_on_char ',' bridges));
             }
+          in
+          match Vmm_core.Policy.usable policy with
+          | Ok p -> Ok p
+          | Error (`Msg err) -> Error (`Msg err))
       | _ ->
           Error
             (`Msg

From f24276616e4da61d53106b6b44b11424bbd87606 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 15:49:38 +0200
Subject: [PATCH 27/64] return policies

---
 albatross.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross.ml b/albatross.ml
index b2320a65..a9991a11 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -386,7 +386,7 @@ struct
         (* now we tell albatross about it, using a command for throwing it away *)
         (* note that the 'certs' / 'gen_cert' uses the policies for intermediate certificates *)
         query t ~domain (`Unikernel_cmd `Unikernel_info) >|= function
-        | Ok _ -> Ok ()
+        | Ok _ -> Ok (Vmm_trie.collect name t.policies)
         | Error msg ->
             Logs.warn (fun m -> m "error updating policies: %s" msg);
             t.policies <- old_policies;

From 3b129c5b7ba1fa18d30ba3fd40954ca9cb878cb5 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 15:49:51 +0200
Subject: [PATCH 28/64] check policy is usable

---
 albatross_json.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index 531724c0..26a142dc 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -208,7 +208,7 @@ let policy_of_json js =
             }
           in
           match Vmm_core.Policy.usable policy with
-          | Ok p -> Ok p
+          | Ok () -> Ok policy
           | Error (`Msg err) -> Error (`Msg err))
       | _ ->
           Error

From fcca1232041d7d0dba98c9dfb634bafcf18f5d4d Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 15:50:03 +0200
Subject: [PATCH 29/64] set policy

---
 unikernel.ml | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/unikernel.ml b/unikernel.ml
index 79bd99c5..47106168 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -830,10 +830,26 @@ struct
         let user_uuid =
           Yojson.Basic.(to_string (json |> Util.member "user_uuid"))
         in
-        match Albatross_json.policy_of_json json with
-        | Ok policy -> http_response reqd ~title:"Error" ~data:"" `Bad_request
-        | Error (`Msg err) ->
-            http_response reqd ~title:"Error" ~data:err `Bad_request)
+        let users = User_model.create_user_uuid_map (snd store).Storage.users in
+        match User_model.find_user_by_key user_uuid users with
+        | Some u -> (
+            match Albatross_json.policy_of_json json with
+            | Ok policy -> (
+                Albatross.set_policy albatross ~domain:u.name policy
+                >>= function
+                | Error err ->
+                    http_response reqd ~title:"Error" ~data:err
+                      `Internal_server_error
+                | Ok policies ->
+                    http_response reqd ~title:"Success"
+                      ~data:
+                        (Yojson.Basic.to_string
+                           (Albatross_json.policy_infos policies))
+                      `OK)
+            | Error (`Msg err) ->
+                http_response reqd ~title:"Error" ~data:err `Bad_request)
+        | None ->
+            http_response reqd ~title:"Error" ~data:"User not found" `Not_found)
 
   let request_handler stack albatross js_file css_file imgs store
       (_ipaddr, _port) reqd =

From c2e0e4a5cd53b3254561b9f5203e4ac3e423a2ca Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 15:54:21 +0200
Subject: [PATCH 30/64] add error logs

---
 albatross.ml | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index a9991a11..0cfb0872 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -51,10 +51,23 @@ struct
   let policy_resource_avalaible t =
     let root_policy =
       match policy t with
-      | Ok p -> ( match p with Some p -> p | None -> empty_policy)
-      | Error _ -> empty_policy
+      | Ok p -> (
+          match p with
+          | Some p -> p
+          | None ->
+              Logs.err (fun m -> m "policy error: empty root policy");
+              empty_policy)
+      | Error err ->
+          Logs.err (fun m -> m "policy error:  %s" err);
+          empty_policy
+    in
+    let policies =
+      match policies t with
+      | Ok p -> p
+      | Error err ->
+          Logs.err (fun m -> m "policy error:  %s" err);
+          []
     in
-    let policies = match policies t with Ok p -> p | Error _err -> [] in
     let vms_used, memory_used, storage_used =
       List.fold_left
         (fun (total_vms, total_memory, total_block) (name_, policy) ->

From 08193408194d95e5e41dd7bee3d9fdc3dc0400bc Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Tue, 1 Oct 2024 16:29:49 +0200
Subject: [PATCH 31/64] Update albatross_json.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross_json.ml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index 26a142dc..52b7de81 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -207,9 +207,9 @@ let policy_of_json js =
                      (String.split_on_char ',' bridges));
             }
           in
-          match Vmm_core.Policy.usable policy with
-          | Ok () -> Ok policy
-          | Error (`Msg err) -> Error (`Msg err))
+          let ( let* ) = Result.bind in
+          let* () = Vmm_core.Policy.usable policy in
+          Ok policy
       | _ ->
           Error
             (`Msg

From 1fcf02b59b7fbd3629153551fe36fc4f3e6461a5 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 19:22:03 +0200
Subject: [PATCH 32/64] check if sub policy is possible

---
 unikernel.ml | 40 +++++++++++++++++++++++++++++++---------
 1 file changed, 31 insertions(+), 9 deletions(-)

diff --git a/unikernel.ml b/unikernel.ml
index 47106168..e48095e3 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -823,7 +823,7 @@ struct
     in
     match json with
     | Error (`Msg err) ->
-        Logs.warn (fun m -> m "Failed to parse JSON: %s" err);
+        Logs.err (fun m -> m "Failed to parse JSON: %s" err);
         http_response reqd ~title:"Error" ~data:(String.escaped err)
           `Bad_request
     | Ok json -> (
@@ -835,17 +835,39 @@ struct
         | Some u -> (
             match Albatross_json.policy_of_json json with
             | Ok policy -> (
-                Albatross.set_policy albatross ~domain:u.name policy
-                >>= function
+                match Albatross.policy albatross with
                 | Error err ->
+                    Logs.err (fun m -> m "policy: %s" err);
                     http_response reqd ~title:"Error" ~data:err
                       `Internal_server_error
-                | Ok policies ->
-                    http_response reqd ~title:"Success"
-                      ~data:
-                        (Yojson.Basic.to_string
-                           (Albatross_json.policy_infos policies))
-                      `OK)
+                | Ok root_policy -> (
+                    match root_policy with
+                    | None ->
+                        Logs.err (fun m ->
+                            m "policy: root policy can't be null ");
+                        http_response reqd ~title:"Error"
+                          ~data:"root policy is null" `Internal_server_error
+                    | Some root_policy -> (
+                        match
+                          Vmm_core.Policy.is_smaller ~super:root_policy
+                            ~sub:policy
+                        with
+                        | Error (`Msg err) ->
+                            Logs.err (fun m -> m "policy: %s" err);
+                            http_response reqd ~title:"Error" ~data:err
+                              `Internal_server_error
+                        | Ok () -> (
+                            Albatross.set_policy albatross ~domain:u.name policy
+                            >>= function
+                            | Error err ->
+                                http_response reqd ~title:"Error" ~data:err
+                                  `Internal_server_error
+                            | Ok policies ->
+                                http_response reqd ~title:"Success"
+                                  ~data:
+                                    (Yojson.Basic.to_string
+                                       (Albatross_json.policy_infos policies))
+                                  `OK))))
             | Error (`Msg err) ->
                 http_response reqd ~title:"Error" ~data:err `Bad_request)
         | None ->

From 44dfc61b92821a395479c2e7f7c772c8a6c5f799 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 19:32:21 +0200
Subject: [PATCH 33/64] properly manage root policy errors

---
 albatross.ml      | 26 +++++++++++++++-----------
 albatross_json.ml |  2 +-
 unikernel.ml      | 38 +++++++++++++++++++++++++++-----------
 3 files changed, 43 insertions(+), 23 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index 0cfb0872..0a4d2ece 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -53,13 +53,13 @@ struct
       match policy t with
       | Ok p -> (
           match p with
-          | Some p -> p
+          | Some p -> Ok p
           | None ->
               Logs.err (fun m -> m "policy error: empty root policy");
-              empty_policy)
+              Error (`Msg "root policy is empty"))
       | Error err ->
           Logs.err (fun m -> m "policy error:  %s" err);
-          empty_policy
+          Error (`Msg "error getting root policy")
     in
     let policies =
       match policies t with
@@ -78,14 +78,18 @@ struct
           else (total_vms, total_memory, total_block))
         (0, 0, 0) policies
     in
-    Vmm_core.Policy.
-      {
-        vms = root_policy.vms - vms_used;
-        cpuids = root_policy.cpuids;
-        memory = root_policy.memory - memory_used;
-        block = Option.map (fun b -> b - storage_used) root_policy.block;
-        bridges = root_policy.bridges;
-      }
+    match root_policy with
+    | Ok root_policy ->
+        Ok
+          Vmm_core.Policy.
+            {
+              vms = root_policy.vms - vms_used;
+              cpuids = root_policy.cpuids;
+              memory = root_policy.memory - memory_used;
+              block = Option.map (fun b -> b - storage_used) root_policy.block;
+              bridges = root_policy.bridges;
+            }
+    | Error (`Msg err) -> Error err
 
   let key_ids exts pub issuer =
     let open X509 in
diff --git a/albatross_json.ml b/albatross_json.ml
index 52b7de81..724a9405 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -188,7 +188,7 @@ let policy_of_json js =
           Some (`Int memory),
           Some (`Int block),
           Some (`String cpuids),
-          Some (`String bridges) ) -> (
+          Some (`String bridges) ) ->
           let policy =
             {
               Vmm_core.Policy.vms;
diff --git a/unikernel.ml b/unikernel.ml
index e48095e3..a01694cf 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -782,23 +782,39 @@ struct
   let edit_policy albatross store uuid reqd (user : User_model.user) =
     let users = User_model.create_user_uuid_map (snd store).Storage.users in
     match User_model.find_user_by_key uuid users with
-    | Some u ->
+    | Some u -> (
         let user_policy =
           match Albatross.policy albatross ~domain:u.name with
           | Ok p -> (
               match p with Some p -> p | None -> Albatross.empty_policy)
           | Error _ -> Albatross.empty_policy
         in
-        let policy_avalaible = Albatross.policy_resource_avalaible albatross in
-        Lwt.return
-          (reply reqd ~content_type:"text/html"
-             (Dashboard.dashboard_layout user
-                ~page_title:(String.capitalize_ascii u.name ^ " | Mollymawk")
-                ~content:
-                  (Update_policy.update_policy_layout u ~user_policy
-                     ~root_policy:policy_avalaible)
-                ~icon:"/images/robur.png" ())
-             `OK)
+        match Albatross.policy_resource_avalaible albatross with
+        | Ok root_policy ->
+            Lwt.return
+              (reply reqd ~content_type:"text/html"
+                 (Dashboard.dashboard_layout user
+                    ~page_title:(String.capitalize_ascii u.name ^ " | Mollymawk")
+                    ~content:
+                      (Update_policy.update_policy_layout u ~user_policy
+                         ~root_policy)
+                    ~icon:"/images/robur.png" ())
+                 `OK)
+        | Error err ->
+            let status =
+              {
+                Utils.Status.code = 500;
+                title = "Error";
+                data = "Policy error: " ^ err;
+                success = false;
+              }
+            in
+            Lwt.return
+              (reply reqd ~content_type:"text/html"
+                 (Guest_layout.guest_layout ~page_title:"500 | Mollymawk"
+                    ~content:(Error_page.error_layout status)
+                    ~icon:"/images/robur.png" ())
+                 `Not_found))
     | None ->
         let status =
           {

From 195f1194088bd7dc402221730d3d332d8a5a4d77 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Tue, 1 Oct 2024 19:37:10 +0200
Subject: [PATCH 34/64] log invalid cpuids

---
 albatross_json.ml | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index 724a9405..388681d7 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -195,11 +195,18 @@ let policy_of_json js =
               memory;
               block = (if block = 0 then None else Some block);
               cpuids =
-                (if cpuids = "" then Vmm_core.IS.empty
-                 else
-                   Vmm_core.IS.of_list
-                     (List.filter_map int_of_string_opt
-                        (String.split_on_char ',' cpuids)));
+                (let parsed_cpuids =
+                   List.filter_map
+                     (fun s ->
+                       match int_of_string_opt s with
+                       | Some i -> Some i
+                       | None ->
+                           Logs.err (fun m -> m "Invalid CPU id: %s" s);
+                           None)
+                     (String.split_on_char ',' cpuids)
+                 in
+                 if parsed_cpuids = [] then Vmm_core.IS.empty
+                 else Vmm_core.IS.of_list parsed_cpuids);
               bridges =
                 (if bridges = "" then Vmm_core.String_set.empty
                  else

From 9e0a3fa80b58c358524663918e0efaad9bc8b752 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 09:49:01 +0200
Subject: [PATCH 35/64] uuid bug fix

---
 unikernel.ml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/unikernel.ml b/unikernel.ml
index a01694cf..5ab11f89 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -847,7 +847,9 @@ struct
           Yojson.Basic.(to_string (json |> Util.member "user_uuid"))
         in
         let users = User_model.create_user_uuid_map (snd store).Storage.users in
-        match User_model.find_user_by_key user_uuid users with
+        match
+          User_model.find_user_by_key (Utils.Json.clean_string user_uuid) users
+        with
         | Some u -> (
             match Albatross_json.policy_of_json json with
             | Ok policy -> (

From c8d95c3bd43f66f02ab088eeea9878877c5f0ccb Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:00:00 +0200
Subject: [PATCH 36/64] Update albatross_json.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross_json.ml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index 388681d7..9d0f0a47 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -208,10 +208,8 @@ let policy_of_json js =
                  if parsed_cpuids = [] then Vmm_core.IS.empty
                  else Vmm_core.IS.of_list parsed_cpuids);
               bridges =
-                (if bridges = "" then Vmm_core.String_set.empty
-                 else
                    Vmm_core.String_set.of_list
-                     (String.split_on_char ',' bridges));
+                     (String.split_on_char ',' bridges);
             }
           in
           let ( let* ) = Result.bind in

From 11818f2d69c8be5062fbd263b13507f53db48229 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:00:13 +0200
Subject: [PATCH 37/64] Update albatross_json.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross_json.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index 9d0f0a47..27a1b939 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -201,7 +201,7 @@ let policy_of_json js =
                        match int_of_string_opt s with
                        | Some i -> Some i
                        | None ->
-                           Logs.err (fun m -> m "Invalid CPU id: %s" s);
+                           Logs.warn (fun m -> m "Ignoring invalid CPU id: %s" s);
                            None)
                      (String.split_on_char ',' cpuids)
                  in

From 197b077583a4258aa76dca27b08abdc1e0ca7667 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:00:22 +0200
Subject: [PATCH 38/64] Update albatross_json.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross_json.ml | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index 27a1b939..461236e6 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -205,8 +205,7 @@ let policy_of_json js =
                            None)
                      (String.split_on_char ',' cpuids)
                  in
-                 if parsed_cpuids = [] then Vmm_core.IS.empty
-                 else Vmm_core.IS.of_list parsed_cpuids);
+                 Vmm_core.IS.of_list parsed_cpuids;
               bridges =
                    Vmm_core.String_set.of_list
                      (String.split_on_char ',' bridges);

From 360a9ff0f6d4af60a4312155606f712d2f9aa2cd Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 10:04:30 +0200
Subject: [PATCH 39/64] review suggestions by @hannesm

---
 albatross.ml      | 2 +-
 albatross_json.ml | 8 ++++----
 unikernel.ml      | 4 ++--
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index 0a4d2ece..b578ee69 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -403,7 +403,7 @@ struct
         (* now we tell albatross about it, using a command for throwing it away *)
         (* note that the 'certs' / 'gen_cert' uses the policies for intermediate certificates *)
         query t ~domain (`Unikernel_cmd `Unikernel_info) >|= function
-        | Ok _ -> Ok (Vmm_trie.collect name t.policies)
+        | Ok _ -> Ok (name, policy)
         | Error msg ->
             Logs.warn (fun m -> m "error updating policies: %s" msg);
             t.policies <- old_policies;
diff --git a/albatross_json.ml b/albatross_json.ml
index 461236e6..bba6457f 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -201,14 +201,14 @@ let policy_of_json js =
                        match int_of_string_opt s with
                        | Some i -> Some i
                        | None ->
-                           Logs.warn (fun m -> m "Ignoring invalid CPU id: %s" s);
+                           Logs.warn (fun m ->
+                               m "Ignoring invalid CPU id: %s" s);
                            None)
                      (String.split_on_char ',' cpuids)
                  in
-                 Vmm_core.IS.of_list parsed_cpuids;
+                 Vmm_core.IS.of_list parsed_cpuids);
               bridges =
-                   Vmm_core.String_set.of_list
-                     (String.split_on_char ',' bridges);
+                Vmm_core.String_set.of_list (String.split_on_char ',' bridges);
             }
           in
           let ( let* ) = Result.bind in
diff --git a/unikernel.ml b/unikernel.ml
index 5ab11f89..2a7c141d 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -880,11 +880,11 @@ struct
                             | Error err ->
                                 http_response reqd ~title:"Error" ~data:err
                                   `Internal_server_error
-                            | Ok policies ->
+                            | Ok policy ->
                                 http_response reqd ~title:"Success"
                                   ~data:
                                     (Yojson.Basic.to_string
-                                       (Albatross_json.policy_infos policies))
+                                       (Albatross_json.policy_info policy))
                                   `OK))))
             | Error (`Msg err) ->
                 http_response reqd ~title:"Error" ~data:err `Bad_request)

From e522cfd08d75b1e20334d8aceb63a925a796828b Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:05:22 +0200
Subject: [PATCH 40/64] Update unikernel.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 unikernel.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/unikernel.ml b/unikernel.ml
index 2a7c141d..0b694247 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -855,7 +855,7 @@ struct
             | Ok policy -> (
                 match Albatross.policy albatross with
                 | Error err ->
-                    Logs.err (fun m -> m "policy: %s" err);
+                    Logs.err (fun m -> m "couldn't retrieve root policy: %s" err);
                     http_response reqd ~title:"Error" ~data:err
                       `Internal_server_error
                 | Ok root_policy -> (

From 0c49d80c83ac8382c7c741ea8e5d953449dff0a5 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:06:49 +0200
Subject: [PATCH 41/64] Update unikernel.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 unikernel.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/unikernel.ml b/unikernel.ml
index 0b694247..70a4caeb 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -858,7 +858,7 @@ struct
                     Logs.err (fun m -> m "couldn't retrieve root policy: %s" err);
                     http_response reqd ~title:"Error" ~data:err
                       `Internal_server_error
-                | Ok root_policy -> (
+                | Ok Some root_policy -> (
                     match root_policy with
                     | None ->
                         Logs.err (fun m ->

From b715074520094b223af056e1bbbcf60216ca3667 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:07:09 +0200
Subject: [PATCH 42/64] Update unikernel.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 unikernel.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/unikernel.ml b/unikernel.ml
index 70a4caeb..79554468 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -871,7 +871,7 @@ struct
                             ~sub:policy
                         with
                         | Error (`Msg err) ->
-                            Logs.err (fun m -> m "policy: %s" err);
+                            Logs.err (fun m -> m "policy %a is not smaller than root policy %a: %s" Vmm_core.Policy.pp policy Vmm_core.Policy.pp root_policy err);
                             http_response reqd ~title:"Error" ~data:err
                               `Internal_server_error
                         | Ok () -> (

From b51fcc900a0717bd9564137043a19831ebd65339 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:07:19 +0200
Subject: [PATCH 43/64] Update unikernel.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 unikernel.ml | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/unikernel.ml b/unikernel.ml
index 79554468..0759cbc8 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -860,12 +860,6 @@ struct
                       `Internal_server_error
                 | Ok Some root_policy -> (
                     match root_policy with
-                    | None ->
-                        Logs.err (fun m ->
-                            m "policy: root policy can't be null ");
-                        http_response reqd ~title:"Error"
-                          ~data:"root policy is null" `Internal_server_error
-                    | Some root_policy -> (
                         match
                           Vmm_core.Policy.is_smaller ~super:root_policy
                             ~sub:policy

From d11ef08c502cf3575faa4520e2d3333777a7c754 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:10:20 +0200
Subject: [PATCH 44/64] Update update_policy.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 update_policy.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/update_policy.ml b/update_policy.ml
index b44d4185..6cfceb86 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -13,7 +13,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
           [
             label
               ~a:[ a_class [ "block text-sm font-medium" ] ]
-              [ txt "Allowed VMs" ];
+              [ txt "Allowed unikernels" ];
             p
               [
                 txt

From 1f89d7d15c38f89b08dd3ac964e87e65f35c1b7d Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 11:31:05 +0200
Subject: [PATCH 45/64] refactor

---
 unikernel.ml | 63 +++++++++++++++++++++++++++++++---------------------
 1 file changed, 38 insertions(+), 25 deletions(-)

diff --git a/unikernel.ml b/unikernel.ml
index 0759cbc8..0a44057f 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -854,32 +854,45 @@ struct
             match Albatross_json.policy_of_json json with
             | Ok policy -> (
                 match Albatross.policy albatross with
-                | Error err ->
-                    Logs.err (fun m -> m "couldn't retrieve root policy: %s" err);
-                    http_response reqd ~title:"Error" ~data:err
-                      `Internal_server_error
-                | Ok Some root_policy -> (
-                    match root_policy with
-                        match
-                          Vmm_core.Policy.is_smaller ~super:root_policy
-                            ~sub:policy
-                        with
-                        | Error (`Msg err) ->
-                            Logs.err (fun m -> m "policy %a is not smaller than root policy %a: %s" Vmm_core.Policy.pp policy Vmm_core.Policy.pp root_policy err);
-                            http_response reqd ~title:"Error" ~data:err
+                | Ok (Some root_policy) -> (
+                    match
+                      Vmm_core.Policy.is_smaller ~super:root_policy ~sub:policy
+                    with
+                    | Error (`Msg err) ->
+                        Logs.err (fun m ->
+                            m "policy %a is not smaller than root policy %a: %s"
+                              Vmm_core.Policy.pp policy Vmm_core.Policy.pp
+                              root_policy err);
+                        http_response reqd ~title:"Error"
+                          ~data:
+                            ("policy is not smaller than root policy: " ^ err)
+                          `Internal_server_error
+                    | Ok () -> (
+                        Albatross.set_policy albatross ~domain:u.name policy
+                        >>= function
+                        | Error err ->
+                            Logs.err (fun m ->
+                                m "error setting policy %a for %s: %s"
+                                  Vmm_core.Policy.pp policy u.name err);
+                            http_response reqd ~title:"Error"
+                              ~data:("error setting policy: " ^ err)
                               `Internal_server_error
-                        | Ok () -> (
-                            Albatross.set_policy albatross ~domain:u.name policy
-                            >>= function
-                            | Error err ->
-                                http_response reqd ~title:"Error" ~data:err
-                                  `Internal_server_error
-                            | Ok policy ->
-                                http_response reqd ~title:"Success"
-                                  ~data:
-                                    (Yojson.Basic.to_string
-                                       (Albatross_json.policy_info policy))
-                                  `OK))))
+                        | Ok policy ->
+                            http_response reqd ~title:"Success"
+                              ~data:
+                                (Yojson.Basic.to_string
+                                   (Albatross_json.policy_info policy))
+                              `OK))
+                | Ok None ->
+                    Logs.err (fun m -> m "policy: root policy can't be null ");
+                    http_response reqd ~title:"Error"
+                      ~data:"root policy is null" `Internal_server_error
+                | Error err ->
+                    Logs.err (fun m ->
+                        m "policy: an error occured while fetching root policy ");
+                    http_response reqd ~title:"Error"
+                      ~data:("error with root policy: " ^ err)
+                      `Internal_server_error)
             | Error (`Msg err) ->
                 http_response reqd ~title:"Error" ~data:err `Bad_request)
         | None ->

From 11d22009b792a57f0c8876c979f1f1fc18c41d1b Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:32:26 +0200
Subject: [PATCH 46/64] Update user_single.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 user_single.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/user_single.ml b/user_single.ml
index a6bdf913..f46dcf1b 100644
--- a/user_single.ml
+++ b/user_single.ml
@@ -270,7 +270,7 @@ let user_single_layout (user : User_model.user) unikernels policy current_time =
                                                     text-primary-600 uppercase";
                                                  ];
                                              ]
-                                           [ txt "Allowed VMs" ];
+                                           [ txt "Allowed unikernels" ];
                                          th
                                            ~a:
                                              [

From e363eea442a285ff655932fc598e98738cc31dac Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:32:38 +0200
Subject: [PATCH 47/64] Update assets/main.js

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 assets/main.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/assets/main.js b/assets/main.js
index 4c09f887..109ce224 100644
--- a/assets/main.js
+++ b/assets/main.js
@@ -291,7 +291,7 @@ async function updatePolicy() {
 			},
 			body: JSON.stringify(
 				{
-					"vms": Number(vm_count),
+					"unikernels": Number(unikernel_count),
 					"memory": Number(mem_size),
 					"block": Number(storage_size),
 					"cpuids": cpuids,

From 743a0c64263e1888c9e06e9cac5e6433803ec66c Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:32:46 +0200
Subject: [PATCH 48/64] Update update_policy.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 update_policy.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/update_policy.ml b/update_policy.ml
index 6cfceb86..c7603879 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -44,7 +44,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
                 span
                   ~a:
                     [
-                      a_id "f_allowed_vms";
+                      a_id "f_allowed_unikernels";
                       a_contenteditable true;
                       a_class [ "text-4xl border px-4" ];
                       a_user_data "x-on:keydown.enter.prevent" "";

From 580de08bfbd1dbdb342cd38444db47858182e95c Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:32:54 +0200
Subject: [PATCH 49/64] Update albatross_json.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross_json.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index bba6457f..6e76d82c 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -178,7 +178,7 @@ let policy_of_json js =
   | `Assoc xs -> (
       match
         Utils.Json.
-          ( get "vms" xs,
+          ( get "unikernels" xs,
             get "memory" xs,
             get "block" xs,
             get "cpuids" xs,

From 2ef3bb98de8d1c55dac596abc2f02bf65a2df0aa Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:33:01 +0200
Subject: [PATCH 50/64] Update assets/main.js

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 assets/main.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/assets/main.js b/assets/main.js
index 109ce224..40ebad8f 100644
--- a/assets/main.js
+++ b/assets/main.js
@@ -274,7 +274,7 @@ function multiselect(selected, options) {
 }
 
 async function updatePolicy() {
-	const vm_count = document.getElementById("f_allowed_vms").innerText;
+	const unikernel_count = document.getElementById("f_allowed_unikernels").innerText;
 	const mem_size = document.getElementById("f_allowed_memory").innerText;
 	const storage_size = document.getElementById("f_allowed_storage").innerText;
 	const cpuids = document.getElementById("selectedCPUs").value;

From 724905b229246916f58859665c1dcc8b4d814c09 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:33:08 +0200
Subject: [PATCH 51/64] Update albatross_json.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross_json.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index 6e76d82c..fd596080 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -184,7 +184,7 @@ let policy_of_json js =
             get "cpuids" xs,
             get "bridges" xs )
       with
-      | ( Some (`Int vms),
+      | ( Some (`Int unikernels),
           Some (`Int memory),
           Some (`Int block),
           Some (`String cpuids),

From 5d954e17f00c361c5dd439cf448ff4eff32c57f2 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:33:15 +0200
Subject: [PATCH 52/64] Update albatross_json.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 albatross_json.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index fd596080..88b28e0a 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -191,7 +191,7 @@ let policy_of_json js =
           Some (`String bridges) ) ->
           let policy =
             {
-              Vmm_core.Policy.vms;
+              Vmm_core.Policy.vms = unikernels;
               memory;
               block = (if block = 0 then None else Some block);
               cpuids =

From ce8aa3c9a6bfdf55d7a091f9146b6a1970f6d400 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 11:38:23 +0200
Subject: [PATCH 53/64] Update unikernel.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 unikernel.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/unikernel.ml b/unikernel.ml
index 0a44057f..3885dddf 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -889,7 +889,7 @@ struct
                       ~data:"root policy is null" `Internal_server_error
                 | Error err ->
                     Logs.err (fun m ->
-                        m "policy: an error occured while fetching root policy ");
+                        m "policy: an error occured while fetching root policy: %s" err);
                     http_response reqd ~title:"Error"
                       ~data:("error with root policy: " ^ err)
                       `Internal_server_error)

From dc96fb9a73102c6cacfcf57eaaa024ea6f9d0fb7 Mon Sep 17 00:00:00 2001
From: Auto-OCamlformat <auto@ocaml.format>
Date: Wed, 2 Oct 2024 09:41:35 +0000
Subject: [PATCH 54/64] formatted code

---
 unikernel.ml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/unikernel.ml b/unikernel.ml
index 3885dddf..2144c6a4 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -889,7 +889,10 @@ struct
                       ~data:"root policy is null" `Internal_server_error
                 | Error err ->
                     Logs.err (fun m ->
-                        m "policy: an error occured while fetching root policy: %s" err);
+                        m
+                          "policy: an error occured while fetching root \
+                           policy: %s"
+                          err);
                     http_response reqd ~title:"Error"
                       ~data:("error with root policy: " ^ err)
                       `Internal_server_error)

From a4129a96c660c84fe8d052ad490a996392b5c165 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 11:46:32 +0200
Subject: [PATCH 55/64] renmae and lint

---
 unikernel.ml     |  9 ++++++---
 update_policy.ml | 13 +++++++------
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/unikernel.ml b/unikernel.ml
index 3885dddf..21575829 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -790,14 +790,14 @@ struct
           | Error _ -> Albatross.empty_policy
         in
         match Albatross.policy_resource_avalaible albatross with
-        | Ok root_policy ->
+        | Ok unallocated_resources ->
             Lwt.return
               (reply reqd ~content_type:"text/html"
                  (Dashboard.dashboard_layout user
                     ~page_title:(String.capitalize_ascii u.name ^ " | Mollymawk")
                     ~content:
                       (Update_policy.update_policy_layout u ~user_policy
-                         ~root_policy)
+                         ~unallocated_resources)
                     ~icon:"/images/robur.png" ())
                  `OK)
         | Error err ->
@@ -889,7 +889,10 @@ struct
                       ~data:"root policy is null" `Internal_server_error
                 | Error err ->
                     Logs.err (fun m ->
-                        m "policy: an error occured while fetching root policy: %s" err);
+                        m
+                          "policy: an error occured while fetching root \
+                           policy: %s"
+                          err);
                     http_response reqd ~title:"Error"
                       ~data:("error with root policy: " ^ err)
                       `Internal_server_error)
diff --git a/update_policy.ml b/update_policy.ml
index c7603879..ef3b738f 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -1,4 +1,5 @@
-let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
+let update_policy_layout (user : User_model.user) ~user_policy
+    ~unallocated_resources =
   Tyxml_html.(
     section
       ~a:[ a_id "policy-form" ]
@@ -18,7 +19,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
               [
                 txt
                   ("total available: "
-                  ^ string_of_int root_policy.Vmm_core.Policy.vms);
+                  ^ string_of_int unallocated_resources.Vmm_core.Policy.vms);
               ];
             div
               ~a:
@@ -84,7 +85,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
               [
                 txt
                   ("total available: "
-                  ^ string_of_int root_policy.Vmm_core.Policy.memory
+                  ^ string_of_int unallocated_resources.Vmm_core.Policy.memory
                   ^ " MB");
               ];
             div
@@ -154,7 +155,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
                 txt
                   ("total available: "
                   ^ string_of_int
-                      (match root_policy.Vmm_core.Policy.block with
+                      (match unallocated_resources.Vmm_core.Policy.block with
                       | None -> 0
                       | Some x -> x)
                   ^ " MB");
@@ -239,7 +240,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
                          (Vmm_core.IS.elements user_policy.cpuids)
                      ^ ", "
                      ^ cpuid_to_array_string
-                         (Vmm_core.IS.elements root_policy.cpuids)
+                         (Vmm_core.IS.elements unallocated_resources.cpuids)
                      ^ ")");
                    a_class [ "multiselect border my-4 p-4 rounded" ];
                  ])
@@ -366,7 +367,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy =
                      ("multiselect("
                      ^ bridges_to_array_string user_policy.bridges
                      ^ ", "
-                     ^ bridges_to_array_string root_policy.bridges
+                     ^ bridges_to_array_string unallocated_resources.bridges
                      ^ ")");
                    a_class [ "multiselect border my-4 p-4 rounded" ];
                  ])

From 18255162234466624ba84a90bb9ad0e648fe208d Mon Sep 17 00:00:00 2001
From: Hannes Mehnert <hannes@mehnert.org>
Date: Wed, 2 Oct 2024 11:58:22 +0200
Subject: [PATCH 56/64] fix last occurence of 'allowed_vms'

---
 albatross_json.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/albatross_json.ml b/albatross_json.ml
index 88b28e0a..e3a95767 100644
--- a/albatross_json.ml
+++ b/albatross_json.ml
@@ -74,7 +74,7 @@ let policy_info (name, policy) =
   `Assoc
     [
       ("name", `String (Vmm_core.Name.to_string name));
-      ("allowed_vms", `Int policy.Vmm_core.Policy.vms);
+      ("allowed_unikernels", `Int policy.Vmm_core.Policy.vms);
       ( "allowed_cpuids",
         `List
           (List.map (fun id -> `Int id) (Vmm_core.IS.elements policy.cpuids)) );

From 2c2f545c23a2d7412f9e7c25a0a7a168ec89dec4 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 12:10:13 +0200
Subject: [PATCH 57/64] computer total available resources correctly

---
 albatross.ml     | 17 ++++++------
 unikernel.ml     |  4 +--
 update_policy.ml | 72 ++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 68 insertions(+), 25 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index b578ee69..950a558e 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -81,14 +81,15 @@ struct
     match root_policy with
     | Ok root_policy ->
         Ok
-          Vmm_core.Policy.
-            {
-              vms = root_policy.vms - vms_used;
-              cpuids = root_policy.cpuids;
-              memory = root_policy.memory - memory_used;
-              block = Option.map (fun b -> b - storage_used) root_policy.block;
-              bridges = root_policy.bridges;
-            }
+          ( Vmm_core.Policy.
+              {
+                vms = root_policy.vms - vms_used;
+                cpuids = root_policy.cpuids;
+                memory = root_policy.memory - memory_used;
+                block = Option.map (fun b -> b - storage_used) root_policy.block;
+                bridges = root_policy.bridges;
+              },
+            root_policy )
     | Error (`Msg err) -> Error err
 
   let key_ids exts pub issuer =
diff --git a/unikernel.ml b/unikernel.ml
index 21575829..163cd77d 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -790,14 +790,14 @@ struct
           | Error _ -> Albatross.empty_policy
         in
         match Albatross.policy_resource_avalaible albatross with
-        | Ok unallocated_resources ->
+        | Ok (unallocated_resources, root_policy) ->
             Lwt.return
               (reply reqd ~content_type:"text/html"
                  (Dashboard.dashboard_layout user
                     ~page_title:(String.capitalize_ascii u.name ^ " | Mollymawk")
                     ~content:
                       (Update_policy.update_policy_layout u ~user_policy
-                         ~unallocated_resources)
+                         ~root_policy ~unallocated_resources)
                     ~icon:"/images/robur.png" ())
                  `OK)
         | Error err ->
diff --git a/update_policy.ml b/update_policy.ml
index ef3b738f..2da94949 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -1,4 +1,4 @@
-let update_policy_layout (user : User_model.user) ~user_policy
+let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
     ~unallocated_resources =
   Tyxml_html.(
     section
@@ -17,9 +17,21 @@ let update_policy_layout (user : User_model.user) ~user_policy
               [ txt "Allowed unikernels" ];
             p
               [
-                txt
-                  ("total available: "
-                  ^ string_of_int unallocated_resources.Vmm_core.Policy.vms);
+                span
+                  ~a:[ a_class [ "space-x-3" ] ]
+                  [
+                    txt
+                      ("total available: "
+                      ^ string_of_int
+                          Vmm_core.Policy.(root_policy.vms - user_policy.vms));
+                  ];
+                span
+                  [
+                    txt
+                      ("total unallocated: "
+                      ^ string_of_int unallocated_resources.Vmm_core.Policy.vms
+                      );
+                  ];
               ];
             div
               ~a:
@@ -83,10 +95,22 @@ let update_policy_layout (user : User_model.user) ~user_policy
               [ txt "Allowed Memory" ];
             p
               [
-                txt
-                  ("total available: "
-                  ^ string_of_int unallocated_resources.Vmm_core.Policy.memory
-                  ^ " MB");
+                span
+                  ~a:[ a_class [ "space-x-3" ] ]
+                  [
+                    txt
+                      ("total available: "
+                      ^ string_of_int
+                          Vmm_core.Policy.(
+                            root_policy.memory - user_policy.memory));
+                  ];
+                span
+                  [
+                    txt
+                      ("total unallocated: "
+                      ^ string_of_int
+                          unallocated_resources.Vmm_core.Policy.memory);
+                  ];
               ];
             div
               ~a:
@@ -152,13 +176,31 @@ let update_policy_layout (user : User_model.user) ~user_policy
               [ txt "Allowed Storage" ];
             p
               [
-                txt
-                  ("total available: "
-                  ^ string_of_int
-                      (match unallocated_resources.Vmm_core.Policy.block with
-                      | None -> 0
-                      | Some x -> x)
-                  ^ " MB");
+                span
+                  ~a:[ a_class [ "space-x-3" ] ]
+                  [
+                    txt
+                      ("total available: "
+                      ^ string_of_int
+                          Vmm_core.Policy.(
+                            match (root_policy.block, user_policy.block) with
+                            | Some root_block, Some user_block ->
+                                root_block - user_block
+                            | Some root_block, None -> root_block
+                            | _ -> 0));
+                  ];
+                span
+                  [
+                    txt
+                      ("total unallocated: "
+                      ^ string_of_int
+                          (match
+                             unallocated_resources.Vmm_core.Policy.block
+                           with
+                          | None -> 0
+                          | Some x -> x)
+                      ^ " MB");
+                  ];
               ];
             div
               ~a:

From 4bfbb90142c36295385b40f4aeee4470a9529ca9 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 12:10:50 +0200
Subject: [PATCH 58/64] some styling

---
 update_policy.ml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/update_policy.ml b/update_policy.ml
index 2da94949..acb98413 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -18,7 +18,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
             p
               [
                 span
-                  ~a:[ a_class [ "space-x-3" ] ]
+                  ~a:[ a_class [ "space-x-5" ] ]
                   [
                     txt
                       ("total available: "
@@ -96,7 +96,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
             p
               [
                 span
-                  ~a:[ a_class [ "space-x-3" ] ]
+                  ~a:[ a_class [ "space-x-5" ] ]
                   [
                     txt
                       ("total available: "
@@ -177,7 +177,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
             p
               [
                 span
-                  ~a:[ a_class [ "space-x-3" ] ]
+                  ~a:[ a_class [ "space-x-5" ] ]
                   [
                     txt
                       ("total available: "

From d85a02c74d8492b2c7e2ff849302f12f52d2046f Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 13:02:46 +0200
Subject: [PATCH 59/64] better layout

---
 update_policy.ml | 99 +++++++++++++++++++++---------------------------
 1 file changed, 43 insertions(+), 56 deletions(-)

diff --git a/update_policy.ml b/update_policy.ml
index acb98413..310e7420 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -16,22 +16,18 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
               ~a:[ a_class [ "block text-sm font-medium" ] ]
               [ txt "Allowed unikernels" ];
             p
+              ~a:[ a_class [ "space-x-5" ] ]
               [
-                span
-                  ~a:[ a_class [ "space-x-5" ] ]
-                  [
-                    txt
-                      ("total available: "
-                      ^ string_of_int
-                          Vmm_core.Policy.(root_policy.vms - user_policy.vms));
-                  ];
-                span
-                  [
-                    txt
-                      ("total unallocated: "
-                      ^ string_of_int unallocated_resources.Vmm_core.Policy.vms
-                      );
-                  ];
+                txt
+                  ("total available: "
+                  ^ string_of_int
+                      Vmm_core.Policy.(root_policy.vms - user_policy.vms));
+              ];
+            p
+              [
+                txt
+                  ("total unallocated: "
+                  ^ string_of_int unallocated_resources.Vmm_core.Policy.vms);
               ];
             div
               ~a:
@@ -94,23 +90,19 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
               ~a:[ a_class [ "block text-sm font-medium" ] ]
               [ txt "Allowed Memory" ];
             p
+              ~a:[ a_class [ "space-x-5" ] ]
               [
-                span
-                  ~a:[ a_class [ "space-x-5" ] ]
-                  [
-                    txt
-                      ("total available: "
-                      ^ string_of_int
-                          Vmm_core.Policy.(
-                            root_policy.memory - user_policy.memory));
-                  ];
-                span
-                  [
-                    txt
-                      ("total unallocated: "
-                      ^ string_of_int
-                          unallocated_resources.Vmm_core.Policy.memory);
-                  ];
+                txt
+                  ("total available: "
+                  ^ string_of_int
+                      Vmm_core.Policy.(root_policy.memory - user_policy.memory)
+                  );
+              ];
+            p
+              [
+                txt
+                  ("total unallocated: "
+                  ^ string_of_int unallocated_resources.Vmm_core.Policy.memory);
               ];
             div
               ~a:
@@ -175,32 +167,27 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
               ~a:[ a_class [ "block text-sm font-medium" ] ]
               [ txt "Allowed Storage" ];
             p
+              ~a:[ a_class [ "space-x-5" ] ]
               [
-                span
-                  ~a:[ a_class [ "space-x-5" ] ]
-                  [
-                    txt
-                      ("total available: "
-                      ^ string_of_int
-                          Vmm_core.Policy.(
-                            match (root_policy.block, user_policy.block) with
-                            | Some root_block, Some user_block ->
-                                root_block - user_block
-                            | Some root_block, None -> root_block
-                            | _ -> 0));
-                  ];
-                span
-                  [
-                    txt
-                      ("total unallocated: "
-                      ^ string_of_int
-                          (match
-                             unallocated_resources.Vmm_core.Policy.block
-                           with
-                          | None -> 0
-                          | Some x -> x)
-                      ^ " MB");
-                  ];
+                txt
+                  ("total available: "
+                  ^ string_of_int
+                      Vmm_core.Policy.(
+                        match (root_policy.block, user_policy.block) with
+                        | Some root_block, Some user_block ->
+                            root_block - user_block
+                        | Some root_block, None -> root_block
+                        | _ -> 0));
+              ];
+            p
+              [
+                txt
+                  ("total unallocated: "
+                  ^ string_of_int
+                      (match unallocated_resources.Vmm_core.Policy.block with
+                      | None -> 0
+                      | Some x -> x)
+                  ^ " MB");
               ];
             div
               ~a:

From 6f6378db2f92471846f12676f36ff94fc9a439e7 Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 13:57:41 +0200
Subject: [PATCH 60/64] Update update_policy.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 update_policy.ml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/update_policy.ml b/update_policy.ml
index 310e7420..5031aa7f 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -21,7 +21,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
                 txt
                   ("total available: "
                   ^ string_of_int
-                      Vmm_core.Policy.(root_policy.vms - user_policy.vms));
+                      Vmm_core.Policy.(unallocated_resources.vms + user_policy.vms));
               ];
             p
               [

From 434d32ada5478ddb73e0033c51be6ff70d56315f Mon Sep 17 00:00:00 2001
From: PixieDust <111846546+PizieDust@users.noreply.github.com>
Date: Wed, 2 Oct 2024 13:57:49 +0200
Subject: [PATCH 61/64] Update update_policy.ml

Co-authored-by: Hannes Mehnert <hannes@mehnert.org>
---
 update_policy.ml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/update_policy.ml b/update_policy.ml
index 5031aa7f..2d3620b7 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -173,10 +173,10 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
                   ("total available: "
                   ^ string_of_int
                       Vmm_core.Policy.(
-                        match (root_policy.block, user_policy.block) with
-                        | Some root_block, Some user_block ->
-                            root_block - user_block
-                        | Some root_block, None -> root_block
+                        match (unallocated_resources.block, user_policy.block) with
+                        | Some unallocated, Some user_block ->
+                            unallocated + user_block
+                        | Some unallocated, None -> unallocated
                         | _ -> 0));
               ];
             p

From f2418180263ee9accb5519f0746a446a8f1e8f7d Mon Sep 17 00:00:00 2001
From: Auto-OCamlformat <auto@ocaml.format>
Date: Wed, 2 Oct 2024 12:00:36 +0000
Subject: [PATCH 62/64] formatted code

---
 update_policy.ml | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/update_policy.ml b/update_policy.ml
index 2d3620b7..57a34f2e 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -21,7 +21,8 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
                 txt
                   ("total available: "
                   ^ string_of_int
-                      Vmm_core.Policy.(unallocated_resources.vms + user_policy.vms));
+                      Vmm_core.Policy.(
+                        unallocated_resources.vms + user_policy.vms));
               ];
             p
               [
@@ -173,7 +174,9 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
                   ("total available: "
                   ^ string_of_int
                       Vmm_core.Policy.(
-                        match (unallocated_resources.block, user_policy.block) with
+                        match
+                          (unallocated_resources.block, user_policy.block)
+                        with
                         | Some unallocated, Some user_block ->
                             unallocated + user_block
                         | Some unallocated, None -> unallocated

From 739db37fccf729509cd6562e98a7a9187c8cae68 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 14:17:08 +0200
Subject: [PATCH 63/64] changes suggested by @hannesm

---
 albatross.ml     | 17 +++++++------
 unikernel.ml     |  4 ++--
 update_policy.ml | 62 ++++++++++++++++--------------------------------
 3 files changed, 30 insertions(+), 53 deletions(-)

diff --git a/albatross.ml b/albatross.ml
index 950a558e..b578ee69 100644
--- a/albatross.ml
+++ b/albatross.ml
@@ -81,15 +81,14 @@ struct
     match root_policy with
     | Ok root_policy ->
         Ok
-          ( Vmm_core.Policy.
-              {
-                vms = root_policy.vms - vms_used;
-                cpuids = root_policy.cpuids;
-                memory = root_policy.memory - memory_used;
-                block = Option.map (fun b -> b - storage_used) root_policy.block;
-                bridges = root_policy.bridges;
-              },
-            root_policy )
+          Vmm_core.Policy.
+            {
+              vms = root_policy.vms - vms_used;
+              cpuids = root_policy.cpuids;
+              memory = root_policy.memory - memory_used;
+              block = Option.map (fun b -> b - storage_used) root_policy.block;
+              bridges = root_policy.bridges;
+            }
     | Error (`Msg err) -> Error err
 
   let key_ids exts pub issuer =
diff --git a/unikernel.ml b/unikernel.ml
index 163cd77d..21575829 100644
--- a/unikernel.ml
+++ b/unikernel.ml
@@ -790,14 +790,14 @@ struct
           | Error _ -> Albatross.empty_policy
         in
         match Albatross.policy_resource_avalaible albatross with
-        | Ok (unallocated_resources, root_policy) ->
+        | Ok unallocated_resources ->
             Lwt.return
               (reply reqd ~content_type:"text/html"
                  (Dashboard.dashboard_layout user
                     ~page_title:(String.capitalize_ascii u.name ^ " | Mollymawk")
                     ~content:
                       (Update_policy.update_policy_layout u ~user_policy
-                         ~root_policy ~unallocated_resources)
+                         ~unallocated_resources)
                     ~icon:"/images/robur.png" ())
                  `OK)
         | Error err ->
diff --git a/update_policy.ml b/update_policy.ml
index 57a34f2e..822edb61 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -1,11 +1,11 @@
-let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
+let update_policy_layout (user : User_model.user) ~user_policy
     ~unallocated_resources =
   Tyxml_html.(
     section
       ~a:[ a_id "policy-form" ]
       [
         h2
-          ~a:[ a_class [ "font-semibold text-xl" ] ]
+          ~a:[ a_class [ "font-semibold text-2xl" ] ]
           [ txt ("Set Policy for " ^ user.name) ];
         p ~a:[ a_id "form-alert"; a_class [ "my-4" ] ] [];
         p ~a:[ a_id "user_id"; a_class [ "hidden" ] ] [ txt user.uuid ];
@@ -13,23 +13,17 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
           ~a:[ a_class [ "my-3" ] ]
           [
             label
-              ~a:[ a_class [ "block text-sm font-medium" ] ]
-              [ txt "Allowed unikernels" ];
-            p
-              ~a:[ a_class [ "space-x-5" ] ]
+              ~a:[ a_class [ "block font-medium" ] ]
+              [ txt "Allowed Unikernels" ];
+            small
+              ~a:[ a_class [ "text-sm" ] ]
               [
                 txt
-                  ("total available: "
+                  ("can assign up to: "
                   ^ string_of_int
                       Vmm_core.Policy.(
                         unallocated_resources.vms + user_policy.vms));
               ];
-            p
-              [
-                txt
-                  ("total unallocated: "
-                  ^ string_of_int unallocated_resources.Vmm_core.Policy.vms);
-              ];
             div
               ~a:
                 [
@@ -88,22 +82,17 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
           ~a:[ a_class [ "my-3" ] ]
           [
             label
-              ~a:[ a_class [ "block text-sm font-medium" ] ]
+              ~a:[ a_class [ "block font-medium" ] ]
               [ txt "Allowed Memory" ];
             p
-              ~a:[ a_class [ "space-x-5" ] ]
+              ~a:[ a_class [ "text-sm" ] ]
               [
                 txt
-                  ("total available: "
+                  ("can assign up to: "
                   ^ string_of_int
-                      Vmm_core.Policy.(root_policy.memory - user_policy.memory)
-                  );
-              ];
-            p
-              [
-                txt
-                  ("total unallocated: "
-                  ^ string_of_int unallocated_resources.Vmm_core.Policy.memory);
+                      Vmm_core.Policy.(
+                        unallocated_resources.memory + user_policy.memory)
+                  ^ " MB");
               ];
             div
               ~a:
@@ -145,7 +134,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
                         "$event.target.innerText = count;";
                     ]
                   [];
-                span ~a:[ a_class [ "text-4xl" ] ] [ txt "MB" ];
+                span ~a:[ a_class [ "text-4xl" ] ] [ txt " MB" ];
                 button
                   ~a:
                     [
@@ -165,13 +154,13 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
           ~a:[ a_class [ "my-3" ] ]
           [
             label
-              ~a:[ a_class [ "block text-sm font-medium" ] ]
+              ~a:[ a_class [ "block font-medium" ] ]
               [ txt "Allowed Storage" ];
             p
-              ~a:[ a_class [ "space-x-5" ] ]
+              ~a:[ a_class [ "text-sm" ] ]
               [
                 txt
-                  ("total available: "
+                  ("can assign up to: "
                   ^ string_of_int
                       Vmm_core.Policy.(
                         match
@@ -180,16 +169,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
                         | Some unallocated, Some user_block ->
                             unallocated + user_block
                         | Some unallocated, None -> unallocated
-                        | _ -> 0));
-              ];
-            p
-              [
-                txt
-                  ("total unallocated: "
-                  ^ string_of_int
-                      (match unallocated_resources.Vmm_core.Policy.block with
-                      | None -> 0
-                      | Some x -> x)
+                        | _ -> 0)
                   ^ " MB");
               ];
             div
@@ -252,9 +232,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
         div
           ~a:[ a_class [ "my-3" ] ]
           [
-            label
-              ~a:[ a_class [ "block text-sm font-medium" ] ]
-              [ txt "CPU IDs" ];
+            label ~a:[ a_class [ "block font-medium" ] ] [ txt "CPU IDs" ];
             div
               ~a:
                 (let cpuid_to_array_string lst =
@@ -382,7 +360,7 @@ let update_policy_layout (user : User_model.user) ~user_policy ~root_policy
           ~a:[ a_class [ "my-3" ] ]
           [
             label
-              ~a:[ a_class [ "block text-sm font-medium" ] ]
+              ~a:[ a_class [ "block font-medium" ] ]
               [ txt "Network interfaces" ];
             div
               ~a:

From be9d97df681ce2068e0114b2ac5dc09fd23f12e3 Mon Sep 17 00:00:00 2001
From: PizieDust <playersrebirth@gmail.com>
Date: Wed, 2 Oct 2024 15:00:11 +0200
Subject: [PATCH 64/64] minor change

---
 update_policy.ml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/update_policy.ml b/update_policy.ml
index 822edb61..56df64b7 100644
--- a/update_policy.ml
+++ b/update_policy.ml
@@ -84,7 +84,7 @@ let update_policy_layout (user : User_model.user) ~user_policy
             label
               ~a:[ a_class [ "block font-medium" ] ]
               [ txt "Allowed Memory" ];
-            p
+            small
               ~a:[ a_class [ "text-sm" ] ]
               [
                 txt
@@ -156,7 +156,7 @@ let update_policy_layout (user : User_model.user) ~user_policy
             label
               ~a:[ a_class [ "block font-medium" ] ]
               [ txt "Allowed Storage" ];
-            p
+            small
               ~a:[ a_class [ "text-sm" ] ]
               [
                 txt