From fc336cc4cc878bfa5be050a76dfcae594d1fc21e Mon Sep 17 00:00:00 2001
From: Sebastian Mohr <sebastian@mohrenclan.de>
Date: Tue, 14 Jan 2025 14:26:27 +0100
Subject: [PATCH] Added a new notification component (looks like a post-it
 note)

---
 CHANGELOG.md                                  |  10 +-
 .../common/PaperWithTape.module.scss          | 117 ++++++
 .../components/common/PaperWithTape.tsx       |  62 +++
 .../components/common/UpdateNotification.tsx  |  25 +-
 .../pwa/AddToHomeScreen/AddToIosSafari.tsx    |  86 ++---
 .../pwa/AddToHomeScreen/AddToMobileChrome.tsx |  84 ++--
 .../AddToHomeScreen/AddToMobileChromeIos.tsx  |  86 ++---
 .../AddToHomeScreen/AddToMobileFirefox.tsx    |  94 +++--
 .../AddToHomeScreen/AddToMobileFirefoxIos.tsx |  94 ++---
 .../pwa/AddToHomeScreen/AddToOtherBrowser.tsx |  88 +++--
 .../pwa/AddToHomeScreen/AddToSamsung.tsx      |  90 +++--
 apps/fullstack/package.json                   |   2 +-
 apps/socket/src/prefixes/snip/socket.ts       |   2 +-
 pnpm-lock.yaml                                | 358 ++++++++++++------
 14 files changed, 741 insertions(+), 457 deletions(-)
 create mode 100644 apps/fullstack/components/common/PaperWithTape.module.scss
 create mode 100644 apps/fullstack/components/common/PaperWithTape.tsx

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1809fcfb..234630a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [unreleased]
+
+### Added
+- New post-it note component
+    - May be used to display notifications or other information
+    - At the moment used for the update notification and mobile installation notification
+
 
 ## [1.9.2]
 
@@ -486,7 +493,8 @@ the labels did not update correctly and were not removed correctly
 
 There are no changelogs for versions before 1.3.1.
 
-[unreleased]: https://gitlab.gwdg.de/irp/snip/-/compare/v1.9.1...HEAD
+[unreleased]: https://gitlab.gwdg.de/irp/snip/-/compare/v1.9.2...HEAD
+[1.9.2]: https://gitlab.gwdg.de/irp/snip/-/compare/v1.9.1...v1.9.2
 [1.9.1]: https://gitlab.gwdg.de/irp/snip/-/compare/v1.9.0...v1.9.1
 [1.9.0]: https://gitlab.gwdg.de/irp/snip/-/compare/v1.8.5...v1.9.0
 [1.8.5]: https://gitlab.gwdg.de/irp/snip/-/compare/v1.8.4...v1.8.5
