From 880eb422ff096b83b267a15abfbeae389173e0c1 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 07:32:46 +0200 Subject: [PATCH 01/22] deploy unikernels --- assets/create_unikernel.html | 69 --------------------- assets/main.js | 63 ++++++++++++++++++- dashboard.ml | 2 + unikernel.ml | 69 ++++++++++++++++----- unikernel_create.ml | 116 +++++++++++++++++++++++++++++++++++ 5 files changed, 232 insertions(+), 87 deletions(-) delete mode 100644 assets/create_unikernel.html create mode 100644 unikernel_create.ml diff --git a/assets/create_unikernel.html b/assets/create_unikernel.html deleted file mode 100644 index 6bf72b88..00000000 --- a/assets/create_unikernel.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - Mollyhawk | Create Unikernel - - - - - - -
-

Deply a Unikernel

-
-
-
- - -
-
-
- - -
-
- -
- -
-
- -
-
-

- - - - - diff --git a/assets/main.js b/assets/main.js index d7d4f802..95b546cf 100644 --- a/assets/main.js +++ b/assets/main.js @@ -1,5 +1,5 @@ document.addEventListener('DOMContentLoaded', function () { - AOS.init(); + AOS.init(); }); function filterData() { @@ -74,7 +74,8 @@ async function saveConfig() { formAlert.classList.remove("hidden", "text-secondary-500"); formAlert.classList.add("text-primary-500"); formAlert.textContent = "Succesfully updated"; - setTimeout(function() { + postAlert("bg-primary-300", data.data); + setTimeout(function () { window.location.reload(); }, 2000); } else { @@ -95,3 +96,61 @@ function closeBanner() { var banner = document.getElementById("banner-message"); banner.style.display = "none"; } + +function postAlert(bg_color, content) { + const alertContainer = document.getElementById("alert-container"); + const alert = document.createElement("div"); + alert.className = `text-white transition ease-in-out delay-150 duration-300 ${bg_color}`; + alert.textContent = content; + alertContainer.appendChild(alert); + setTimeout(() => { + alertContainer.removeChild(alert); + }, 1600); +} + +async function deployUnikernel() { + const name = document.getElementById("unikernel-name").value.trim(); + const arguments = document.getElementById("unikernel-arguments").value.trim(); + const binary = document.getElementById("unikernel-binary").files[0]; + const formAlert = document.getElementById("form-alert"); + if (!name || !binary) { + formAlert.classList.remove("hidden", "text-primary-500"); + formAlert.classList.add("text-secondary-500"); + formAlert.textContent = "Please fill in the required data" + } else { + let formData = new FormData(); + formData.append("name", name.value); + formData.append("binary", binary) + formData.append("arguments", arguments) + try { + const response = await fetch("/unikernel/create", { + method: 'POST', + mode: "no-cors", + headers: { + "Content-Type": "application/json", + }, + body: formData + }) + 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.href = "/dashboard"; + }, 2000); + } else { + postAlert("bg-secondary-300", data.data); + formAlert.classList.remove("hidden", "text-primary-500"); + formAlert.classList.add("text-secondary-500"); + formAlert.textContent = data.data + } + } catch (error) { + postAlert("bg-secondary-300", error); + formAlert.classList.remove("hidden"); + formAlert.classList.add("text-secondary-500"); + formAlert.textContent = error + } + } +} diff --git a/dashboard.ml b/dashboard.ml index a0d6b610..783797f5 100644 --- a/dashboard.ml +++ b/dashboard.ml @@ -169,12 +169,14 @@ let dashboard_layout ~icon ?(page_title = "Dashboard | Mollymawk") section ~a:[ a_class [ "col-span-2 px-4 py-6 w-full mx-auto" ] ] [ + div ~a:[a_id "alert-container"; a_class ["absolute top-1/4 rounded-md right-4 z-50 w-fit space-y-2 p-4 shadow border text-wrap"]] []; div ~a:[ a_class [ "w-full my-2" ] ] [ a ~a: [ + a_href "/unikernel/deploy"; a_class [ "cursor-pointer bg-primary-500 \ diff --git a/unikernel.ml b/unikernel.ml index 1149c327..0cfeb2d1 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -55,11 +55,6 @@ struct { molly_img; robur_img; albatross_img; mirage_img; dashboard_img } | _ -> failwith "Unexpected number of images" - let create_html_form assets = - KV_ASSETS.get assets (Mirage_kv.Key.v "create_unikernel.html") >|= function - | Error _e -> invalid_arg "Form could not be loaded" - | Ok html -> html - module Store = Storage.Make (BLOCK) module Map = Map.Make (String) @@ -105,7 +100,7 @@ struct in go (Map.empty, []) m - let request_handler stack albatross js_file css_file imgs html store + let request_handler stack albatross js_file css_file imgs store (_ipaddr, _port) reqd = Lwt.async (fun () -> let reply ?(content_type = "text/plain") ?(header_list = []) data = @@ -764,13 +759,19 @@ struct Lwt.return (reply ~content_type:"application/json" (Utils.Status.to_json status))) - | "/unikernel/create" -> + | "/unikernel/deploy" -> let now = Ptime.v (P.now_d_ps ()) in let _, (t : Storage.t) = !store in let users = User_model.create_user_session_map t.users in let middlewares = [ Middleware.auth_middleware now users ] in Middleware.apply_middleware middlewares - (fun _reqd -> Lwt.return (reply ~content_type:"text/html" html)) + (fun _reqd -> + Lwt.return + (reply ~content_type:"text/html" + (Dashboard.dashboard_layout + ~page_title:"Deploy a Unikernel | Mollymawk" + ~content:Unikernel_create.unikernel_create_layout + ~icon:"/images/robur.png" ()))) reqd | path when String.( @@ -800,7 +801,7 @@ struct in reply_json (`List data); Lwt.return_unit - | "/unikernel/deploy" -> ( + | "/unikernel/create" -> ( let response_body = Httpaf.Reqd.request_body reqd in let finished, notify_finished = Lwt.wait () in let wakeup v = Lwt.wakeup_later notify_finished v in @@ -855,17 +856,54 @@ struct | Error () -> Logs.warn (fun m -> m "error querying albatross"); - reply "error while querying albatross" + let status = + { + Utils.Status.code = 500; + title = "Error"; + data = "Error while querying Albatross."; + success = false; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) | Ok None -> Logs.warn (fun m -> m "got none"); - reply "got none" + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = "Got none"; + success = true; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) | Ok (Some (_hdr, res)) -> - reply_json (Albatross_json.res res)) + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = + Yojson.Safe.to_string + (Albatross_json.res res); + success = true; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status)) | Error (`Msg msg) -> Logs.warn (fun m -> m "couldn't decode data %s" msg); + let status = + { + Utils.Status.code = 403; + title = "Error"; + data = msg; + success = false; + } + in Lwt.return - (reply ("couldn't decode data (of_json): " ^ msg)) - ) + (reply ~content_type:"application/json" + (Utils.Status.to_json status))) | _ -> Logs.warn (fun m -> m "couldn't find fields"); Lwt.return (reply "couldn't find fields")))) @@ -911,7 +949,6 @@ struct js_contents assets >>= fun js_file -> css_contents assets >>= fun css_file -> images assets >>= fun imgs -> - create_html_form assets >>= fun html -> Store.Stored_data.connect storage >>= fun stored_data -> Store.read_data stored_data >>= function | Error (`Msg msg) -> failwith msg @@ -927,7 +964,7 @@ struct m "Initialise an HTTP server (no HTTPS) on http://127.0.0.1:%u/" port); let request_handler _flow = - request_handler stack albatross js_file css_file imgs html store + request_handler stack albatross js_file css_file imgs store in Paf.init ~port:8080 (S.tcp stack) >>= fun service -> let http = Paf.http_service ~error_handler request_handler in diff --git a/unikernel_create.ml b/unikernel_create.ml new file mode 100644 index 00000000..13c710c8 --- /dev/null +++ b/unikernel_create.ml @@ -0,0 +1,116 @@ +let unikernel_create_layout = + Tyxml_html.( + section + ~a:[ a_class [ "col-span-7 p-4 bg-gray-50 my-1" ] ] + [ + div + ~a:[ a_class [ "px-3 flex justify-between items-center" ] ] + [ + p + ~a:[ a_class [ "font-bold text-gray-700" ] ] + [ txt "Deploy a Unikernel" ]; + ]; + hr (); + div + ~a:[ a_class [ "space-y-6 mt-8 p-6 max-w-5xl mx-auto" ] ] + [ + p ~a:[ a_id "form-alert"; a_class [ "my-4 hidden" ] ] []; + div + [ + label + ~a:[ a_class [ "block text-sm font-medium" ] ] + [ txt "Name*" ]; + input + ~a: + [ + a_input_type `Text; + a_name "name"; + a_required () ; + a_id "unikernel-name"; + a_class + [ + "ring-primary-100 mt-1.5 transition appearance-none \ + block w-full px-3 py-3 rounded-xl shadow-sm border \ + hover:border-primary-200\n\ + \ \ + focus:border-primary-300 bg-primary-50 bg-opacity-0 \ + hover:bg-opacity-50 focus:bg-opacity-50 \ + ring-primary-200 focus:ring-primary-200\n\ + \ \ + focus:ring-[1px] focus:outline-none"; + ]; + ] + (); + ]; + div + [ + label + ~a:[ a_class [ "block text-sm font-medium" ] ] + [ txt "Arguments*" ]; + textarea + ~a: + [ + a_rows 4; + a_required () ; + a_name "arguments"; + a_id "unikernel-arguments"; + a_class + [ + "ring-primary-100 mt-1.5 transition appearance-none \ + block w-full px-3 py-3 rounded-xl shadow-sm border \ + hover:border-primary-200\n\ + \ \ + focus:border-primary-300 bg-primary-50 bg-opacity-0 \ + hover:bg-opacity-50 focus:bg-opacity-50 \ + ring-primary-200 focus:ring-primary-200\n\ + \ \ + focus:ring-[1px] focus:outline-none"; + ]; + ] + (txt ""); + ]; + div + [ + label + ~a:[ a_class [ "block text-sm font-medium" ] ] + [ txt "Unikernel Image Binary*" ]; + input + ~a: + [ + a_input_type `File; + a_name "binary"; + a_required () ; + a_id "unikernel-binary"; + a_class + [ + "ring-primary-100 mt-1.5 transition appearance-none \ + block w-full px-3 py-3 rounded-xl shadow-sm border \ + hover:border-primary-200\n\ + \ \ + focus:border-primary-300 bg-primary-50 bg-opacity-0 \ + hover:bg-opacity-50 focus:bg-opacity-50 \ + ring-primary-200 focus:ring-primary-200\n\ + \ \ + focus:ring-[1px] focus:outline-none"; + ]; + ] + (); + ]; + div + ~a:[ a_class [ "flex justify-items-center mx-auto w-60" ] ] + [ + button + ~a: + [ + a_id "deploy-button"; + a_onclick "deployUnikernel()"; + a_class + [ + "py-3 rounded bg-primary-500 hover:bg-primary-800 \ + w-full text-gray-50 font-semibold"; + ]; + ] + [ txt "Deploy" ]; + ]; + ]; + ]) From 78f2a78a04ebf26ce22179296c329c4e2ba13b05 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 09:08:37 +0200 Subject: [PATCH 02/22] view single unikernel --- unikernel_single.ml | 313 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 unikernel_single.ml diff --git a/unikernel_single.ml b/unikernel_single.ml new file mode 100644 index 00000000..ed84839b --- /dev/null +++ b/unikernel_single.ml @@ -0,0 +1,313 @@ +let unikernel_single_layout unikernel = + Tyxml_html.( + section + ~a:[ a_class [ "col-span-10 p-4 bg-gray-50 my-1" ] ] + [ + div + ~a:[ a_id "unikernel-container"; a_class [ "p-4 rounded-md" ] ] + [ + div + ~a:[ a_id "info-container" ] + [ + div + ~a:[ a_class [ "flex justify-between" ] ] + [ + div + [ + h2 [ txt "ExampleUnikernel" ]; + p + [ + txt + (Cstruct.to_string + unikernel.Vmm_core.Unikernel.digest); + ]; + ]; + div + [ + button + ~a: + [ + a_onclick "destroyUnikernel()"; + a_class + [ + "my-3 py-2 px-3 rounded bg-secondary-500 \ + text-white hover:bg-secondary-700 \ + text-secondary-50 font-semibold"; + ]; + ] + [ txt "Destory" ]; + ]; + ]; + div + ~a:[ a_class [ "grid grid-cols-3 gap-3 text-white my-3" ] ] + [ + div + ~a:[ a_class [ "p-4 rounded border border-primary-700" ] ] + [ + div + ~a:[ a_class [ "flex justify-between" ] ] + [ + i + ~a: + [ + a_class + [ + "fa-solid fa-microchip text-xl \ + text-primary-500"; + ]; + ] + []; + p ~a:[ a_class [ "text-md" ] ] [ txt "CPU" ]; + ]; + p + ~a:[ a_class [ "text-md" ] ] + [ txt (string_of_int unikernel.cpuid) ]; + ]; + div + ~a:[ a_class [ "p-4 rounded border border-primary-700" ] ] + [ + div + ~a:[ a_class [ "flex justify-between" ] ] + [ + i + ~a: + [ + a_class + [ + "fa-solid fa-hard-drive text-xl \ + text-primary-500"; + ]; + ] + []; + p ~a:[ a_class [ "text-md" ] ] [ txt "Memory" ]; + ]; + p + ~a:[ a_class [ "text-md" ] ] + [ txt (string_of_int unikernel.memory) ]; + ]; + div + ~a:[ a_class [ "p-4 rounded border border-primary-700" ] ] + [ + div + ~a:[ a_class [ "flex justify-between" ] ] + [ + i + ~a: + [ + a_class + [ + "fa-solid fa-warehouse text-xl \ + text-primary-500"; + ]; + ] + []; + p ~a:[ a_class [ "text-md" ] ] [ txt "Type" ]; + ]; + p + ~a:[ a_class [ "text-md" ] ] + [ txt (match unikernel.typ with `Solo5 -> "Solo5") ]; + ]; + ]; + div + ~a:[ a_class [ "grid grid-cols-2" ] ] + [ + div + ~a: + [ + a_class + [ + "rounded my-4 text-white p-4 divide-y border \ + border-primary-700"; + ]; + ] + [ + div + [ + p + ~a:[ a_class [ "text-xl font-semibold" ] ] + [ txt "Arguments" ]; + ]; + div + ~a:[ a_class [ "my-4" ] ] + [ + p + ~a:[ a_class [ "text-xl font-semibold" ] ] + [ txt "Block Devices" ]; + table + ~a: + [ + a_class + [ + "items-center bg-transparent w-full \ + border-collapse"; + ]; + ] + ~thead: + (thead + [ + tr + [ + th + ~a: + [ + a_class + [ + "border-t-0 px-6 \ + align-middle border-l-0 \ + border-r-0 text-md \ + whitespace-nowrap p-4"; + ]; + ] + [ txt "Host device" ]; + th + ~a: + [ + a_class + [ + "border-t-0 px-6 \ + align-middle border-l-0 \ + border-r-0 text-md \ + whitespace-nowrap p-4"; + ]; + ] + [ txt "Name" ]; + th + ~a: + [ + a_class + [ + "border-t-0 px-6 \ + align-middle border-l-0 \ + border-r-0 text-md \ + whitespace-nowrap p-4"; + ]; + ] + [ txt "Sector size" ]; + ]; + ]) + (List.map + (fun (name, device, size) -> + tr + [ + td + ~a:[ a_class [ "text-center" ] ] + [ txt name ]; + td + ~a:[ a_class [ "text-center" ] ] + [ + txt (Option.value device ~default:""); + ]; + td + ~a:[ a_class [ "text-center" ] ] + [ + txt + (string_of_int + (Option.value size ~default:0) + ^ "MB"); + ]; + ]) + unikernel.block_devices); + ]; + div + ~a:[ a_class [ "my-4" ] ] + [ + p + ~a:[ a_class [ "text-xl font-semibold" ] ] + [ txt "Network Devices" ]; + table + ~a: + [ + a_class + [ + "items-center bg-transparent w-full \ + border-collapse"; + ]; + ] + ~thead: + (thead + [ + tr + [ + th + ~a: + [ + a_class + [ + "border-t-0 px-6 \ + align-middle border-l-0 \ + border-r-0 text-md \ + whitespace-nowrap p-4"; + ]; + ] + [ txt "Host device" ]; + th + ~a: + [ + a_class + [ + "border-t-0 px-6 \ + align-middle border-l-0 \ + border-r-0 text-md \ + whitespace-nowrap p-4"; + ]; + ] + [ txt "Name" ]; + th + ~a: + [ + a_class + [ + "border-t-0 px-6 \ + align-middle border-l-0 \ + border-r-0 text-md \ + whitespace-nowrap p-4"; + ]; + ] + [ txt "Sector size" ]; + ]; + ]) + (List.map + (fun (name, device, mac) -> + tr + [ + td + ~a:[ a_class [ "text-center" ] ] + [ txt name ]; + td + ~a:[ a_class [ "text-center" ] ] + [ + txt (Option.value device ~default:""); + ]; + td + ~a:[ a_class [ "text-center" ] ] + [ + txt + (Macaddr.to_string + (Option.value mac + ~default: + (Macaddr.of_string_exn + "00-00-00-00-00-00"))); + ]; + ]) + unikernel.bridges); + ]; + div + ~a:[ a_class [ "my-4" ] ] + [ + p + ~a:[ a_class [ "text-xl font-semibold" ] ] + [ txt "Fail Behaviour" ]; + (match unikernel.fail_behaviour with + | `Quit -> p [ txt "Quit" ] + | `Restart None -> p [ txt "Restart" ] + | `Restart (Some _codes) -> + p [txt ""] + (* List.map + (fun code -> p [ txt (string_of_int code) ]) + (Vmm_core.IS.elements codes); *) ) + ]; + ]; + ]; + ]; + ]; + ]) From 3df7b6b35d43ccca19382693d41eb7840b3425e0 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:18:43 +0200 Subject: [PATCH 03/22] add toast --- dashboard.ml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dashboard.ml b/dashboard.ml index 783797f5..eac463fa 100644 --- a/dashboard.ml +++ b/dashboard.ml @@ -169,7 +169,17 @@ let dashboard_layout ~icon ?(page_title = "Dashboard | Mollymawk") section ~a:[ a_class [ "col-span-2 px-4 py-6 w-full mx-auto" ] ] [ - div ~a:[a_id "alert-container"; a_class ["absolute top-1/4 rounded-md right-4 z-50 w-fit space-y-2 p-4 shadow border text-wrap"]] []; + div + ~a: + [ + a_id "alert-container"; + a_class + [ + "absolute top-1/4 rounded-md right-4 z-50 w-fit \ + space-y-2 p-4 shadow border text-wrap"; + ]; + ] + []; div ~a:[ a_class [ "w-full my-2" ] ] [ From 1008c1ecfe1a4340c1c41622b90a918bcec0e188 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:19:17 +0200 Subject: [PATCH 04/22] view single unikernel --- unikernel.ml | 69 +++++++++++++++++++++++++++++++++++++++++---- unikernel_single.ml | 33 +++++++++++++--------- 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/unikernel.ml b/unikernel.ml index 0cfeb2d1..3fb6c97b 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -142,14 +142,71 @@ struct | path when String.(length path >= 16 && sub path 0 16 = "/unikernel/info/") -> ( +let request = Httpaf.Reqd.request reqd in + match request.meth with + | `GET -> + (let now = Ptime.v (P.now_d_ps ()) in + let _, (t : Storage.t) = !store in + let users = User_model.create_user_session_map t.users in + let middlewares = + [ + (* Middleware.email_verified_middleware now u:sers;*) + Middleware.auth_middleware now users; + ] + in (* TODO: middleware, extract domain from middleware *) - let unikernel_name = String.sub path 16 (String.length path - 16) in - Albatross.query !albatross (`Unikernel_cmd `Unikernel_info) - ~domain:"robur" ~name:unikernel_name +Middleware.apply_middleware middlewares (fun _reqd -> + match Middleware.user_of_cookie users now reqd with + | Ok user -> + let unikernel_name = +String.sub path 16 (String.length path - 16) +in +( Albatross.query !albatross +(`Unikernel_cmd `Unikernel_info) + ~domain:user.name (* TODO use uuid in the future *) +~name:unikernel_name >|= function - | Error () -> reply "error while querying albatross" - | Ok None -> reply "got none" - | Ok (Some (_hdr, res)) -> reply_json (Albatross_json.res res)) + | Error () -> + Logs.err (fun m -> + m "error while communicating with albatross"); + [] + | Ok None -> [] + | Ok + (Some + (_hdr, `Success (`Unikernel_info unikernel))) -> + unikernel + | Ok (Some reply) -> + Logs.err (fun m -> + m + "expected a unikernel info reply, received \ + %a" + (Vmm_commands.pp_wire ~verbose:false) + reply); + []) + >>= fun unikernels -> + Lwt.return + (reply ~content_type:"text/html" + (Dashboard.dashboard_layout + ~content: + (Unikernel_single.unikernel_single_layout + (List.hd unikernels) now) + ~icon:"/images/robur.png" ())) + | Error _ -> + Logs.err (fun m -> m "couldn't find user of cookie"); + assert false)) + reqd + | _ -> + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = "Bad request HTTP method"; + success = false; + } + in + Lwt.return + (reply ~content_type:"application/json" + (Utils.Status.to_json status))) | "/main.js" -> Lwt.return (reply ~content_type:"text/plain" js_file) | "/images/molly_bird.jpeg" -> Lwt.return (reply ~content_type:"image/jpeg" imgs.molly_img) diff --git a/unikernel_single.ml b/unikernel_single.ml index ed84839b..cdc56a83 100644 --- a/unikernel_single.ml +++ b/unikernel_single.ml @@ -1,4 +1,5 @@ -let unikernel_single_layout unikernel = +let unikernel_single_layout unikernel now = + let name, data = unikernel in Tyxml_html.( section ~a:[ a_class [ "col-span-10 p-4 bg-gray-50 my-1" ] ] @@ -14,20 +15,24 @@ let unikernel_single_layout unikernel = [ div [ - h2 [ txt "ExampleUnikernel" ]; + h2 [ txt (Vmm_core.Name.to_string name) ]; p [ txt - (Cstruct.to_string - unikernel.Vmm_core.Unikernel.digest); + (Utils.TimeHelper.time_ago now + data.Vmm_core.Unikernel.started); ]; + p [ txt (Cstruct.to_string data.digest) ]; ]; div [ button ~a: [ - a_onclick "destroyUnikernel()"; + a_onclick + ("destroyUnikernel('" + ^ Vmm_core.Name.to_string name + ^ "')"); a_class [ "my-3 py-2 px-3 rounded bg-secondary-500 \ @@ -61,7 +66,7 @@ let unikernel_single_layout unikernel = ]; p ~a:[ a_class [ "text-md" ] ] - [ txt (string_of_int unikernel.cpuid) ]; + [ txt (string_of_int data.cpuid) ]; ]; div ~a:[ a_class [ "p-4 rounded border border-primary-700" ] ] @@ -83,7 +88,7 @@ let unikernel_single_layout unikernel = ]; p ~a:[ a_class [ "text-md" ] ] - [ txt (string_of_int unikernel.memory) ]; + [ txt (string_of_int data.memory) ]; ]; div ~a:[ a_class [ "p-4 rounded border border-primary-700" ] ] @@ -105,7 +110,7 @@ let unikernel_single_layout unikernel = ]; p ~a:[ a_class [ "text-md" ] ] - [ txt (match unikernel.typ with `Solo5 -> "Solo5") ]; + [ txt (match data.typ with `Solo5 -> "Solo5") ]; ]; ]; div @@ -206,7 +211,7 @@ let unikernel_single_layout unikernel = ^ "MB"); ]; ]) - unikernel.block_devices); + data.block_devices); ]; div ~a:[ a_class [ "my-4" ] ] @@ -289,7 +294,7 @@ let unikernel_single_layout unikernel = "00-00-00-00-00-00"))); ]; ]) - unikernel.bridges); + data.bridges); ]; div ~a:[ a_class [ "my-4" ] ] @@ -297,14 +302,14 @@ let unikernel_single_layout unikernel = p ~a:[ a_class [ "text-xl font-semibold" ] ] [ txt "Fail Behaviour" ]; - (match unikernel.fail_behaviour with + (match data.fail_behaviour with | `Quit -> p [ txt "Quit" ] | `Restart None -> p [ txt "Restart" ] | `Restart (Some _codes) -> - p [txt ""] + p [ txt "" ] (* List.map - (fun code -> p [ txt (string_of_int code) ]) - (Vmm_core.IS.elements codes); *) ) + (fun code -> p [ txt (string_of_int code) ]) + (Vmm_core.IS.elements codes); *)); ]; ]; ]; From 18c98a774f9b9ffea5eb7770950a0a83eb91cfbd Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:20:25 +0200 Subject: [PATCH 05/22] add middleware and formatted responses --- unikernel.ml | 277 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 169 insertions(+), 108 deletions(-) diff --git a/unikernel.ml b/unikernel.ml index 3fb6c97b..31c75983 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -142,7 +142,7 @@ struct | path when String.(length path >= 16 && sub path 0 16 = "/unikernel/info/") -> ( -let request = Httpaf.Reqd.request reqd in + let request = Httpaf.Reqd.request reqd in match request.meth with | `GET -> (let now = Ptime.v (P.now_d_ps ()) in @@ -154,19 +154,19 @@ let request = Httpaf.Reqd.request reqd in Middleware.auth_middleware now users; ] in - (* TODO: middleware, extract domain from middleware *) -Middleware.apply_middleware middlewares (fun _reqd -> + (* TODO: middleware, extract domain from middleware *) + Middleware.apply_middleware middlewares (fun _reqd -> match Middleware.user_of_cookie users now reqd with | Ok user -> - let unikernel_name = -String.sub path 16 (String.length path - 16) -in -( Albatross.query !albatross -(`Unikernel_cmd `Unikernel_info) - ~domain:user.name (* TODO use uuid in the future *) -~name:unikernel_name - >|= function - | Error () -> + let unikernel_name = + String.sub path 16 (String.length path - 16) + in + (Albatross.query !albatross + (`Unikernel_cmd `Unikernel_info) + ~domain:user.name (* TODO use uuid in the future *) + ~name:unikernel_name + >|= function + | Error () -> Logs.err (fun m -> m "error while communicating with albatross"); [] @@ -832,7 +832,7 @@ in reqd | path when String.( - length path >= 20 && sub path 0 20 = "/unikernel/shutdown/") + length path >= 20 && sub path 0 20 = "/unikernel/destroy/") -> ( let unikernel_name = String.sub path 20 (String.length path - 20) in (* TODO: middleware, extract domain from middleware *) @@ -859,111 +859,172 @@ in reply_json (`List data); Lwt.return_unit | "/unikernel/create" -> ( - let response_body = Httpaf.Reqd.request_body reqd in - let finished, notify_finished = Lwt.wait () in - let wakeup v = Lwt.wakeup_later notify_finished v in - let on_eof data () = wakeup data in - let f acc s = acc ^ s in - let rec on_read on_eof acc bs ~off ~len = - let str = Bigstringaf.substring ~off ~len bs in - let acc = acc >>= fun acc -> Lwt.return (f acc str) in - Httpaf.Body.schedule_read response_body - ~on_read:(on_read on_eof acc) ~on_eof:(on_eof acc) - in - let f_init = Lwt.return "" in - Httpaf.Body.schedule_read response_body - ~on_read:(on_read on_eof f_init) ~on_eof:(on_eof f_init); - finished >>= fun data -> - data >>= fun data -> - let content_type = - Httpaf.( - Headers.get_exn (Reqd.request reqd).Request.headers - "content-type") - in - let ct = - Multipart_form.Content_type.of_string (content_type ^ "\r\n") - in - match ct with - | Error (`Msg msg) -> - Logs.warn (fun m -> m "couldn't content-type: %s" msg); - Lwt.return (reply "couldn't content-type") - | Ok ct -> ( - match Multipart_form.of_string_to_list data ct with - | Error (`Msg msg) -> - Logs.warn (fun m -> m "couldn't multipart: %s" msg); - Lwt.return (reply ("couldn't multipart: " ^ msg)) - | Ok (m, assoc) -> ( - let m, _r = to_map ~assoc m in - match - ( Map.find_opt "arguments" m, - Map.find_opt "name" m, - Map.find_opt "binary" m ) - with - | Some (_, args), Some (_, name), Some (_, binary) -> ( - Logs.info (fun m -> m "args %s" args); - match Albatross_json.config_of_json args with - | Ok cfg -> ( - let config = - { cfg with image = Cstruct.of_string binary } - in - (* TODO: middleware, extract domain from middleware *) - Albatross.query !albatross ~domain:"robur" ~name - (`Unikernel_cmd (`Unikernel_create config)) - >|= function - | Error () -> - Logs.warn (fun m -> - m "error querying albatross"); - let status = - { - Utils.Status.code = 500; - title = "Error"; - data = "Error while querying Albatross."; - success = false; - } - in - reply ~content_type:"application/json" - (Utils.Status.to_json status) - | Ok None -> - Logs.warn (fun m -> m "got none"); - let status = - { - Utils.Status.code = 200; - title = "Success"; - data = "Got none"; - success = true; - } - in - reply ~content_type:"application/json" - (Utils.Status.to_json status) - | Ok (Some (_hdr, res)) -> - let status = - { - Utils.Status.code = 200; - title = "Success"; - data = - Yojson.Safe.to_string - (Albatross_json.res res); - success = true; - } - in - reply ~content_type:"application/json" - (Utils.Status.to_json status)) + let request = Httpaf.Reqd.request reqd in + match request.meth with + | `POST -> + let now = Ptime.v (P.now_d_ps ()) in + let _, (t : Storage.t) = !store in + let users = User_model.create_user_session_map t.users in + let middlewares = [ Middleware.auth_middleware now users ] in + Middleware.apply_middleware middlewares + (fun _reqd -> + let response_body = Httpaf.Reqd.request_body reqd in + let finished, notify_finished = Lwt.wait () in + let wakeup v = Lwt.wakeup_later notify_finished v in + let on_eof data () = wakeup data in + let f acc s = acc ^ s in + let rec on_read on_eof acc bs ~off ~len = + let str = Bigstringaf.substring ~off ~len bs in + let acc = acc >>= fun acc -> Lwt.return (f acc str) in + Httpaf.Body.schedule_read response_body + ~on_read:(on_read on_eof acc) ~on_eof:(on_eof acc) + in + let f_init = Lwt.return "" in + Httpaf.Body.schedule_read response_body + ~on_read:(on_read on_eof f_init) ~on_eof:(on_eof f_init); + finished >>= fun data -> + data >>= fun data -> + let content_type = + Httpaf.( + Headers.get_exn (Reqd.request reqd).Request.headers + "content-type") + in + let ct = + Multipart_form.Content_type.of_string + (content_type ^ "\r\n") + in + match ct with + | Error (`Msg msg) -> + Logs.warn (fun m -> m "couldn't content-type: %s" msg); + let status = + { + Utils.Status.code = 403; + title = "Error"; + data = "Couldn't content-type: " ^ msg; + success = false; + } + in + Lwt.return + (reply ~content_type:"application/json" + (Utils.Status.to_json status)) + | Ok ct -> ( + match Multipart_form.of_string_to_list data ct with | Error (`Msg msg) -> - Logs.warn (fun m -> m "couldn't decode data %s" msg); + Logs.warn (fun m -> m "couldn't multipart: %s" msg); let status = { Utils.Status.code = 403; title = "Error"; - data = msg; + data = "Couldn't multipart: " ^ msg; success = false; } in Lwt.return (reply ~content_type:"application/json" - (Utils.Status.to_json status))) - | _ -> - Logs.warn (fun m -> m "couldn't find fields"); - Lwt.return (reply "couldn't find fields")))) + (Utils.Status.to_json status)) + | Ok (m, assoc) -> ( + let m, _r = to_map ~assoc m in + match + ( Map.find_opt "arguments" m, + Map.find_opt "name" m, + Map.find_opt "binary" m ) + with + | Some (_, args), Some (_, name), Some (_, binary) + -> ( + Logs.info (fun m -> m "args %s" args); + match Albatross_json.config_of_json args with + | Ok cfg -> ( + let config = + { + cfg with + image = Cstruct.of_string binary; + } + in + (* TODO: middleware, extract domain from middleware *) + Albatross.query !albatross ~domain:"robur" + ~name + (`Unikernel_cmd + (`Unikernel_create config)) + >|= function + | Error () -> + Logs.warn (fun m -> + m "error querying albatross"); + let status = + { + Utils.Status.code = 500; + title = "Error"; + data = + "Error while querying Albatross."; + success = false; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) + | Ok None -> + Logs.warn (fun m -> m "got none"); + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = "Got none"; + success = true; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) + | Ok (Some (_hdr, res)) -> + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = + Yojson.Safe.to_string + (Albatross_json.res res); + success = true; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status)) + | Error (`Msg msg) -> + Logs.warn (fun m -> + m "couldn't decode data %s" msg); + let status = + { + Utils.Status.code = 403; + title = "Error"; + data = msg; + success = false; + } + in + Lwt.return + (reply ~content_type:"application/json" + (Utils.Status.to_json status))) + | _ -> + Logs.warn (fun m -> m "couldn't find fields"); + let status = + { + Utils.Status.code = 403; + title = "Error"; + data = "Couldn't find fields"; + success = false; + } + in + Lwt.return + (reply ~content_type:"application/json" + (Utils.Status.to_json status))))) + reqd + | _ -> + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = "Bad request HTTP method"; + success = false; + } + in + Lwt.return + (reply ~content_type:"application/json" + (Utils.Status.to_json status))) | _ -> ( let now = Ptime.v (P.now_d_ps ()) in let _, (t : Storage.t) = !store in From 6e1210a36a63511ca35243e2b559ee67288487c0 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:20:43 +0200 Subject: [PATCH 06/22] destroy unikernels --- assets/main.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/assets/main.js b/assets/main.js index 95b546cf..1768aef8 100644 --- a/assets/main.js +++ b/assets/main.js @@ -154,3 +154,23 @@ async function deployUnikernel() { } } } + +async function destroyUnikernel(name) { + try { + const response = await fetch(`/unikernel/destroy/${name}`, { + method: 'GET', + mode: "no-cors" + }) + const data = await response.json(); + if (data.status === 200) { + postAlert("bg-primary-300", `Successful: ${data.data}`); + setTimeout(function () { + window.location.href = "/dashboard"; + }, 2000); + } else { + postAlert("bg-secondary-300", data.data); + } + } catch (error) { + postAlert("bg-secondary-300", error); + } +} From 53ef6ee7960215ee432dcc128562d23adebf7123 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:20:51 +0200 Subject: [PATCH 07/22] update styles --- assets/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/style.css b/assets/style.css index d6a3e668..d90e6c5d 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}.top-0{top:0}.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-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.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}.mb-2{margin-bottom:.5rem}.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-\[150\%\]{height:150%}.h-full{height:100%}.h-px{height:1px}.h-60{height:15rem}.h-96{height:24rem}.h-5{height:1.25rem}.min-h-screen{min-height:100vh}.w-0{width:0}.w-14{width:3.5rem}.w-16{width:4rem}.w-32{width:8rem}.w-\[150\%\]{width:150%}.w-full{width:100%}.w-max{width:-moz-max-content;width:max-content}.w-px{width:1px}.w-min{width:-moz-min-content;width:min-content}.w-5{width:1.25rem}.min-w-full{min-width:100%}.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}.grow{flex-grow:1}.table-auto{table-layout:auto}.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}.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))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.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-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-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))}.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-10>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2.5rem*var(--tw-space-x-reverse));margin-left:calc(2.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-16>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(4rem*var(--tw-space-x-reverse));margin-left:calc(4rem*(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-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*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-nowrap{white-space:nowrap}.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-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-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-200{--tw-bg-opacity:1;background-color:rgb(171 228 214/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-700{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.bg-secondary-50{--tw-bg-opacity:1;background-color:rgb(255 243 241/var(--tw-bg-opacity))}.bg-secondary-500{--tw-bg-opacity:1;background-color:rgb(255 78 51/var(--tw-bg-opacity))}.bg-primary-100{--tw-bg-opacity:1;background-color:rgb(213 242 235/var(--tw-bg-opacity))}.bg-secondary-100{--tw-bg-opacity:1;background-color:rgb(255 227 223/var(--tw-bg-opacity))}.bg-primary-900{--tw-bg-opacity:1;background-color:rgb(31 66 62/var(--tw-bg-opacity))}.bg-secondary-200{--tw-bg-opacity:1;background-color:rgb(255 205 197/var(--tw-bg-opacity))}.bg-secondary-800{--tw-bg-opacity:1;background-color:rgb(165 35 15/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-primary-800{--tw-gradient-from:#204f4a var(--tw-gradient-from-position);--tw-gradient-to:#204f4a00 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-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-16{padding:4rem}.p-2{padding:.5rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-10{padding:2.5rem}.p-0{padding:0}.p-20{padding: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}.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-20{padding-top:5rem;padding-bottom:5rem}.pb-12{padding-bottom:3rem}.pb-16{padding-bottom:4rem}.pb-3{padding-bottom:.75rem}.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}.text-left{text-align:left}.text-center{text-align:center}.text-start{text-align:start}.align-middle{vertical-align:middle}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.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}.text-4xl{font-size:2.25rem;line-height:2.5rem}.font-\[450\]{font-weight:450}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.tracking-wider{letter-spacing:.05em}.tracking-tight{letter-spacing:-.025em}.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-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-secondary-500{--tw-text-opacity:1;color:rgb(255 78 51/var(--tw-text-opacity))}.text-primary-300{--tw-text-opacity:1;color:rgb(122 206 189/var(--tw-text-opacity))}.text-secondary-300{--tw-text-opacity:1;color:rgb(255 170 157/var(--tw-text-opacity))}.text-secondary-800{--tw-text-opacity:1;color:rgb(165 35 15/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\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.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-opacity-50:hover{--tw-bg-opacity:0.5}.hover\:font-bold:hover{font-weight:700}.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\: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\: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-max{max-width:-moz-max-content;max-width:max-content}.lg\:max-w-xl{max-width:36rem}.lg\:max-w-7xl{max-width:80rem}.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-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}.h-screen{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-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}.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-col{flex-direction:column}.place-items-center{place-items:center}.items-start{align-items:flex-start}.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-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-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-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/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-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-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.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-700{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.bg-transparent{background-color:initial}.bg-primary-900{--tw-bg-opacity:1;background-color:rgb(31 66 62/var(--tw-bg-opacity))}.bg-secondary-500{--tw-bg-opacity:1;background-color:rgb(255 78 51/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}.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}.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-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}.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}.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}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.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-secondary-300{--tw-text-opacity:1;color:rgb(255 170 157/var(--tw-text-opacity))}.text-secondary-500{--tw-text-opacity:1;color:rgb(255 78 51/var(--tw-text-opacity))}.text-primary-700{--tw-text-opacity:1;color:rgb(35 98 90/var(--tw-text-opacity))}.text-secondary-50{--tw-text-opacity:1;color:rgb(255 243 241/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\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.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-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\: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\: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 35f3eee49d1f0daadff837ef2420976f33ac187f Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:20:57 +0200 Subject: [PATCH 08/22] lint --- unikernel_create.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unikernel_create.ml b/unikernel_create.ml index 13c710c8..a5c2d1a8 100644 --- a/unikernel_create.ml +++ b/unikernel_create.ml @@ -25,7 +25,7 @@ let unikernel_create_layout = [ a_input_type `Text; a_name "name"; - a_required () ; + a_required (); a_id "unikernel-name"; a_class [ @@ -51,7 +51,7 @@ let unikernel_create_layout = ~a: [ a_rows 4; - a_required () ; + a_required (); a_name "arguments"; a_id "unikernel-arguments"; a_class @@ -79,7 +79,7 @@ let unikernel_create_layout = [ a_input_type `File; a_name "binary"; - a_required () ; + a_required (); a_id "unikernel-binary"; a_class [ From 81ef0099e72207d32200109b25a1fbc4c1c22a3d Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:28:31 +0200 Subject: [PATCH 09/22] add middleware to destroy endpoint --- unikernel.ml | 69 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/unikernel.ml b/unikernel.ml index 31c75983..d925028c 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -834,14 +834,67 @@ struct when String.( length path >= 20 && sub path 0 20 = "/unikernel/destroy/") -> ( - let unikernel_name = String.sub path 20 (String.length path - 20) in - (* TODO: middleware, extract domain from middleware *) - Albatross.query !albatross ~domain:"robur" - (`Unikernel_cmd `Unikernel_destroy) ~name:unikernel_name - >|= function - | Error () -> reply "error while querying albatross" - | Ok None -> reply "got none" - | Ok (Some (_hdr, res)) -> reply_json (Albatross_json.res res)) + let request = Httpaf.Reqd.request reqd in + match request.meth with + | `GET -> + let now = Ptime.v (P.now_d_ps ()) in + let _, (t : Storage.t) = !store in + let users = User_model.create_user_session_map t.users in + let middlewares = [ Middleware.auth_middleware now users ] in + Middleware.apply_middleware middlewares + (fun _reqd -> + match Middleware.user_of_cookie users now reqd with + | Ok user -> ( + let unikernel_name = + String.sub path 20 (String.length path - 20) + in + (* TODO: middleware, extract domain from middleware *) + Albatross.query !albatross ~domain:user.name + (`Unikernel_cmd `Unikernel_destroy) + ~name:unikernel_name + >|= function + | Error () -> + Logs.err (fun m -> m "Error querying albatross"); + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = "Error querying albatross"; + success = false; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) + | Ok None -> + Logs.err (fun m -> m "got none"); + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = "Got none"; + success = false; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) + | Ok (Some (_hdr, res)) -> + reply_json (Albatross_json.res res)) + | Error _ -> + Logs.err (fun m -> m "couldn't find user of cookie"); + assert false) + reqd + | _ -> + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = "Bad request HTTP method"; + success = false; + } + in + Lwt.return + (reply ~content_type:"application/json" + (Utils.Status.to_json status))) | path when String.( length path >= 19 && sub path 0 19 = "/unikernel/console/") -> From b6c785433d449c7b70ee634677df7ebc7b266c43 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:31:21 +0200 Subject: [PATCH 10/22] update todo --- TODO | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TODO b/TODO index cf1b326a..cd278a19 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,11 @@ ## 20240813 - [x] style for the dashboard (unikernel info) -- TODO unikernel detailed information (/unikernel/info/) - displayed -- TODO shutdown and restart of a unikernel -- TODO deploy/create a new unikernel - +- [x] unikernel detailed information (/unikernel/info/) - displayed +- [x] destroy of a unikernel +- [x] deploy/create a new unikernels - [x] store configuration data on block device (PR pending) - [x] have an HTTP endpoint for editing / uploading the configuration +- TODO Shutdown and restart a unikernel - TODO we should remove all unauthenticated endpoints (comment out) @@ -26,4 +26,4 @@ - TODO email verification (sending emails) - TODO deploy unikernels via GitHub action (Bearer token etc.) -- TODO REST API - rely on bearer tokens +- TODO REST API - rely on bearer tokenss From ac07fabd66c654fe931511e8b57c5dccbc84fac2 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:51:30 +0200 Subject: [PATCH 11/22] hide toast alert till activated --- assets/main.js | 1 + dashboard.ml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/main.js b/assets/main.js index 1768aef8..fee6d11a 100644 --- a/assets/main.js +++ b/assets/main.js @@ -99,6 +99,7 @@ function closeBanner() { function postAlert(bg_color, content) { const alertContainer = document.getElementById("alert-container"); + alertContainer.classList.add("block") const alert = document.createElement("div"); alert.className = `text-white transition ease-in-out delay-150 duration-300 ${bg_color}`; alert.textContent = content; diff --git a/dashboard.ml b/dashboard.ml index eac463fa..b032bbed 100644 --- a/dashboard.ml +++ b/dashboard.ml @@ -176,7 +176,7 @@ let dashboard_layout ~icon ?(page_title = "Dashboard | Mollymawk") a_class [ "absolute top-1/4 rounded-md right-4 z-50 w-fit \ - space-y-2 p-4 shadow border text-wrap"; + space-y-2 p-4 shadow border text-wrap hidden"; ]; ] []; From 60452a745471e71328cd169bdca746869cc22679 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 10:58:11 +0200 Subject: [PATCH 12/22] update styles for post alert --- assets/main.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/main.js b/assets/main.js index fee6d11a..e165c93a 100644 --- a/assets/main.js +++ b/assets/main.js @@ -99,12 +99,15 @@ function closeBanner() { function postAlert(bg_color, content) { const alertContainer = document.getElementById("alert-container"); - alertContainer.classList.add("block") + alertContainer.classList.remove("hidden") + alertContainer.classList.add("block", `${bg_color}`, "text-white", "transition", "ease-in-out", "delay-150", "duration-300") const alert = document.createElement("div"); alert.className = `text-white transition ease-in-out delay-150 duration-300 ${bg_color}`; alert.textContent = content; alertContainer.appendChild(alert); setTimeout(() => { + alertContainer.classList.remove("block", `${bg_color}`) + alertContainer.classList.add("hidden") alertContainer.removeChild(alert); }, 1600); } From 1185c444caa5aa288b1482634fa5f22f71efdabb Mon Sep 17 00:00:00 2001 From: PixieDust <111846546+PizieDust@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:24:30 +0200 Subject: [PATCH 13/22] Update unikernel.ml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Reynir Björnsson --- unikernel.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unikernel.ml b/unikernel.ml index d925028c..784e2175 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -948,7 +948,7 @@ struct in match ct with | Error (`Msg msg) -> - Logs.warn (fun m -> m "couldn't content-type: %s" msg); + Logs.warn (fun m -> m "couldn't content-type: %S" msg); let status = { Utils.Status.code = 403; From a43ece7940a998b7cd6a461d8252ab3d46b192e2 Mon Sep 17 00:00:00 2001 From: PixieDust <111846546+PizieDust@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:24:50 +0200 Subject: [PATCH 14/22] Update unikernel.ml Co-authored-by: Hannes Mehnert --- unikernel.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/unikernel.ml b/unikernel.ml index 784e2175..9f048f8c 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -848,7 +848,6 @@ struct let unikernel_name = String.sub path 20 (String.length path - 20) in - (* TODO: middleware, extract domain from middleware *) Albatross.query !albatross ~domain:user.name (`Unikernel_cmd `Unikernel_destroy) ~name:unikernel_name From b797137b9dd32dc5cd88948c9156cf79496992f4 Mon Sep 17 00:00:00 2001 From: PixieDust <111846546+PizieDust@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:24:57 +0200 Subject: [PATCH 15/22] Update unikernel.ml Co-authored-by: Hannes Mehnert --- unikernel.ml | 1 - 1 file changed, 1 deletion(-) diff --git a/unikernel.ml b/unikernel.ml index 9f048f8c..a35a6fd7 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -154,7 +154,6 @@ struct Middleware.auth_middleware now users; ] in - (* TODO: middleware, extract domain from middleware *) Middleware.apply_middleware middlewares (fun _reqd -> match Middleware.user_of_cookie users now reqd with | Ok user -> From 79f7edc65470d306f2ee14751d776bd48f75e197 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 16:27:36 +0200 Subject: [PATCH 16/22] minor changes --- assets/style.css | 2 +- unikernel.ml | 20 +++- unikernel_single.ml | 231 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 196 insertions(+), 57 deletions(-) diff --git a/assets/style.css b/assets/style.css index d90e6c5d..a6dbdc9f 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-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}.h-screen{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-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}.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-col{flex-direction:column}.place-items-center{place-items:center}.items-start{align-items:flex-start}.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-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-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-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/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-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-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity))}.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-700{--tw-bg-opacity:1;background-color:rgb(200 38 13/var(--tw-bg-opacity))}.bg-transparent{background-color:initial}.bg-primary-900{--tw-bg-opacity:1;background-color:rgb(31 66 62/var(--tw-bg-opacity))}.bg-secondary-500{--tw-bg-opacity:1;background-color:rgb(255 78 51/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}.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}.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-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}.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}.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}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.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-secondary-300{--tw-text-opacity:1;color:rgb(255 170 157/var(--tw-text-opacity))}.text-secondary-500{--tw-text-opacity:1;color:rgb(255 78 51/var(--tw-text-opacity))}.text-primary-700{--tw-text-opacity:1;color:rgb(35 98 90/var(--tw-text-opacity))}.text-secondary-50{--tw-text-opacity:1;color:rgb(255 243 241/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\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.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-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\: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\: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-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}.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-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}.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-col{flex-direction:column}.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-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-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}.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-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}.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}.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}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.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-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\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.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-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\: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\: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/unikernel.ml b/unikernel.ml index d925028c..1f7d52ca 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -872,13 +872,23 @@ struct Utils.Status.code = 200; title = "Success"; data = "Got none"; - success = false; + success = true; } in reply ~content_type:"application/json" (Utils.Status.to_json status) | Ok (Some (_hdr, res)) -> - reply_json (Albatross_json.res res)) + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = + Yojson.Safe.to_string (Albatross_json.res res); + success = true; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status)) | Error _ -> Logs.err (fun m -> m "couldn't find user of cookie"); assert false) @@ -951,7 +961,7 @@ struct Logs.warn (fun m -> m "couldn't content-type: %s" msg); let status = { - Utils.Status.code = 403; + Utils.Status.code = 400; title = "Error"; data = "Couldn't content-type: " ^ msg; success = false; @@ -966,7 +976,7 @@ struct Logs.warn (fun m -> m "couldn't multipart: %s" msg); let status = { - Utils.Status.code = 403; + Utils.Status.code = 400; title = "Error"; data = "Couldn't multipart: " ^ msg; success = false; @@ -1043,7 +1053,7 @@ struct m "couldn't decode data %s" msg); let status = { - Utils.Status.code = 403; + Utils.Status.code = 400; title = "Error"; data = msg; success = false; diff --git a/unikernel_single.ml b/unikernel_single.ml index cdc56a83..5d2c044d 100644 --- a/unikernel_single.ml +++ b/unikernel_single.ml @@ -1,5 +1,5 @@ let unikernel_single_layout unikernel now = - let name, data = unikernel in + let u_name, data = unikernel in Tyxml_html.( section ~a:[ a_class [ "col-span-10 p-4 bg-gray-50 my-1" ] ] @@ -15,14 +15,24 @@ let unikernel_single_layout unikernel now = [ div [ - h2 [ txt (Vmm_core.Name.to_string name) ]; - p + div + ~a:[ a_class [ "flex space-x-2 items-end" ] ] [ - txt - (Utils.TimeHelper.time_ago now - data.Vmm_core.Unikernel.started); + h2 + ~a:[ a_class [ "text-xl font-bold uppercase" ] ] + [ txt (Vmm_core.Name.to_string u_name) ]; + p + ~a:[ a_class [ "text-sm" ] ] + [ + txt + ("created " + ^ Utils.TimeHelper.time_ago now + data.Vmm_core.Unikernel.started); + ]; ]; - p [ txt (Cstruct.to_string data.digest) ]; + p + ~a:[ a_class [ "text-sm" ] ] + [ txt (Cstruct.to_hex_string data.digest) ]; ]; div [ @@ -31,7 +41,7 @@ let unikernel_single_layout unikernel now = [ a_onclick ("destroyUnikernel('" - ^ Vmm_core.Name.to_string name + ^ Vmm_core.Name.to_string u_name ^ "')"); a_class [ @@ -65,7 +75,7 @@ let unikernel_single_layout unikernel now = p ~a:[ a_class [ "text-md" ] ] [ txt "CPU" ]; ]; p - ~a:[ a_class [ "text-md" ] ] + ~a:[ a_class [ "text-3xl text-right" ] ] [ txt (string_of_int data.cpuid) ]; ]; div @@ -87,8 +97,8 @@ let unikernel_single_layout unikernel now = p ~a:[ a_class [ "text-md" ] ] [ txt "Memory" ]; ]; p - ~a:[ a_class [ "text-md" ] ] - [ txt (string_of_int data.memory) ]; + ~a:[ a_class [ "text-3xl text-right" ] ] + [ txt (string_of_int data.memory ^ "MB") ]; ]; div ~a:[ a_class [ "p-4 rounded border border-primary-700" ] ] @@ -109,7 +119,7 @@ let unikernel_single_layout unikernel now = p ~a:[ a_class [ "text-md" ] ] [ txt "Type" ]; ]; p - ~a:[ a_class [ "text-md" ] ] + ~a:[ a_class [ "text-3xl text-right" ] ] [ txt (match data.typ with `Solo5 -> "Solo5") ]; ]; ]; @@ -131,6 +141,81 @@ let unikernel_single_layout unikernel now = p ~a:[ a_class [ "text-xl font-semibold" ] ] [ txt "Arguments" ]; + 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 "Key" ]; + th + ~a: + [ + a_class + [ + "px-6 py-2 text-start \ + text-xs font-bold \ + text-primary-600 uppercase"; + ]; + ] + [ txt "Value" ]; + ]; + ]) + (List.map + (fun arg -> + match String.split_on_char '=' arg with + | [ key; value ] -> + tr + [ + td + ~a: + [ + a_class + [ + "px-6 py-1 \ + whitespace-nowrap \ + text-sm font-medium \ + text-gray-800"; + ]; + ] + [ + txt + (String.sub key 2 + (String.length key - 2)); + ]; + td + ~a: + [ + a_class + [ + "px-6 py-1 \ + whitespace-normal \ + text-sm font-medium \ + text-gray-800"; + ]; + ] + [ txt value ]; + ] + | _ -> tr []) + (Option.value data.argv ~default:[])); ]; div ~a:[ a_class [ "my-4" ] ] @@ -143,8 +228,8 @@ let unikernel_single_layout unikernel now = [ a_class [ - "items-center bg-transparent w-full \ - border-collapse"; + "table-auto min-w-full divide-y \ + divide-gray-200"; ]; ] ~thead: @@ -157,10 +242,9 @@ let unikernel_single_layout unikernel now = [ a_class [ - "border-t-0 px-6 \ - align-middle border-l-0 \ - border-r-0 text-md \ - whitespace-nowrap p-4"; + "px-6 py-2 text-start \ + text-xs font-bold \ + text-primary-600 uppercase"; ]; ] [ txt "Host device" ]; @@ -169,10 +253,9 @@ let unikernel_single_layout unikernel now = [ a_class [ - "border-t-0 px-6 \ - align-middle border-l-0 \ - border-r-0 text-md \ - whitespace-nowrap p-4"; + "px-6 py-2 text-start \ + text-xs font-bold \ + text-primary-600 uppercase"; ]; ] [ txt "Name" ]; @@ -181,10 +264,9 @@ let unikernel_single_layout unikernel now = [ a_class [ - "border-t-0 px-6 \ - align-middle border-l-0 \ - border-r-0 text-md \ - whitespace-nowrap p-4"; + "px-6 py-2 text-start \ + text-xs font-bold \ + text-primary-600 uppercase"; ]; ] [ txt "Sector size" ]; @@ -195,19 +277,43 @@ let unikernel_single_layout unikernel now = tr [ td - ~a:[ a_class [ "text-center" ] ] + ~a: + [ + a_class + [ + "px-6 py-1 whitespace-nowrap \ + text-sm font-medium \ + text-gray-800"; + ]; + ] [ txt name ]; td - ~a:[ a_class [ "text-center" ] ] + ~a: + [ + a_class + [ + "px-6 py-1 whitespace-nowrap \ + text-sm font-medium \ + text-gray-800"; + ]; + ] [ txt (Option.value device ~default:""); ]; td - ~a:[ a_class [ "text-center" ] ] + ~a: + [ + a_class + [ + "px-6 py-1 whitespace-nowrap \ + text-sm font-medium \ + text-gray-800"; + ]; + ] [ txt (string_of_int - (Option.value size ~default:0) + (Option.value size ~default:(-1)) ^ "MB"); ]; ]) @@ -224,8 +330,8 @@ let unikernel_single_layout unikernel now = [ a_class [ - "items-center bg-transparent w-full \ - border-collapse"; + "table-auto min-w-full divide-y \ + divide-gray-200"; ]; ] ~thead: @@ -238,10 +344,9 @@ let unikernel_single_layout unikernel now = [ a_class [ - "border-t-0 px-6 \ - align-middle border-l-0 \ - border-r-0 text-md \ - whitespace-nowrap p-4"; + "px-6 py-2 text-start \ + text-xs font-bold \ + text-primary-600 uppercase"; ]; ] [ txt "Host device" ]; @@ -250,10 +355,9 @@ let unikernel_single_layout unikernel now = [ a_class [ - "border-t-0 px-6 \ - align-middle border-l-0 \ - border-r-0 text-md \ - whitespace-nowrap p-4"; + "px-6 py-2 text-start \ + text-xs font-bold \ + text-primary-600 uppercase"; ]; ] [ txt "Name" ]; @@ -262,13 +366,12 @@ let unikernel_single_layout unikernel now = [ a_class [ - "border-t-0 px-6 \ - align-middle border-l-0 \ - border-r-0 text-md \ - whitespace-nowrap p-4"; + "px-6 py-2 text-start \ + text-xs font-bold \ + text-primary-600 uppercase"; ]; ] - [ txt "Sector size" ]; + [ txt "MAC Address" ]; ]; ]) (List.map @@ -276,22 +379,48 @@ let unikernel_single_layout unikernel now = tr [ td - ~a:[ a_class [ "text-center" ] ] + ~a: + [ + a_class + [ + "px-6 py-1 whitespace-nowrap \ + text-sm font-medium \ + text-gray-800"; + ]; + ] [ txt name ]; td - ~a:[ a_class [ "text-center" ] ] + ~a: + [ + a_class + [ + "px-6 py-1 whitespace-nowrap \ + text-sm font-medium \ + text-gray-800"; + ]; + ] [ - txt (Option.value device ~default:""); + txt + (Option.value device ~default:name); ]; td - ~a:[ a_class [ "text-center" ] ] + ~a: + [ + a_class + [ + "px-6 py-1 whitespace-nowrap \ + text-sm font-medium \ + text-gray-800"; + ]; + ] [ txt (Macaddr.to_string (Option.value mac ~default: - (Macaddr.of_string_exn - "00-00-00-00-00-00"))); + (Vmm_core.Name.mac u_name + (Option.value device + ~default:name)))); ]; ]) data.bridges); From 730f95cd66169f9ad226fc834584e212fe3b525f Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 17:37:23 +0200 Subject: [PATCH 17/22] console output --- assets/main.js | 31 ++++ unikernel.ml | 336 ++++++++++++++++++++++++++------------------ unikernel_single.ml | 14 +- 3 files changed, 243 insertions(+), 138 deletions(-) diff --git a/assets/main.js b/assets/main.js index e165c93a..7ff88d16 100644 --- a/assets/main.js +++ b/assets/main.js @@ -1,5 +1,8 @@ document.addEventListener('DOMContentLoaded', function () { AOS.init(); + if (window.location.pathname.startsWith("/unikernel/info")) { + setInterval(getConsoleOutput, 300); + } }); function filterData() { @@ -178,3 +181,31 @@ async function destroyUnikernel(name) { postAlert("bg-secondary-300", error); } } + +function getConsoleOutput() { + const unikernel_name = document.getElementById("unikernel-name").textContent.trim(); + if (unikernel_name) { + const consoleDiv = document.getElementById("console-container"); + try { + const response = fetch(`/unikernel/console/${unikernel_name}`, { + method: 'GET', + mode: "no-cors" + }); + const data = response.json(); + + if (response.status === 200 && data.success) { + let output = ""; + data.forEach(item => { + output += `${item.timestamp} - ${item.line} \n`; + }); + consoleDiv.textContent = output; + console.log("Response:", data); + } else { + postAlert("bg-secondary-300", data.data); + } + } catch (error) { + postAlert("bg-secondary-300", error.message); + } + } +} + diff --git a/unikernel.ml b/unikernel.ml index 443230e0..2aee24bc 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -183,13 +183,29 @@ struct reply); []) >>= fun unikernels -> - Lwt.return - (reply ~content_type:"text/html" - (Dashboard.dashboard_layout - ~content: - (Unikernel_single.unikernel_single_layout - (List.hd unikernels) now) - ~icon:"/images/robur.png" ())) + if List.length unikernels > 0 then + Lwt.return + (reply ~content_type:"text/html" + (Dashboard.dashboard_layout + ~content: + (Unikernel_single.unikernel_single_layout + (List.hd unikernels) now) + ~icon:"/images/robur.png" ())) + else + let error = + { + Utils.Status.code = 404; + title = "An error occured"; + success = false; + data = "Error while fetching unikernel."; + } + in + Lwt.return + (reply ~content_type:"text/html" + (Dashboard.dashboard_layout + ~page_title:"404 | Mollymawk" + ~content:(Error_page.error_layout error) + ~icon:"/images/robur.png" ())) | Error _ -> Logs.err (fun m -> m "couldn't find user of cookie"); assert false)) @@ -905,20 +921,50 @@ struct (Utils.Status.to_json status))) | path when String.( - length path >= 19 && sub path 0 19 = "/unikernel/console/") -> - let unikernel_name = String.sub path 19 (String.length path - 19) in - (* TODO: middleware, extract domain from middleware *) - ( Albatross.query ~domain:"robur" !albatross - (`Console_cmd (`Console_subscribe (`Count 10))) - ~name:unikernel_name - >|= fun _ -> () ) - >>= fun () -> - let data = - Option.value ~default:[] - (Map.find_opt unikernel_name !albatross.Albatross.console_output) - in - reply_json (`List data); - Lwt.return_unit + length path >= 19 && sub path 0 19 = "/unikernel/console/") + -> ( + let request = Httpaf.Reqd.request reqd in + match request.meth with + | `GET -> + let now = Ptime.v (P.now_d_ps ()) in + let _, (t : Storage.t) = !store in + let users = User_model.create_user_session_map t.users in + let middlewares = [ Middleware.auth_middleware now users ] in + Middleware.apply_middleware middlewares + (fun _reqd -> + match Middleware.user_of_cookie users now reqd with + | Ok user -> + let unikernel_name = + String.sub path 19 (String.length path - 19) + in + ( Albatross.query ~domain:user.name !albatross + (`Console_cmd (`Console_subscribe (`Count 10))) + ~name:unikernel_name + >|= fun _ -> () ) + >>= fun () -> + let data = + Option.value ~default:[] + (Map.find_opt unikernel_name + !albatross.Albatross.console_output) + in + reply_json (`List data); + Lwt.return_unit + | Error _ -> + Logs.err (fun m -> m "couldn't find user of cookie"); + assert false) + reqd + | _ -> + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = "Bad request HTTP method"; + success = false; + } + in + Lwt.return + (reply ~content_type:"application/json" + (Utils.Status.to_json status))) | "/unikernel/create" -> ( let request = Httpaf.Reqd.request reqd in match request.meth with @@ -929,150 +975,166 @@ struct let middlewares = [ Middleware.auth_middleware now users ] in Middleware.apply_middleware middlewares (fun _reqd -> - let response_body = Httpaf.Reqd.request_body reqd in - let finished, notify_finished = Lwt.wait () in - let wakeup v = Lwt.wakeup_later notify_finished v in - let on_eof data () = wakeup data in - let f acc s = acc ^ s in - let rec on_read on_eof acc bs ~off ~len = - let str = Bigstringaf.substring ~off ~len bs in - let acc = acc >>= fun acc -> Lwt.return (f acc str) in - Httpaf.Body.schedule_read response_body - ~on_read:(on_read on_eof acc) ~on_eof:(on_eof acc) - in - let f_init = Lwt.return "" in - Httpaf.Body.schedule_read response_body - ~on_read:(on_read on_eof f_init) ~on_eof:(on_eof f_init); - finished >>= fun data -> - data >>= fun data -> - let content_type = - Httpaf.( - Headers.get_exn (Reqd.request reqd).Request.headers - "content-type") - in - let ct = - Multipart_form.Content_type.of_string - (content_type ^ "\r\n") - in - match ct with - | Error (`Msg msg) -> - Logs.warn (fun m -> m "couldn't content-type: %S" msg); - let status = - { - Utils.Status.code = 400; - title = "Error"; - data = "Couldn't content-type: " ^ msg; - success = false; - } + match Middleware.user_of_cookie users now reqd with + | Ok user -> ( + let response_body = Httpaf.Reqd.request_body reqd in + let finished, notify_finished = Lwt.wait () in + let wakeup v = Lwt.wakeup_later notify_finished v in + let on_eof data () = wakeup data in + let f acc s = acc ^ s in + let rec on_read on_eof acc bs ~off ~len = + let str = Bigstringaf.substring ~off ~len bs in + let acc = acc >>= fun acc -> Lwt.return (f acc str) in + Httpaf.Body.schedule_read response_body + ~on_read:(on_read on_eof acc) ~on_eof:(on_eof acc) in - Lwt.return - (reply ~content_type:"application/json" - (Utils.Status.to_json status)) - | Ok ct -> ( - match Multipart_form.of_string_to_list data ct with + let f_init = Lwt.return "" in + Httpaf.Body.schedule_read response_body + ~on_read:(on_read on_eof f_init) + ~on_eof:(on_eof f_init); + finished >>= fun data -> + data >>= fun data -> + let content_type = + Httpaf.( + Headers.get_exn (Reqd.request reqd).Request.headers + "content-type") + in + let ct = + Multipart_form.Content_type.of_string + (content_type ^ "\r\n") + in + match ct with | Error (`Msg msg) -> - Logs.warn (fun m -> m "couldn't multipart: %s" msg); + Logs.warn (fun m -> + m "couldn't content-type: %S" msg); let status = { Utils.Status.code = 400; title = "Error"; - data = "Couldn't multipart: " ^ msg; + data = "Couldn't content-type: " ^ msg; success = false; } in Lwt.return (reply ~content_type:"application/json" (Utils.Status.to_json status)) - | Ok (m, assoc) -> ( - let m, _r = to_map ~assoc m in - match - ( Map.find_opt "arguments" m, - Map.find_opt "name" m, - Map.find_opt "binary" m ) - with - | Some (_, args), Some (_, name), Some (_, binary) - -> ( - Logs.info (fun m -> m "args %s" args); - match Albatross_json.config_of_json args with - | Ok cfg -> ( - let config = - { - cfg with - image = Cstruct.of_string binary; - } - in - (* TODO: middleware, extract domain from middleware *) - Albatross.query !albatross ~domain:"robur" - ~name - (`Unikernel_cmd - (`Unikernel_create config)) - >|= function - | Error () -> - Logs.warn (fun m -> - m "error querying albatross"); - let status = - { - Utils.Status.code = 500; - title = "Error"; - data = - "Error while querying Albatross."; - success = false; - } - in - reply ~content_type:"application/json" - (Utils.Status.to_json status) - | Ok None -> - Logs.warn (fun m -> m "got none"); - let status = + | Ok ct -> ( + match Multipart_form.of_string_to_list data ct with + | Error (`Msg msg) -> + Logs.warn (fun m -> + m "couldn't multipart: %s" msg); + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = "Couldn't multipart: " ^ msg; + success = false; + } + in + Lwt.return + (reply ~content_type:"application/json" + (Utils.Status.to_json status)) + | Ok (m, assoc) -> ( + let m, _r = to_map ~assoc m in + match + ( Map.find_opt "arguments" m, + Map.find_opt "name" m, + Map.find_opt "binary" m ) + with + | ( Some (_, args), + Some (_, name), + Some (_, binary) ) -> ( + Logs.info (fun m -> m "args %s" args); + match + Albatross_json.config_of_json args + with + | Ok cfg -> ( + let config = { - Utils.Status.code = 200; - title = "Success"; - data = "Got none"; - success = true; + cfg with + image = Cstruct.of_string binary; } in - reply ~content_type:"application/json" - (Utils.Status.to_json status) - | Ok (Some (_hdr, res)) -> + Albatross.query !albatross + ~domain:user.name ~name + (`Unikernel_cmd + (`Unikernel_create config)) + >|= function + | Error () -> + Logs.warn (fun m -> + m "error querying albatross"); + let status = + { + Utils.Status.code = 500; + title = "Error"; + data = + "Error while querying \ + Albatross."; + success = false; + } + in + reply + ~content_type:"application/json" + (Utils.Status.to_json status) + | Ok None -> + Logs.warn (fun m -> m "got none"); + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = "Got none"; + success = true; + } + in + reply + ~content_type:"application/json" + (Utils.Status.to_json status) + | Ok (Some (_hdr, res)) -> + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = + Yojson.Safe.to_string + (Albatross_json.res res); + success = true; + } + in + reply + ~content_type:"application/json" + (Utils.Status.to_json status)) + | Error (`Msg msg) -> + Logs.warn (fun m -> + m "couldn't decode data %s" msg); let status = { - Utils.Status.code = 200; - title = "Success"; - data = - Yojson.Safe.to_string - (Albatross_json.res res); - success = true; + Utils.Status.code = 400; + title = "Error"; + data = msg; + success = false; } in - reply ~content_type:"application/json" - (Utils.Status.to_json status)) - | Error (`Msg msg) -> + Lwt.return + (reply + ~content_type:"application/json" + (Utils.Status.to_json status))) + | _ -> Logs.warn (fun m -> - m "couldn't decode data %s" msg); + m "couldn't find fields"); let status = { - Utils.Status.code = 400; + Utils.Status.code = 403; title = "Error"; - data = msg; + data = "Couldn't find fields"; success = false; } in Lwt.return (reply ~content_type:"application/json" - (Utils.Status.to_json status))) - | _ -> - Logs.warn (fun m -> m "couldn't find fields"); - let status = - { - Utils.Status.code = 403; - title = "Error"; - data = "Couldn't find fields"; - success = false; - } - in - Lwt.return - (reply ~content_type:"application/json" - (Utils.Status.to_json status))))) + (Utils.Status.to_json status))))) + | Error _ -> + Logs.err (fun m -> m "couldn't find user of cookie"); + assert false) reqd | _ -> let status = diff --git a/unikernel_single.ml b/unikernel_single.ml index 5d2c044d..fb0aaff9 100644 --- a/unikernel_single.ml +++ b/unikernel_single.ml @@ -19,7 +19,11 @@ let unikernel_single_layout unikernel now = ~a:[ a_class [ "flex space-x-2 items-end" ] ] [ h2 - ~a:[ a_class [ "text-xl font-bold uppercase" ] ] + ~a: + [ + a_id "unikernel-name"; + a_class [ "text-xl font-bold uppercase" ]; + ] [ txt (Vmm_core.Name.to_string u_name) ]; p ~a:[ a_class [ "text-sm" ] ] @@ -441,6 +445,14 @@ let unikernel_single_layout unikernel now = (Vmm_core.IS.elements codes); *)); ]; ]; + div + ~a:[ a_class [ "text-left p-4 overflow-hidden" ] ] + [ + p + ~a:[ a_class [ "text-xl font-semibold" ] ] + [ txt "Console Output" ]; + div ~a:[ a_id "console-container" ] []; + ]; ]; ]; ]; From 7a195fec3be89ad76ba192ab5fde752d0617f186 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 17:55:38 +0200 Subject: [PATCH 18/22] get unikernel name --- assets/main.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/assets/main.js b/assets/main.js index 7ff88d16..119524d9 100644 --- a/assets/main.js +++ b/assets/main.js @@ -182,8 +182,14 @@ async function destroyUnikernel(name) { } } -function getConsoleOutput() { - const unikernel_name = document.getElementById("unikernel-name").textContent.trim(); +function getUnikernelName(url) { + const urlObj = new URL(url); + const pathParts = urlObj.pathname.split('/'); + return pathParts[pathParts.length - 1]; +} + +async function getConsoleOutput() { + let unikernel_name = getUnikernelName(window.location.href); if (unikernel_name) { const consoleDiv = document.getElementById("console-container"); try { @@ -191,9 +197,9 @@ function getConsoleOutput() { method: 'GET', mode: "no-cors" }); - const data = response.json(); + const data = await response.json(); - if (response.status === 200 && data.success) { + if (response.status === 200) { let output = ""; data.forEach(item => { output += `${item.timestamp} - ${item.line} \n`; From 6185550453fbbc134ec207312c58392453276a78 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 18:00:51 +0200 Subject: [PATCH 19/22] rewrite getConsoleFunction --- assets/main.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/assets/main.js b/assets/main.js index 119524d9..3d821def 100644 --- a/assets/main.js +++ b/assets/main.js @@ -188,30 +188,30 @@ function getUnikernelName(url) { return pathParts[pathParts.length - 1]; } +var consoleArea = document.createElement("textarea"); async function getConsoleOutput() { let unikernel_name = getUnikernelName(window.location.href); if (unikernel_name) { const consoleDiv = document.getElementById("console-container"); - try { - const response = fetch(`/unikernel/console/${unikernel_name}`, { - method: 'GET', - mode: "no-cors" - }); - const data = await response.json(); + fetch("/unikernel/console/"+unikernel_name, { + method: "get", + }) + .then((response) => response.json()) + .then((responseText) => { + let data= "" + responseText.forEach(item => { + data += `${item.timestamp} - ${item.line} \n` + }) + consoleArea.textContent = "" + consoleArea.textContent = data + }) + .catch((error) => { + postAlert("bg-secondary-300", `${unikernel_name} ${error}`) + }) + .finally(() => { - if (response.status === 200) { - let output = ""; - data.forEach(item => { - output += `${item.timestamp} - ${item.line} \n`; - }); - consoleDiv.textContent = output; - console.log("Response:", data); - } else { - postAlert("bg-secondary-300", data.data); - } - } catch (error) { - postAlert("bg-secondary-300", error.message); - } + }); + consoleDiv.appendChild(consoleArea) } } From 94edebf7f1a4f5b7ecc2d487574120504cea74d7 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 18:08:29 +0200 Subject: [PATCH 20/22] fix route bug --- unikernel.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unikernel.ml b/unikernel.ml index 2aee24bc..b8ba7c3e 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -847,7 +847,7 @@ struct reqd | path when String.( - length path >= 20 && sub path 0 20 = "/unikernel/destroy/") + length path >= 19 && sub path 0 19 = "/unikernel/destroy/") -> ( let request = Httpaf.Reqd.request reqd in match request.meth with From 640b1c9a0ae3676e8940eb750f98a3e1da6141bf Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 30 Aug 2024 18:09:06 +0200 Subject: [PATCH 21/22] fix name bug --- unikernel.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unikernel.ml b/unikernel.ml index b8ba7c3e..1e3f516e 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -861,7 +861,7 @@ struct match Middleware.user_of_cookie users now reqd with | Ok user -> ( let unikernel_name = - String.sub path 20 (String.length path - 20) + String.sub path 19 (String.length path - 19) in Albatross.query !albatross ~domain:user.name (`Unikernel_cmd `Unikernel_destroy) From 82906ce99ee55b99ff0a0ecfd87de55a8f8f319f Mon Sep 17 00:00:00 2001 From: PizieDust Date: Sat, 31 Aug 2024 10:52:02 +0200 Subject: [PATCH 22/22] better error reporting --- albatross_json.ml | 12 ++--- assets/main.js | 51 +++++++++++++------- assets/style.css | 2 +- unikernel.ml | 119 +++++++++++++++++++++++++++++++++++----------- 4 files changed, 133 insertions(+), 51 deletions(-) diff --git a/albatross_json.ml b/albatross_json.ml index 7de7e212..20d002ff 100644 --- a/albatross_json.ml +++ b/albatross_json.ml @@ -105,12 +105,12 @@ let console_data_to_json (ts, data) = [ ("timestamp", `String (Ptime.to_rfc3339 ts)); ("line", `String data) ] let res = function - | `Command _ -> `String "command not supported" - | `Success s -> success s - | `Failure f -> `String ("failure: " ^ f) - | `Data (`Console_data (ts, data)) -> console_data_to_json (ts, data) - | `Data (`Utc_console_data (ts, data)) -> console_data_to_json (ts, data) - | `Data (`Stats_data _) -> `String "stats not supported" + | `Command _ -> Error (`String "command not supported") + | `Success s -> Ok (success s) + | `Failure f -> Error (`String ("failure: " ^ f)) + | `Data (`Console_data (ts, data)) -> Ok (console_data_to_json (ts, data)) + | `Data (`Utc_console_data (ts, data)) -> Ok (console_data_to_json (ts, data)) + | `Data (`Stats_data _) -> Error (`String "stats not supported") let fail_behaviour_of_json js = let ( let* ) = Result.bind in diff --git a/assets/main.js b/assets/main.js index 3d821def..2008e12b 100644 --- a/assets/main.js +++ b/assets/main.js @@ -116,6 +116,7 @@ function postAlert(bg_color, content) { } async function deployUnikernel() { + const deployButton = document.getElementById("deploy-button"); const name = document.getElementById("unikernel-name").value.trim(); const arguments = document.getElementById("unikernel-arguments").value.trim(); const binary = document.getElementById("unikernel-binary").files[0]; @@ -124,22 +125,23 @@ async function deployUnikernel() { formAlert.classList.remove("hidden", "text-primary-500"); formAlert.classList.add("text-secondary-500"); formAlert.textContent = "Please fill in the required data" + buttonLoading(deployButton, false, "Deploy") } else { + buttonLoading(deployButton, true, "Deploying...") let formData = new FormData(); - formData.append("name", name.value); + formData.append("name", name); formData.append("binary", binary) formData.append("arguments", arguments) try { const response = await fetch("/unikernel/create", { method: 'POST', - mode: "no-cors", headers: { "Content-Type": "application/json", }, body: formData }) const data = await response.json(); - if (data.status === 200) { + if (data.status === 200 && data.success) { formAlert.classList.remove("hidden", "text-secondary-500"); formAlert.classList.add("text-primary-500"); formAlert.textContent = "Succesfully updated"; @@ -152,12 +154,14 @@ async function deployUnikernel() { formAlert.classList.remove("hidden", "text-primary-500"); formAlert.classList.add("text-secondary-500"); formAlert.textContent = data.data + buttonLoading(deployButton, false, "Deploy") } } catch (error) { postAlert("bg-secondary-300", error); formAlert.classList.remove("hidden"); formAlert.classList.add("text-secondary-500"); formAlert.textContent = error + buttonLoading(deployButton, false, "Deploy") } } } @@ -183,9 +187,9 @@ async function destroyUnikernel(name) { } function getUnikernelName(url) { - const urlObj = new URL(url); - const pathParts = urlObj.pathname.split('/'); - return pathParts[pathParts.length - 1]; + const urlObj = new URL(url); + const pathParts = urlObj.pathname.split('/'); + return pathParts[pathParts.length - 1]; } var consoleArea = document.createElement("textarea"); @@ -193,25 +197,40 @@ async function getConsoleOutput() { let unikernel_name = getUnikernelName(window.location.href); if (unikernel_name) { const consoleDiv = document.getElementById("console-container"); - fetch("/unikernel/console/"+unikernel_name, { + fetch("/unikernel/console/" + unikernel_name, { method: "get", - }) + }) .then((response) => response.json()) .then((responseText) => { - let data= "" - responseText.forEach(item => { - data += `${item.timestamp} - ${item.line} \n` - }) - consoleArea.textContent = "" - consoleArea.textContent = data + let data = "" + responseText.forEach(item => { + data += `${item.timestamp} - ${item.line} \n` + }) + consoleArea.textContent = "" + consoleArea.textContent = data }) .catch((error) => { - postAlert("bg-secondary-300", `${unikernel_name} ${error}`) + postAlert("bg-secondary-300", `${unikernel_name} ${error}`) }) .finally(() => { }); - consoleDiv.appendChild(consoleArea) + consoleDiv.appendChild(consoleArea) } } +function buttonLoading(btn, load, text) { + if (load) { + btn.disabled = true; + btn.classList.remove("bg-primary-500", "text-gray-50"); + btn.classList.add("bg-primary-50", "text-primary-950"); + btn.innerHTML = ` ${text}`; + } else { + btn.disabled = false; + btn.classList.remove("bg-primary-50", "text-primary-950"); + btn.classList.add("bg-primary-500", "text-gray-50"); + btn.innerHTML = text; + } +} + + diff --git a/assets/style.css b/assets/style.css index a6dbdc9f..05d64bf6 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-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}.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-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}.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-col{flex-direction:column}.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-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-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}.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-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}.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}.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}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.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-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\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.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-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\: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\: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-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}.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-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}.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-col{flex-direction:column}.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-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-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}.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-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}.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}.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}.leading-6{line-height:1.5rem}.leading-none{line-height:1}.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-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-primary-950{--tw-text-opacity:1;color:rgb(13 38 37/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\:border-primary-200:hover{--tw-border-opacity:1;border-color:rgb(171 228 214/var(--tw-border-opacity))}.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-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\: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\: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/unikernel.ml b/unikernel.ml index 1e3f516e..0be0cd52 100644 --- a/unikernel.ml +++ b/unikernel.ml @@ -136,9 +136,42 @@ struct Albatross.query !albatross ~domain:"robur" (`Unikernel_cmd `Unikernel_info) >|= function - | Error () -> reply "error while querying albatross" + | Error () -> + let status = + { + Utils.Status.code = 500; + title = "Error"; + data = "Error while querying albatross"; + success = false; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) | Ok None -> reply "got none" - | Ok (Some (_hdr, res)) -> reply_json (Albatross_json.res res)) + | Ok (Some (_hdr, res)) -> ( + match Albatross_json.res res with + | Ok res -> + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = Yojson.Safe.to_string res; + success = true; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) + | Error (`String res) -> + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = Yojson.Safe.to_string (`String res); + success = false; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status))) | path when String.(length path >= 16 && sub path 0 16 = "/unikernel/info/") -> ( @@ -891,18 +924,30 @@ struct in reply ~content_type:"application/json" (Utils.Status.to_json status) - | Ok (Some (_hdr, res)) -> - let status = - { - Utils.Status.code = 200; - title = "Success"; - data = - Yojson.Safe.to_string (Albatross_json.res res); - success = true; - } - in - reply ~content_type:"application/json" - (Utils.Status.to_json status)) + | Ok (Some (_hdr, res)) -> ( + match Albatross_json.res res with + | Ok res -> + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = Yojson.Safe.to_string res; + success = true; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status) + | Error (`String res) -> + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = Yojson.Safe.to_string (`String res); + success = false; + } + in + reply ~content_type:"application/json" + (Utils.Status.to_json status))) | Error _ -> Logs.err (fun m -> m "couldn't find user of cookie"); assert false) @@ -1089,20 +1134,38 @@ struct reply ~content_type:"application/json" (Utils.Status.to_json status) - | Ok (Some (_hdr, res)) -> - let status = - { - Utils.Status.code = 200; - title = "Success"; - data = - Yojson.Safe.to_string - (Albatross_json.res res); - success = true; - } - in - reply - ~content_type:"application/json" - (Utils.Status.to_json status)) + | Ok (Some (_hdr, res)) -> ( + match Albatross_json.res res with + | Ok res -> + let status = + { + Utils.Status.code = 200; + title = "Success"; + data = + Yojson.Safe.to_string res; + success = true; + } + in + reply + ~content_type: + "application/json" + (Utils.Status.to_json status) + | Error (`String res) -> + let status = + { + Utils.Status.code = 400; + title = "Error"; + data = + Yojson.Safe.to_string + (`String res); + success = false; + } + in + reply + ~content_type: + "application/json" + (Utils.Status.to_json status)) + ) | Error (`Msg msg) -> Logs.warn (fun m -> m "couldn't decode data %s" msg);