diff --git a/apps/fullstack/components/common/PaperWithTape.module.scss b/apps/fullstack/components/common/PaperWithTape.module.scss
new file mode 100644
index 00000000..612b47ad
--- /dev/null
+++ b/apps/fullstack/components/common/PaperWithTape.module.scss
@@ -0,0 +1,117 @@
+// Tape colors
+$tape_gray: #dbd8be;
+$tape_edge_gray: #b7b49d;
+$tape-width: 6vmin;
+$tape-height: 2vmin;
+
+.blue {
+    --paper-color: #d5e0f9;
+    --paper-dark: #c2d0ea;
+    --shadow-size: 2px;
+    --transparent: rgba(213, 224, 249, 0);
+}
+
+.paper {
+    // Positioning
+    position: relative;
+    display: flex;
+    flex-direction: column;
+
+    // Sizing
+    min-height: 130px;
+    min-width: 250px;
+
+    // Styling
+    background: linear-gradient(
+            to bottom right,
+            var(--paper-dark),
+            20%,
+            var(--transparent)
+        ),
+        var(--paper-color);
+
+    box-shadow: var(--shadow-size) var(--shadow-size) 2px var(--paper-dark);
+
+    margin-inline: 1.6rem;
+    margin-top: 1rem;
+}
+
+.tapeTop {
+    // Positioning
+    position: absolute;
+    width: 100%;
+    display: flex;
+
+    &::before,
+    &::after {
+        content: "";
+        position: absolute;
+
+        width: $tape-width;
+        height: $tape-height;
+
+        background-color: $tape_gray;
+        opacity: 0.5;
+        border-right: 1px dotted $tape_edge_gray;
+        border-left: 1px dotted $tape_edge_gray;
+
+        top: 0;
+    }
+
+    &::before {
+        transform: rotate(-45deg);
+        left: -$tape-width / 2 + $tape-width / 6;
+    }
+
+    &::after {
+        transform: rotate(45deg);
+        right: -$tape-width / 2 + $tape-width / 6;
+        top: 0;
+    }
+}
+
+@font-face {
+    font-family: "Pacifico";
+    src: url("/fonts/Pacifico-Regular.ttf");
+}
+
+.header {
+    padding-inline: 2rem;
+    padding-top: 0.6rem;
+    font-family: "Pacifico", serif;
+    font-size: 1.5rem;
+
+    // Default alignment (left or as per parent)
+    display: flex;
+    justify-content: flex-start; // Default alignment
+    align-items: flex-start; // Default alignment
+    flex-direction: column;
+}
+
+.content {
+    padding-inline: 1rem;
+    padding-block: 0.6rem;
+    display: flex;
+    flex-direction: column;
+    justify-content: flex-start;
+    align-items: flex-start;
+}
+
+.paper {
+    a {
+        color: var(#2196f3);
+        text-decoration: none;
+        font-weight: bold;
+        transition: color 0.3s ease;
+
+        &:hover {
+            text-decoration: underline;
+            color: var(--link-hover-color, #1976d2);
+        }
+    }
+
+    button {
+        // I think it looks better without the typical button styling
+        border-radius: 0.1rem;
+    }
+}
diff --git a/apps/fullstack/components/common/PaperWithTape.tsx b/apps/fullstack/components/common/PaperWithTape.tsx
new file mode 100644
index 00000000..83ee1b28
--- /dev/null
+++ b/apps/fullstack/components/common/PaperWithTape.tsx
@@ -0,0 +1,62 @@
+import { HTMLAttributes } from "react";
+
+import styles from "./PaperWithTape.module.scss";
+
+interface PaperWithTapeWrapperProps extends HTMLAttributes<HTMLDivElement> {
+    children: React.ReactNode;
+    color?: "blue";
+    show?: boolean;
+}
+
+export function PaperWithTapeWrapper({
+    children,
+    color = "blue",
+    show = true,
+    ...props
+}: PaperWithTapeWrapperProps) {
+    let cStyle: string | undefined = "";
+    if (color === "blue") {
+        cStyle = styles.blue;
+    }
+
+    return (
+        <div
+            className={styles.paper + " " + cStyle}
+            {...props}
+            style={{ display: show ? "block" : "none", ...props.style }}
+        >
+            <div className={styles.tapeTop} />
+            {children}
+        </div>
+    );
+}
+
+export function PaperWithTapeHeader({
+    children,
+    ...props
+}: HTMLAttributes<HTMLDivElement>) {
+    return (
+        <div className={styles.header} role="heading" aria-level={3} {...props}>
+            {children}
+        </div>
+    );
+}
+
+export function PaperWithTapeContent({
+    children,
+    ...props
+}: HTMLAttributes<HTMLDivElement>) {
+    return (
+        <div className={styles.content} {...props}>
+            {children}
+        </div>
+    );
+}
+
+const PaperWithTape = {
+    Wrapper: PaperWithTapeWrapper,
+    Heading: PaperWithTapeHeader,
+    Content: PaperWithTapeContent,
+};
+
+export default PaperWithTape;
diff --git a/apps/fullstack/components/common/UpdateNotification.tsx b/apps/fullstack/components/common/UpdateNotification.tsx
index e380d17f..84432096 100644
--- a/apps/fullstack/components/common/UpdateNotification.tsx
+++ b/apps/fullstack/components/common/UpdateNotification.tsx
@@ -3,6 +3,8 @@ import Link from "next/link";
 import { useEffect, useState } from "react";
 import { Alert, Button } from "react-bootstrap";
 
+import PaperWithTape from "./PaperWithTape";
+
 const CURRENT_VERSION = "v1.9.0";
 
 export default function UpdateNotification() {
@@ -42,17 +44,14 @@ export default function UpdateNotification() {
     }, []);
 
     return (
-        <Alert variant="info" className="mx-2 py-2" show={displayPrompt}>
-            <Alert.Heading
-                style={{
-                    fontSize: "1.1rem",
-                    fontWeight: "bold",
-                    marginBottom: "0.25rem",
-                }}
-            >
+        <PaperWithTape.Wrapper
+            show={displayPrompt}
+            style={{ marginBottom: "1.5rem" }}
+        >
+            <PaperWithTape.Heading>
                 Update Notification {CURRENT_VERSION}
-            </Alert.Heading>
-            <div
+            </PaperWithTape.Heading>
+            <PaperWithTape.Content
                 style={{
                     display: "flex",
                     flexDirection: "column",
@@ -73,7 +72,7 @@ export default function UpdateNotification() {
                     package to streamline programmatic interactions with Snip.
                 </div>
                 <ChangelogAndFeedbackReminder />
-                <div className="d-flex justify-content-between mt-1">
+                <div className="d-flex justify-content-between mt-1 w-100">
                     <Button
                         className="p-1 d-flex align-items-center gap-1"
                         variant="outline-primary"
@@ -89,8 +88,8 @@ export default function UpdateNotification() {
                         Close
                     </Button>
                 </div>
-            </div>
-        </Alert>
+            </PaperWithTape.Content>
+        </PaperWithTape.Wrapper>
     );
 }
 
diff --git a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToIosSafari.tsx b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToIosSafari.tsx
index 662847e0..9bc08730 100644
--- a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToIosSafari.tsx
+++ b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToIosSafari.tsx
@@ -1,7 +1,9 @@
-import { Alert, Button } from "react-bootstrap";
+import Button from "react-bootstrap/esm/Button";
 import { AiOutlinePlusSquare } from "react-icons/ai";
 import { TbShare2 } from "react-icons/tb";
 
+import PaperWithTape from "components/common/PaperWithTape";
+
 interface Props {
     closePrompt: () => void;
     doNotShowAgain: () => void;
@@ -11,51 +13,49 @@ export default function AddToIosSafari(props: Props) {
     const { closePrompt, doNotShowAgain } = props;
 
     return (
-        <Alert show={true} className="mx-2 py-2" variant="info">
-            <Alert.Heading
-                style={{
-                    fontSize: "1.1rem",
-                    fontWeight: "bold",
-                    marginBottom: "0.25rem",
-                }}
-            >
+        <PaperWithTape.Wrapper show={true}>
+            <PaperWithTape.Heading>
                 Add SNIP to your Home Screen
-            </Alert.Heading>
-            <div>
-                For the best experience, we recommend installing SNIP to your
-                home screen! This enables enhanced features and functionality
-                for mobile users.
-            </div>
-            <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2">
-                <div className="d-flex gap-1 text-center">
-                    <div>Click the</div>
-                    <TbShare2 size={24} />
-                    <div>icon</div>
+            </PaperWithTape.Heading>
+            <PaperWithTape.Content>
+                <div>
+                    For the best experience, we highly recommend installing SNIP
+                    to your home screen! Without it, you might miss out on a few
+                    features and run into issue with the editor — apparently
+                    Apple likes to make things difficult for developers and
+                    users alike.
                 </div>
-                <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
-                    <div>Scroll down and then click:</div>
-                    <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
-                        <div>Add to Home Screen</div>
-                        <AiOutlinePlusSquare size={24} />
+                <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2 w-100">
+                    <div className="d-flex gap-1 text-center">
+                        <div>Click the</div>
+                        <TbShare2 size={24} />
+                        <div>icon</div>
+                    </div>
+                    <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
+                        <div>Scroll down and then click:</div>
+                        <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
+                            <div>Add to Home Screen</div>
+                            <AiOutlinePlusSquare size={24} />
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div className="d-flex justify-content-between">
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={doNotShowAgain}
-                    variant="outline-secondary"
-                >
-                    Don&apos;t show again
-                </Button>
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={closePrompt}
-                    variant="outline-dark"
-                >
-                    Close
-                </Button>
-            </div>
-        </Alert>
+                <div className="d-flex justify-content-between w-100">
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={doNotShowAgain}
+                        variant="outline-primary"
+                    >
+                        Don&apos;t show again
+                    </Button>
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={closePrompt}
+                        variant="outline-primary"
+                    >
+                        Close
+                    </Button>
+                </div>
+            </PaperWithTape.Content>
+        </PaperWithTape.Wrapper>
     );
 }
diff --git a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileChrome.tsx b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileChrome.tsx
index a8448487..50d93866 100644
--- a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileChrome.tsx
+++ b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileChrome.tsx
@@ -1,7 +1,9 @@
-import { Alert, Button } from "react-bootstrap";
+import Button from "react-bootstrap/esm/Button";
 import { HiDotsVertical } from "react-icons/hi";
 import { MdAddToHomeScreen } from "react-icons/md";
 
+import PaperWithTape from "components/common/PaperWithTape";
+
 interface Props {
     closePrompt: () => void;
     doNotShowAgain: () => void;
@@ -11,51 +13,47 @@ export default function AddToMobileChrome(props: Props) {
     const { closePrompt, doNotShowAgain } = props;
 
     return (
-        <Alert show={true} className="mx-2 py-2" variant="info">
-            <Alert.Heading
-                style={{
-                    fontSize: "1.1rem",
-                    fontWeight: "bold",
-                    marginBottom: "0.25rem",
-                }}
-            >
+        <PaperWithTape.Wrapper show={true}>
+            <PaperWithTape.Heading>
                 Add SNIP to your Home Screen
-            </Alert.Heading>
-            <div>
-                For the best experience, we recommend installing SNIP to your
-                home screen! This enables enhanced features and functionality
-                for mobile users.
-            </div>
-            <div className="d-flex align-items-center justify-content-center flex-column border border-1 rounded-3 p-2 gap-2 my-2">
-                <div className="d-flex gap-1 text-center">
-                    <div>Click the</div>
-                    <HiDotsVertical size={24} />
-                    <div>icon</div>
+            </PaperWithTape.Heading>
+            <PaperWithTape.Content>
+                <div>
+                    For the best experience, we highly recommend installing SNIP
+                    to your home screen! This enables enhanced features and
+                    functionality for mobile users.
                 </div>
-                <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
-                    <div>Scroll down and then click:</div>
-                    <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
-                        <MdAddToHomeScreen size={24} />
-                        <div>Add to Home Screen</div>
+                <div className="d-flex align-items-center justify-content-center flex-column border border-1 rounded-3 p-2 gap-2 my-2 w-100">
+                    <div className="d-flex gap-1 text-center">
+                        <div>Click the</div>
+                        <HiDotsVertical size={24} />
+                        <div>icon</div>
+                    </div>
+                    <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
+                        <div>Scroll down and then click:</div>
+                        <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
+                            <MdAddToHomeScreen size={24} />
+                            <div>Add to Home Screen</div>
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div className="d-flex justify-content-between">
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={doNotShowAgain}
-                    variant="outline-secondary"
-                >
-                    Don&apos;t show again
-                </Button>
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={closePrompt}
-                    variant="outline-dark"
-                >
-                    Close
-                </Button>
-            </div>
-        </Alert>
+                <div className="d-flex justify-content-between w-100">
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={doNotShowAgain}
+                        variant="outline-primary"
+                    >
+                        Don&apos;t show again
+                    </Button>
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={closePrompt}
+                        variant="outline-primary"
+                    >
+                        Close
+                    </Button>
+                </div>
+            </PaperWithTape.Content>
+        </PaperWithTape.Wrapper>
     );
 }
diff --git a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileChromeIos.tsx b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileChromeIos.tsx
index ea32324f..dd3807ce 100644
--- a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileChromeIos.tsx
+++ b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileChromeIos.tsx
@@ -1,7 +1,9 @@
-import { Alert, Button } from "react-bootstrap";
+import Button from "react-bootstrap/esm/Button";
 import { AiOutlinePlusSquare } from "react-icons/ai";
 import { TbShare2 } from "react-icons/tb";
 
+import PaperWithTape from "components/common/PaperWithTape";
+
 interface Props {
     closePrompt: () => void;
     doNotShowAgain: () => void;
@@ -11,51 +13,49 @@ export default function AddToMobileChromeIos(props: Props) {
     const { closePrompt, doNotShowAgain } = props;
 
     return (
-        <Alert show={true} className="mx-2 py-2" variant="info">
-            <Alert.Heading
-                style={{
-                    fontSize: "1.1rem",
-                    fontWeight: "bold",
-                    marginBottom: "0.25rem",
-                }}
-            >
+        <PaperWithTape.Wrapper show={true}>
+            <PaperWithTape.Heading>
                 Add SNIP to your Home Screen
-            </Alert.Heading>
-            <div>
-                For the best experience, we recommend installing SNIP to your
-                home screen! This enables enhanced features and functionality
-                for mobile users.
-            </div>
-            <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2">
-                <div className="d-flex gap-1 text-center">
-                    <div>Click the</div>
-                    <TbShare2 size={24} />
-                    <div>icon</div>
+            </PaperWithTape.Heading>
+            <PaperWithTape.Content>
+                <div>
+                    For the best experience, we highly recommend installing SNIP
+                    to your home screen! Without it, you might miss out on a few
+                    features and run into issue with the editor — apparently
+                    Apple likes to make things difficult for developers and
+                    users alike.
                 </div>
-                <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
-                    <div>Scroll down and then click:</div>
-                    <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
-                        <div>Add to Home Screen</div>
-                        <AiOutlinePlusSquare size={24} />
+                <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2 w-100">
+                    <div className="d-flex gap-1 text-center">
+                        <div>Click the</div>
+                        <TbShare2 size={24} />
+                        <div>icon</div>
+                    </div>
+                    <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
+                        <div>Scroll down and then click:</div>
+                        <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
+                            <div>Add to Home Screen</div>
+                            <AiOutlinePlusSquare size={24} />
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div className="d-flex justify-content-between">
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={doNotShowAgain}
-                    variant="outline-secondary"
-                >
-                    Don&apos;t show again
-                </Button>
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={closePrompt}
-                    variant="outline-dark"
-                >
-                    Close
-                </Button>
-            </div>
-        </Alert>
+                <div className="d-flex justify-content-between w-100">
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={doNotShowAgain}
+                        variant="outline-primary"
+                    >
+                        Don&apos;t show again
+                    </Button>
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={closePrompt}
+                        variant="outline-primary"
+                    >
+                        Close
+                    </Button>
+                </div>
+            </PaperWithTape.Content>
+        </PaperWithTape.Wrapper>
     );
 }
diff --git a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileFirefox.tsx b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileFirefox.tsx
index a0c769ac..fb0b2fbf 100644
--- a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileFirefox.tsx
+++ b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileFirefox.tsx
@@ -1,9 +1,11 @@
 import Image from "next/image";
-import { Alert, Button } from "react-bootstrap";
+import Button from "react-bootstrap/esm/Button";
 import { HiDotsVertical } from "react-icons/hi";
 
 import ffIcon from "@img/icons/firefox-install.png";
 
+import PaperWithTape from "components/common/PaperWithTape";
+
 interface Props {
     closePrompt: () => void;
     doNotShowAgain: () => void;
@@ -13,56 +15,52 @@ export default function AddToMobileFirefox(props: Props) {
     const { closePrompt, doNotShowAgain } = props;
 
     return (
-        <Alert show={true} className="mx-2 py-2" variant="info">
-            <Alert.Heading
-                style={{
-                    fontSize: "1.1rem",
-                    fontWeight: "bold",
-                    marginBottom: "0.25rem",
-                }}
-            >
+        <PaperWithTape.Wrapper show={true}>
+            <PaperWithTape.Heading>
                 Add SNIP to your Home Screen
-            </Alert.Heading>
-            <div>
-                For the best experience, we recommend installing SNIP to your
-                home screen! This enables enhanced features and functionality
-                for mobile users.
-            </div>
-            <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2">
-                <div className="d-flex gap-1 text-center">
-                    <div>Click the</div>
-                    <HiDotsVertical size={24} />
-                    <div>icon</div>
+            </PaperWithTape.Heading>
+            <PaperWithTape.Content>
+                <div>
+                    For the best experience, we highly recommend installing SNIP
+                    to your home screen! This enables enhanced features and
+                    functionality for mobile users.
                 </div>
-                <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
-                    <div>Scroll down and then click:</div>
-                    <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
-                        <Image
-                            src={ffIcon}
-                            alt="Firefox install icon"
-                            width={24}
-                            height={24}
-                        />
-                        <div>Install</div>
+                <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2 w-100">
+                    <div className="d-flex gap-1 text-center">
+                        <div>Click the</div>
+                        <HiDotsVertical size={24} />
+                        <div>icon</div>
+                    </div>
+                    <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
+                        <div>Scroll down and then click:</div>
+                        <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
+                            <Image
+                                src={ffIcon}
+                                alt="Firefox install icon"
+                                width={24}
+                                height={24}
+                            />
+                            <div>Install</div>
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div className="d-flex justify-content-between">
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={doNotShowAgain}
-                    variant="outline-secondary"
-                >
-                    Don&apos;t show again
-                </Button>
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={closePrompt}
-                    variant="outline-dark"
-                >
-                    Close
-                </Button>
-            </div>
-        </Alert>
+                <div className="d-flex justify-content-between w-100">
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={doNotShowAgain}
+                        variant="outline-primary"
+                    >
+                        Don&apos;t show again
+                    </Button>
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={closePrompt}
+                        variant="outline-primary"
+                    >
+                        Close
+                    </Button>
+                </div>
+            </PaperWithTape.Content>
+        </PaperWithTape.Wrapper>
     );
 }
diff --git a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileFirefoxIos.tsx b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileFirefoxIos.tsx
index ee6445c9..58d34723 100644
--- a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileFirefoxIos.tsx
+++ b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToMobileFirefoxIos.tsx
@@ -1,9 +1,11 @@
 import React from "react";
-import { Alert, Button } from "react-bootstrap";
+import Button from "react-bootstrap/esm/Button";
 import { AiOutlinePlusSquare } from "react-icons/ai";
 import { FaBars } from "react-icons/fa";
 import { FiShare } from "react-icons/fi";
 
+import PaperWithTape from "components/common/PaperWithTape";
+
 interface Props {
     closePrompt: () => void;
     doNotShowAgain: () => void;
@@ -13,56 +15,54 @@ export default function AddToMobileFirefoxIos(props: Props) {
     const { closePrompt, doNotShowAgain } = props;
 
     return (
-        <Alert show={true} className="mx-2 py-2" variant="info">
-            <Alert.Heading
-                style={{
-                    fontSize: "1.1rem",
-                    fontWeight: "bold",
-                    marginBottom: "0.25rem",
-                }}
-            >
+        <PaperWithTape.Wrapper show={true}>
+            <PaperWithTape.Heading>
                 Add SNIP to your Home Screen
-            </Alert.Heading>
-            <div>
-                For the best experience, we recommend installing SNIP to your
-                home screen! This enables enhanced features and functionality
-                for mobile users.
-            </div>
-            <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2">
-                <div className="d-flex gap-1 text-center">
-                    <div>Click the</div>
-                    <FaBars size={24} />
-                    <div>icon</div>
+            </PaperWithTape.Heading>
+            <PaperWithTape.Content>
+                <div>
+                    For the best experience, we highly recommend installing SNIP
+                    to your home screen! Without it, you might miss out on a few
+                    features and run into issue with the editor — apparently
+                    Apple likes to make things difficult for developers and
+                    users alike.
                 </div>
-                <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
-                    <div>Scroll down and then click:</div>
-                    <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
-                        <div>Share</div>
-                        <FiShare size={24} />
+                <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2 w-100">
+                    <div className="d-flex gap-1 text-center">
+                        <div>Click the</div>
+                        <FaBars size={24} />
+                        <div>icon</div>
                     </div>
-                    <div>Then click:</div>
-                    <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
-                        <div>Add to Home Screen</div>
-                        <AiOutlinePlusSquare size={24} />
+                    <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
+                        <div>Scroll down and then click:</div>
+                        <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
+                            <div>Share</div>
+                            <FiShare size={24} />
+                        </div>
+                        <div>Then click:</div>
+                        <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
+                            <div>Add to Home Screen</div>
+                            <AiOutlinePlusSquare size={24} />
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div className="d-flex justify-content-between">
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={doNotShowAgain}
-                    variant="outline-secondary"
-                >
-                    Don&apos;t show again
-                </Button>
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={closePrompt}
-                    variant="outline-dark"
-                >
-                    Close
-                </Button>
-            </div>
-        </Alert>
+                <div className="d-flex justify-content-between w-100">
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={doNotShowAgain}
+                        variant="outline-primary"
+                    >
+                        Don&apos;t show again
+                    </Button>
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={closePrompt}
+                        variant="outline-primary"
+                    >
+                        Close
+                    </Button>
+                </div>
+            </PaperWithTape.Content>
+        </PaperWithTape.Wrapper>
     );
 }
diff --git a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToOtherBrowser.tsx b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToOtherBrowser.tsx
index 0efac45e..1b9a6ef9 100644
--- a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToOtherBrowser.tsx
+++ b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToOtherBrowser.tsx
@@ -1,6 +1,8 @@
 import Link from "next/link";
 import React from "react";
-import { Alert, Button } from "react-bootstrap";
+import Button from "react-bootstrap/esm/Button";
+
+import PaperWithTape from "components/common/PaperWithTape";
 
 interface Props {
     closePrompt: () => void;
@@ -12,53 +14,49 @@ export default function AddToOtherBrowser(props: Props) {
     const searchUrl = `https://www.google.com/search?q=add+to+home+screen+for+common-mobile-browsers`;
 
     return (
-        <Alert show={true} className="mx-2 py-2" variant="info">
-            <Alert.Heading
-                style={{
-                    fontSize: "1.1rem",
-                    fontWeight: "bold",
-                    marginBottom: "0.25rem",
-                }}
-            >
+        <PaperWithTape.Wrapper show={true}>
+            <PaperWithTape.Heading>
                 Add SNIP to your Home Screen
-            </Alert.Heading>
-            <div>
-                For the best experience, we recommend installing SNIP to your
-                home screen! This enables enhanced features and functionality
-                for mobile users.
-            </div>
-            <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2">
-                <div className="d-flex gap-1 text-center flex-column">
-                    <div>
-                        Unfortunately, we were unable to determine which browser
-                        you are using. Please search for how to install a web
-                        app for your browser.
+            </PaperWithTape.Heading>
+            <PaperWithTape.Content>
+                <div>
+                    For the best experience, we recommend installing SNIP to
+                    your home screen! This enables enhanced features and
+                    functionality for mobile users.
+                </div>
+                <div className="d-flex align-items-center justify-content-center flex-column border border-1 rounded-3 p-2 gap-2 my-2 w-100">
+                    <div className="d-flex gap-1 text-center flex-column">
+                        <div>
+                            Unfortunately, we were unable to determine which
+                            browser you are using. Please search for how to
+                            install a web app for your browser.
+                        </div>
+                        <Link
+                            className="text-blue-300"
+                            href={searchUrl}
+                            target="_blank"
+                        >
+                            Try This Search
+                        </Link>
                     </div>
-                    <Link
-                        className="text-blue-300"
-                        href={searchUrl}
-                        target="_blank"
+                </div>
+                <div className="d-flex justify-content-between w-100">
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={doNotShowAgain}
+                        variant="outline-primary"
+                    >
+                        Don&apos;t show again
+                    </Button>
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={closePrompt}
+                        variant="outline-primary"
                     >
-                        Try This Search
-                    </Link>
+                        Close
+                    </Button>
                 </div>
-            </div>
-            <div className="d-flex justify-content-between">
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={doNotShowAgain}
-                    variant="outline-secondary"
-                >
-                    Don&apos;t show again
-                </Button>
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={closePrompt}
-                    variant="outline-dark"
-                >
-                    Close
-                </Button>
-            </div>
-        </Alert>
+            </PaperWithTape.Content>
+        </PaperWithTape.Wrapper>
     );
 }
diff --git a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToSamsung.tsx b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToSamsung.tsx
index 0884b222..b3992ffd 100644
--- a/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToSamsung.tsx
+++ b/apps/fullstack/components/common/pwa/AddToHomeScreen/AddToSamsung.tsx
@@ -1,8 +1,10 @@
 import React from "react";
-import { Alert, Button } from "react-bootstrap";
+import Button from "react-bootstrap/esm/Button";
 import { FaBars } from "react-icons/fa";
 import { TfiPlus } from "react-icons/tfi";
 
+import PaperWithTape from "components/common/PaperWithTape";
+
 interface Props {
     closePrompt: () => void;
     doNotShowAgain: () => void;
@@ -12,55 +14,51 @@ export default function AddToSamsung(props: Props) {
     const { closePrompt, doNotShowAgain } = props;
 
     return (
-        <Alert show={true} className="mx-2 py-2" variant="info">
-            <Alert.Heading
-                style={{
-                    fontSize: "1.1rem",
-                    fontWeight: "bold",
-                    marginBottom: "0.25rem",
-                }}
-            >
+        <PaperWithTape.Wrapper show={true}>
+            <PaperWithTape.Heading>
                 Add SNIP to your Home Screen
-            </Alert.Heading>
-            <div>
-                For the best experience, we recommend installing SNIP to your
-                home screen! This enables enhanced features and functionality
-                for mobile users.
-            </div>
-            <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2">
-                <div className="d-flex gap-1 text-center">
-                    <div>Click the</div>
-                    <FaBars size={24} />
-                    <div>icon</div>
+            </PaperWithTape.Heading>
+            <PaperWithTape.Content>
+                <div>
+                    For the best experience, we recommend installing SNIP to
+                    your home screen! This enables enhanced features and
+                    functionality for mobile users.
                 </div>
-                <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
-                    <div>Scroll down and then click:</div>
-                    <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
-                        <TfiPlus size={24} />
-                        <div>Add page to</div>
+                <div className="d-flex align-items-center justify-content-center flex-column  border border-1 rounded-3 p-2 gap-2 my-2 w-100">
+                    <div className="d-flex gap-1 text-center">
+                        <div>Click the</div>
+                        <FaBars size={24} />
+                        <div>icon</div>
                     </div>
-                    <div>Then select:</div>
-                    <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
-                        <div>Home Screen</div>
+                    <div className="d-flex flex-column align-items-center w-100 text-center gap-2">
+                        <div>Scroll down and then click:</div>
+                        <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
+                            <TfiPlus size={24} />
+                            <div>Add page to</div>
+                        </div>
+                        <div>Then select:</div>
+                        <div className="d-flex align-items-center border border-1 px-2 py-1 gap-1 rounded-3">
+                            <div>Home Screen</div>
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div className="d-flex justify-content-between">
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={doNotShowAgain}
-                    variant="outline-secondary"
-                >
-                    Don&apos;t show again
-                </Button>
-                <Button
-                    className="p-1 d-flex align-items-center gap-1"
-                    onClick={closePrompt}
-                    variant="outline-dark"
-                >
-                    Close
-                </Button>
-            </div>
-        </Alert>
+                <div className="d-flex justify-content-between w-100">
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={doNotShowAgain}
+                        variant="outline-primary"
+                    >
+                        Don&apos;t show again
+                    </Button>
+                    <Button
+                        className="p-1 d-flex align-items-center gap-1"
+                        onClick={closePrompt}
+                        variant="outline-primary"
+                    >
+                        Close
+                    </Button>
+                </div>
+            </PaperWithTape.Content>
+        </PaperWithTape.Wrapper>
     );
 }
diff --git a/apps/fullstack/package.json b/apps/fullstack/package.json
index 1f2bfe7c..272e6df1 100644
--- a/apps/fullstack/package.json
+++ b/apps/fullstack/package.json
@@ -38,7 +38,7 @@
         "remark-gfm": "^3.0.1",
         "remark-math": "^5.1.1",
         "remark-unwrap-images": "^4.0.0",
-        "sass": "^1.77.8",
+        "sass": "^1.83.4",
         "sharp": "^0.33.5",
         "socket.io-client": "^4.7.5",
         "swr": "^2.2.5",
diff --git a/apps/socket/src/prefixes/snip/socket.ts b/apps/socket/src/prefixes/snip/socket.ts
index 3e1f46b4..fc00706f 100644
--- a/apps/socket/src/prefixes/snip/socket.ts
+++ b/apps/socket/src/prefixes/snip/socket.ts
@@ -111,7 +111,7 @@ async function upsert(
         // Snip does not exist insert
     }
 
-    const new_snipData = await SQLService.snip.upsert(snip_data);
+    const new_snipData = await SQLService.snip.upsert!(snip_data);
 
     if (page_id !== undefined && page_id !== null) {
         // Snip was placed!
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index afccd288..5e48ff66 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -170,7 +170,7 @@ importers:
         version: 5.6.3
       vitest:
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
       zod:
         specifier: ^3.23.8
         version: 3.23.8
@@ -239,7 +239,7 @@ importers:
         version: 0.16.11
       next:
         specifier: 14.2.18
-        version: 14.2.18(@playwright/test@1.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8)
+        version: 14.2.18(@playwright/test@1.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.83.4)
       next-mdx-remote:
         specifier: ^4.4.1
         version: 4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -283,8 +283,8 @@ importers:
         specifier: ^4.0.0
         version: 4.0.0
       sass:
-        specifier: ^1.77.8
-        version: 1.77.8
+        specifier: ^1.83.4
+        version: 1.83.4
       sharp:
         specifier: ^0.33.5
         version: 0.33.5
@@ -351,10 +351,10 @@ importers:
         version: 5.6.3
       vite-tsconfig-paths:
         specifier: ^4.3.2
-        version: 4.3.2(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))
+        version: 4.3.2(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))
       vitest:
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
 
   apps/images:
     dependencies:
@@ -454,13 +454,13 @@ importers:
         version: 5.6.3
       vite-plugin-wasm:
         specifier: ^3.3.0
-        version: 3.3.0(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))
+        version: 3.3.0(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))
       vite-tsconfig-paths:
         specifier: ^4.3.2
-        version: 4.3.2(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))
+        version: 4.3.2(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))
       vitest:
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
       zod:
         specifier: ^3.23.8
         version: 3.23.8
@@ -686,7 +686,7 @@ importers:
         version: 5.6.3
       vitest:
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
 
   packages/configs/tsup-config:
     dependencies:
@@ -744,7 +744,7 @@ importers:
         version: 5.6.3
       vitest:
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@20.17.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+        version: 2.1.4(@types/node@20.17.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
 
   packages/render:
     dependencies:
@@ -772,7 +772,7 @@ importers:
         version: 10.3.2
       '@vitest/browser':
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@22.8.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
+        version: 2.1.4(@types/node@22.8.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
       skia-canvas:
         specifier: ^1.0.2
         version: 1.0.2
@@ -784,7 +784,7 @@ importers:
         version: 5.6.3
       vitest:
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@22.8.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+        version: 2.1.4(@types/node@22.8.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
       webdriverio:
         specifier: ^9.0.5
         version: 9.0.5
@@ -995,7 +995,7 @@ importers:
         version: 5.6.3
       vitest:
         specifier: 'catalog:'
-        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+        version: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
 
   packages/wasm:
     devDependencies:
@@ -2377,6 +2377,88 @@ packages:
   '@open-draft/until@2.1.0':
     resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
 
+  '@parcel/watcher-android-arm64@2.5.0':
+    resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [android]
+
+  '@parcel/watcher-darwin-arm64@2.5.0':
+    resolution: {integrity: sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@parcel/watcher-darwin-x64@2.5.0':
+    resolution: {integrity: sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@parcel/watcher-freebsd-x64@2.5.0':
+    resolution: {integrity: sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@parcel/watcher-linux-arm-glibc@2.5.0':
+    resolution: {integrity: sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm]
+    os: [linux]
+
+  '@parcel/watcher-linux-arm-musl@2.5.0':
+    resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm]
+    os: [linux]
+
+  '@parcel/watcher-linux-arm64-glibc@2.5.0':
+    resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@parcel/watcher-linux-arm64-musl@2.5.0':
+    resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@parcel/watcher-linux-x64-glibc@2.5.0':
+    resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [linux]
+
+  '@parcel/watcher-linux-x64-musl@2.5.0':
+    resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [linux]
+
+  '@parcel/watcher-win32-arm64@2.5.0':
+    resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@parcel/watcher-win32-ia32@2.5.0':
+    resolution: {integrity: sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [ia32]
+    os: [win32]
+
+  '@parcel/watcher-win32-x64@2.5.0':
+    resolution: {integrity: sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==}
+    engines: {node: '>= 10.0.0'}
+    cpu: [x64]
+    os: [win32]
+
+  '@parcel/watcher@2.5.0':
+    resolution: {integrity: sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==}
+    engines: {node: '>= 10.0.0'}
+
   '@pdf-lib/standard-fonts@1.0.0':
     resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==}
 
@@ -3272,10 +3354,6 @@ packages:
   any-promise@1.3.0:
     resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
 
-  anymatch@3.1.3:
-    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
-    engines: {node: '>= 8'}
-
   aproba@2.0.0:
     resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
 
@@ -3475,10 +3553,6 @@ packages:
   bignumber.js@9.0.0:
     resolution: {integrity: sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==}
 
-  binary-extensions@2.3.0:
-    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
-    engines: {node: '>=8'}
-
   binary-install@1.1.0:
     resolution: {integrity: sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==}
     engines: {node: '>=10'}
@@ -3626,10 +3700,6 @@ packages:
     resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==}
     engines: {node: '>=18.17'}
 
-  chokidar@3.6.0:
-    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
-    engines: {node: '>= 8.10.0'}
-
   chokidar@4.0.1:
     resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==}
     engines: {node: '>= 14.16.0'}
@@ -4030,6 +4100,11 @@ packages:
     resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
     engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
 
+  detect-libc@1.0.3:
+    resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+    engines: {node: '>=0.10'}
+    hasBin: true
+
   detect-libc@2.0.3:
     resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
     engines: {node: '>=8'}
@@ -4958,8 +5033,8 @@ packages:
   immediate@3.0.6:
     resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
 
-  immutable@4.3.7:
-    resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
+  immutable@5.0.3:
+    resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==}
 
   import-cwd@3.0.0:
     resolution: {integrity: sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==}
@@ -5045,10 +5120,6 @@ packages:
   is-bigint@1.0.4:
     resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
 
-  is-binary-path@2.1.0:
-    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
-    engines: {node: '>=8'}
-
   is-boolean-object@1.1.2:
     resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
     engines: {node: '>= 0.4'}
@@ -6031,6 +6102,9 @@ packages:
       sass:
         optional: true
 
+  node-addon-api@7.1.1:
+    resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
   node-domexception@1.0.0:
     resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
     engines: {node: '>=10.5.0'}
@@ -6758,10 +6832,6 @@ packages:
   readdir-glob@1.1.3:
     resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
 
-  readdirp@3.6.0:
-    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
-    engines: {node: '>=8.10.0'}
-
   readdirp@4.0.2:
     resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==}
     engines: {node: '>= 14.16.0'}
@@ -6954,8 +7024,8 @@ packages:
   safer-buffer@2.1.2:
     resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
 
-  sass@1.77.8:
-    resolution: {integrity: sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==}
+  sass@1.83.4:
+    resolution: {integrity: sha512-B1bozCeNQiOgDcLd33e2Cs2U60wZwjUUXzh900ZyQF5qUasvMdDZYbQ566LJu7cqR+sAHlAfO6RMkaID5s6qpA==}
     engines: {node: '>=14.0.0'}
     hasBin: true
 
@@ -7112,10 +7182,6 @@ packages:
     resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==}
     engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
 
-  source-map-js@1.2.0:
-    resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
-    engines: {node: '>=0.10.0'}
-
   source-map-js@1.2.1:
     resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
     engines: {node: '>=0.10.0'}
@@ -9510,6 +9576,67 @@ snapshots:
 
   '@open-draft/until@2.1.0': {}
 
+  '@parcel/watcher-android-arm64@2.5.0':
+    optional: true
+
+  '@parcel/watcher-darwin-arm64@2.5.0':
+    optional: true
+
+  '@parcel/watcher-darwin-x64@2.5.0':
+    optional: true
+
+  '@parcel/watcher-freebsd-x64@2.5.0':
+    optional: true
+
+  '@parcel/watcher-linux-arm-glibc@2.5.0':
+    optional: true
+
+  '@parcel/watcher-linux-arm-musl@2.5.0':
+    optional: true
+
+  '@parcel/watcher-linux-arm64-glibc@2.5.0':
+    optional: true
+
+  '@parcel/watcher-linux-arm64-musl@2.5.0':
+    optional: true
+
+  '@parcel/watcher-linux-x64-glibc@2.5.0':
+    optional: true
+
+  '@parcel/watcher-linux-x64-musl@2.5.0':
+    optional: true
+
+  '@parcel/watcher-win32-arm64@2.5.0':
+    optional: true
+
+  '@parcel/watcher-win32-ia32@2.5.0':
+    optional: true
+
+  '@parcel/watcher-win32-x64@2.5.0':
+    optional: true
+
+  '@parcel/watcher@2.5.0':
+    dependencies:
+      detect-libc: 1.0.3
+      is-glob: 4.0.3
+      micromatch: 4.0.7
+      node-addon-api: 7.1.1
+    optionalDependencies:
+      '@parcel/watcher-android-arm64': 2.5.0
+      '@parcel/watcher-darwin-arm64': 2.5.0
+      '@parcel/watcher-darwin-x64': 2.5.0
+      '@parcel/watcher-freebsd-x64': 2.5.0
+      '@parcel/watcher-linux-arm-glibc': 2.5.0
+      '@parcel/watcher-linux-arm-musl': 2.5.0
+      '@parcel/watcher-linux-arm64-glibc': 2.5.0
+      '@parcel/watcher-linux-arm64-musl': 2.5.0
+      '@parcel/watcher-linux-x64-glibc': 2.5.0
+      '@parcel/watcher-linux-x64-musl': 2.5.0
+      '@parcel/watcher-win32-arm64': 2.5.0
+      '@parcel/watcher-win32-ia32': 2.5.0
+      '@parcel/watcher-win32-x64': 2.5.0
+    optional: true
+
   '@pdf-lib/standard-fonts@1.0.0':
     dependencies:
       pako: 1.0.11
@@ -10184,17 +10311,17 @@ snapshots:
 
   '@ungap/structured-clone@1.2.0': {}
 
-  '@vitest/browser@2.1.4(@types/node@20.14.12)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)':
+  '@vitest/browser@2.1.4(@types/node@20.14.12)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)':
     dependencies:
       '@testing-library/dom': 10.4.0
       '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0)
-      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))
+      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))
       '@vitest/utils': 2.1.4
       magic-string: 0.30.12
       msw: 2.6.0(@types/node@20.14.12)(typescript@5.6.3)
       sirv: 3.0.0
       tinyrainbow: 1.2.0
-      vitest: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+      vitest: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
       ws: 8.18.0
     optionalDependencies:
       playwright: 1.45.2
@@ -10207,17 +10334,17 @@ snapshots:
       - vite
     optional: true
 
-  '@vitest/browser@2.1.4(@types/node@20.17.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)':
+  '@vitest/browser@2.1.4(@types/node@20.17.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)':
     dependencies:
       '@testing-library/dom': 10.4.0
       '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0)
-      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(vite@5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0))
+      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(vite@5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0))
       '@vitest/utils': 2.1.4
       magic-string: 0.30.12
       msw: 2.6.0(@types/node@20.17.2)(typescript@5.6.3)
       sirv: 3.0.0
       tinyrainbow: 1.2.0
-      vitest: 2.1.4(@types/node@20.17.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+      vitest: 2.1.4(@types/node@20.17.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
       ws: 8.18.0
     optionalDependencies:
       playwright: 1.45.2
@@ -10230,17 +10357,17 @@ snapshots:
       - vite
     optional: true
 
-  '@vitest/browser@2.1.4(@types/node@22.8.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)':
+  '@vitest/browser@2.1.4(@types/node@22.8.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)':
     dependencies:
       '@testing-library/dom': 10.4.0
       '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0)
-      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(vite@5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0))
+      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(vite@5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0))
       '@vitest/utils': 2.1.4
       magic-string: 0.30.12
       msw: 2.6.0(@types/node@22.8.2)(typescript@5.6.3)
       sirv: 3.0.0
       tinyrainbow: 1.2.0
-      vitest: 2.1.4(@types/node@22.8.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+      vitest: 2.1.4(@types/node@22.8.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
       ws: 8.18.0
     optionalDependencies:
       playwright: 1.45.2
@@ -10266,9 +10393,9 @@ snapshots:
       std-env: 3.7.0
       test-exclude: 7.0.1
       tinyrainbow: 1.2.0
-      vitest: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0)
+      vitest: 2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0)
     optionalDependencies:
-      '@vitest/browser': 2.1.4(@types/node@20.14.12)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
+      '@vitest/browser': 2.1.4(@types/node@20.14.12)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
     transitivePeerDependencies:
       - supports-color
 
@@ -10279,32 +10406,32 @@ snapshots:
       chai: 5.1.2
       tinyrainbow: 1.2.0
 
-  '@vitest/mocker@2.1.4(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))':
+  '@vitest/mocker@2.1.4(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))':
     dependencies:
       '@vitest/spy': 2.1.4
       estree-walker: 3.0.3
       magic-string: 0.30.12
     optionalDependencies:
       msw: 2.6.0(@types/node@20.14.12)(typescript@5.6.3)
-      vite: 5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0)
 
-  '@vitest/mocker@2.1.4(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(vite@5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0))':
+  '@vitest/mocker@2.1.4(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(vite@5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0))':
     dependencies:
       '@vitest/spy': 2.1.4
       estree-walker: 3.0.3
       magic-string: 0.30.12
     optionalDependencies:
       msw: 2.6.0(@types/node@20.17.2)(typescript@5.6.3)
-      vite: 5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0)
 
-  '@vitest/mocker@2.1.4(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(vite@5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0))':
+  '@vitest/mocker@2.1.4(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(vite@5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0))':
     dependencies:
       '@vitest/spy': 2.1.4
       estree-walker: 3.0.3
       magic-string: 0.30.12
     optionalDependencies:
       msw: 2.6.0(@types/node@22.8.2)(typescript@5.6.3)
-      vite: 5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0)
 
   '@vitest/pretty-format@2.1.4':
     dependencies:
@@ -10574,11 +10701,6 @@ snapshots:
 
   any-promise@1.3.0: {}
 
-  anymatch@3.1.3:
-    dependencies:
-      normalize-path: 3.0.0
-      picomatch: 2.3.1
-
   aproba@2.0.0: {}
 
   archiver-utils@5.0.2:
@@ -10829,8 +10951,6 @@ snapshots:
 
   bignumber.js@9.0.0: {}
 
-  binary-extensions@2.3.0: {}
-
   binary-install@1.1.0:
     dependencies:
       axios: 0.26.1
@@ -11008,18 +11128,6 @@ snapshots:
       undici: 6.19.8
       whatwg-mimetype: 4.0.0
 
-  chokidar@3.6.0:
-    dependencies:
-      anymatch: 3.1.3
-      braces: 3.0.3
-      glob-parent: 5.1.2
-      is-binary-path: 2.1.0
-      is-glob: 4.0.3
-      normalize-path: 3.0.0
-      readdirp: 3.6.0
-    optionalDependencies:
-      fsevents: 2.3.3
-
   chokidar@4.0.1:
     dependencies:
       readdirp: 4.0.2
@@ -11399,6 +11507,9 @@ snapshots:
 
   destroy@1.2.0: {}
 
+  detect-libc@1.0.3:
+    optional: true
+
   detect-libc@2.0.3: {}
 
   devlop@1.1.0:
@@ -12763,7 +12874,7 @@ snapshots:
 
   immediate@3.0.6: {}
 
-  immutable@4.3.7: {}
+  immutable@5.0.3: {}
 
   import-cwd@3.0.0:
     dependencies:
@@ -12849,10 +12960,6 @@ snapshots:
     dependencies:
       has-bigints: 1.0.2
 
-  is-binary-path@2.1.0:
-    dependencies:
-      binary-extensions: 2.3.0
-
   is-boolean-object@1.1.2:
     dependencies:
       call-bind: 1.0.7
@@ -14365,7 +14472,7 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
-  next@14.2.18(@playwright/test@1.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.8):
+  next@14.2.18(@playwright/test@1.45.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.83.4):
     dependencies:
       '@next/env': 14.2.18
       '@swc/helpers': 0.5.5
@@ -14387,11 +14494,14 @@ snapshots:
       '@next/swc-win32-ia32-msvc': 14.2.18
       '@next/swc-win32-x64-msvc': 14.2.18
       '@playwright/test': 1.45.2
-      sass: 1.77.8
+      sass: 1.83.4
     transitivePeerDependencies:
       - '@babel/core'
       - babel-plugin-macros
 
+  node-addon-api@7.1.1:
+    optional: true
+
   node-domexception@1.0.0: {}
 
   node-fetch@2.7.0:
@@ -15134,10 +15244,6 @@ snapshots:
     dependencies:
       minimatch: 5.1.6
 
-  readdirp@3.6.0:
-    dependencies:
-      picomatch: 2.3.1
-
   readdirp@4.0.2: {}
 
   rechoir@0.8.0:
@@ -15424,11 +15530,13 @@ snapshots:
 
   safer-buffer@2.1.2: {}
 
-  sass@1.77.8:
+  sass@1.83.4:
     dependencies:
-      chokidar: 3.6.0
-      immutable: 4.3.7
-      source-map-js: 1.2.0
+      chokidar: 4.0.1
+      immutable: 5.0.3
+      source-map-js: 1.2.1
+    optionalDependencies:
+      '@parcel/watcher': 2.5.0
 
   sax@1.4.1: {}
 
@@ -15671,8 +15779,6 @@ snapshots:
       ip-address: 9.0.5
       smart-buffer: 4.2.0
 
-  source-map-js@1.2.0: {}
-
   source-map-js@1.2.1: {}
 
   source-map-support@0.5.21:
@@ -16386,12 +16492,12 @@ snapshots:
       unist-util-stringify-position: 4.0.0
       vfile-message: 4.0.2
 
-  vite-node@2.1.4(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0):
+  vite-node@2.1.4(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0):
     dependencies:
       cac: 6.7.14
       debug: 4.3.7
       pathe: 1.1.2
-      vite: 5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -16403,12 +16509,12 @@ snapshots:
       - supports-color
       - terser
 
-  vite-node@2.1.4(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0):
+  vite-node@2.1.4(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0):
     dependencies:
       cac: 6.7.14
       debug: 4.3.7
       pathe: 1.1.2
-      vite: 5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -16420,12 +16526,12 @@ snapshots:
       - supports-color
       - terser
 
-  vite-node@2.1.4(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0):
+  vite-node@2.1.4(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0):
     dependencies:
       cac: 6.7.14
       debug: 4.3.7
       pathe: 1.1.2
-      vite: 5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -16437,22 +16543,22 @@ snapshots:
       - supports-color
       - terser
 
-  vite-plugin-wasm@3.3.0(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0)):
+  vite-plugin-wasm@3.3.0(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0)):
     dependencies:
-      vite: 5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0)
 
-  vite-tsconfig-paths@4.3.2(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0)):
+  vite-tsconfig-paths@4.3.2(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0)):
     dependencies:
       debug: 4.3.7
       globrex: 0.1.2
       tsconfck: 3.1.1(typescript@5.6.3)
     optionalDependencies:
-      vite: 5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0)
     transitivePeerDependencies:
       - supports-color
       - typescript
 
-  vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0):
+  vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.47
@@ -16460,10 +16566,10 @@ snapshots:
     optionalDependencies:
       '@types/node': 20.14.12
       fsevents: 2.3.3
-      sass: 1.77.8
+      sass: 1.83.4
       terser: 5.36.0
 
-  vite@5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0):
+  vite@5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.47
@@ -16471,10 +16577,10 @@ snapshots:
     optionalDependencies:
       '@types/node': 20.17.2
       fsevents: 2.3.3
-      sass: 1.77.8
+      sass: 1.83.4
       terser: 5.36.0
 
-  vite@5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0):
+  vite@5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.4.47
@@ -16482,13 +16588,13 @@ snapshots:
     optionalDependencies:
       '@types/node': 22.8.2
       fsevents: 2.3.3
-      sass: 1.77.8
+      sass: 1.83.4
       terser: 5.36.0
 
-  vitest@2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0):
+  vitest@2.1.4(@types/node@20.14.12)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0):
     dependencies:
       '@vitest/expect': 2.1.4
-      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))
+      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@20.14.12)(typescript@5.6.3))(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))
       '@vitest/pretty-format': 2.1.4
       '@vitest/runner': 2.1.4
       '@vitest/snapshot': 2.1.4
@@ -16504,12 +16610,12 @@ snapshots:
       tinyexec: 0.3.1
       tinypool: 1.0.1
       tinyrainbow: 1.2.0
-      vite: 5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0)
-      vite-node: 2.1.4(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0)
+      vite-node: 2.1.4(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0)
       why-is-node-running: 2.3.0
     optionalDependencies:
       '@types/node': 20.14.12
-      '@vitest/browser': 2.1.4(@types/node@20.14.12)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.77.8)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
+      '@vitest/browser': 2.1.4(@types/node@20.14.12)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.14.12)(sass@1.83.4)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
       jsdom: 20.0.3
     transitivePeerDependencies:
       - less
@@ -16522,10 +16628,10 @@ snapshots:
       - supports-color
       - terser
 
-  vitest@2.1.4(@types/node@20.17.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0):
+  vitest@2.1.4(@types/node@20.17.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0):
     dependencies:
       '@vitest/expect': 2.1.4
-      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(vite@5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0))
+      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@20.17.2)(typescript@5.6.3))(vite@5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0))
       '@vitest/pretty-format': 2.1.4
       '@vitest/runner': 2.1.4
       '@vitest/snapshot': 2.1.4
@@ -16541,12 +16647,12 @@ snapshots:
       tinyexec: 0.3.1
       tinypool: 1.0.1
       tinyrainbow: 1.2.0
-      vite: 5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0)
-      vite-node: 2.1.4(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0)
+      vite-node: 2.1.4(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0)
       why-is-node-running: 2.3.0
     optionalDependencies:
       '@types/node': 20.17.2
-      '@vitest/browser': 2.1.4(@types/node@20.17.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.17.2)(sass@1.77.8)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
+      '@vitest/browser': 2.1.4(@types/node@20.17.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@20.17.2)(sass@1.83.4)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
       jsdom: 20.0.3
     transitivePeerDependencies:
       - less
@@ -16559,10 +16665,10 @@ snapshots:
       - supports-color
       - terser
 
-  vitest@2.1.4(@types/node@22.8.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(sass@1.77.8)(terser@5.36.0):
+  vitest@2.1.4(@types/node@22.8.2)(@vitest/browser@2.1.4)(jsdom@20.0.3)(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(sass@1.83.4)(terser@5.36.0):
     dependencies:
       '@vitest/expect': 2.1.4
-      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(vite@5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0))
+      '@vitest/mocker': 2.1.4(msw@2.6.0(@types/node@22.8.2)(typescript@5.6.3))(vite@5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0))
       '@vitest/pretty-format': 2.1.4
       '@vitest/runner': 2.1.4
       '@vitest/snapshot': 2.1.4
@@ -16578,12 +16684,12 @@ snapshots:
       tinyexec: 0.3.1
       tinypool: 1.0.1
       tinyrainbow: 1.2.0
-      vite: 5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0)
-      vite-node: 2.1.4(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0)
+      vite: 5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0)
+      vite-node: 2.1.4(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0)
       why-is-node-running: 2.3.0
     optionalDependencies:
       '@types/node': 22.8.2
-      '@vitest/browser': 2.1.4(@types/node@22.8.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.2)(sass@1.77.8)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
+      '@vitest/browser': 2.1.4(@types/node@22.8.2)(playwright@1.45.2)(typescript@5.6.3)(vite@5.4.10(@types/node@22.8.2)(sass@1.83.4)(terser@5.36.0))(vitest@2.1.4)(webdriverio@9.0.5)
       jsdom: 20.0.3
     transitivePeerDependencies:
       - less
-- 
GitLab