diff --git a/client/src/App.jsx b/client/src/App.jsx
index cb5985f..458076d 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -1,4 +1,4 @@
-import { useEffect, useState } from "react";
+import { useEffect, useRef, useState } from "react";
import {
createMenuItem,
createOrder,
@@ -8,16 +8,17 @@ import {
resetMenu,
updateOrderStatus
} from "./api.js";
-import logo from "../../assets/ranaze-logo.png";
+import logo from "../../assets/ranaze-logo-transparent.png";
+import heroCup from "../../assets/ranaze-bubble-tea-single-cup-transparent.png";
const queueStatuses = ["new", "making", "ready"];
const orderIdleTimeoutMs = 90_000;
+const adminUnlockPressMs = 3_000;
const tabLabels = {
order: "Order",
queue: "Order Status",
menu: "Menu Admin"
};
-
const initialBuilder = (settings) => ({
size: settings.sizeOptions[0]?.name ?? "Regular",
sweetness: settings.sweetnessOptions[3] ?? settings.sweetnessOptions[0] ?? "100%",
@@ -27,8 +28,11 @@ const initialBuilder = (settings) => ({
});
export default function App() {
+ const adminPressTimerRef = useRef(null);
const [view, setView] = useState("order");
const [orderMode, setOrderMode] = useState(null);
+ const [adminUnlocked, setAdminUnlocked] = useState(false);
+ const [logoPressing, setLogoPressing] = useState(false);
const [menu, setMenu] = useState([]);
const [orders, setOrders] = useState([]);
const [settings, setSettings] = useState(null);
@@ -51,6 +55,16 @@ export default function App() {
loadBootstrap();
}, []);
+ useEffect(() => {
+ return () => window.clearTimeout(adminPressTimerRef.current);
+ }, []);
+
+ useEffect(() => {
+ if (!adminUnlocked && view !== "order") {
+ setView("order");
+ }
+ }, [adminUnlocked, view]);
+
useEffect(() => {
if (view !== "order" || !orderMode) {
return;
@@ -135,6 +149,7 @@ export default function App() {
return selectedCategory === "All" || item.category === selectedCategory;
});
const selectedDrink = menu.find((item) => item._id === selectedDrinkId) || null;
+ const visibleTabs = adminUnlocked ? ["order", "queue", "menu"] : ["order"];
const modeDetails = {
premade: {
stepLabel: "House favorite",
@@ -157,13 +172,35 @@ export default function App() {
};
const activeMode = orderMode ? modeDetails[orderMode] : null;
+ function startAdminPress() {
+ window.clearTimeout(adminPressTimerRef.current);
+ setLogoPressing(true);
+ adminPressTimerRef.current = window.setTimeout(() => {
+ setAdminUnlocked((current) => !current);
+ setLogoPressing(false);
+ }, adminUnlockPressMs);
+ }
+
+ function cancelAdminPress() {
+ window.clearTimeout(adminPressTimerRef.current);
+ setLogoPressing(false);
+ }
+
return (
))}
@@ -675,6 +729,16 @@ function SummaryCard({ label, value }) {
);
}
+function categoryMonogram(category) {
+ const map = {
+ "Milk Tea": "MT",
+ "Fruit Tea": "FT",
+ Specialty: "SP"
+ };
+
+ return map[category] || category.slice(0, 2).toUpperCase();
+}
+
function nextStatusButton(order, onAdvance) {
const map = {
new: "making",
diff --git a/client/src/styles.css b/client/src/styles.css
index d567e9e..7aaca24 100644
--- a/client/src/styles.css
+++ b/client/src/styles.css
@@ -14,6 +14,20 @@
font-family: "Avenir Next", "Trebuchet MS", sans-serif;
}
+body::before {
+ content: "";
+ position: fixed;
+ inset: 0;
+ background:
+ linear-gradient(180deg, rgba(255, 249, 239, 0.88), rgba(245, 231, 208, 0.94)),
+ radial-gradient(circle at 10% 18%, rgba(246, 179, 62, 0.15), transparent 18%),
+ radial-gradient(circle at 88% 16%, rgba(244, 221, 174, 0.22), transparent 20%),
+ radial-gradient(circle at 16% 82%, rgba(244, 221, 174, 0.16), transparent 18%),
+ radial-gradient(circle at 84% 86%, rgba(246, 179, 62, 0.12), transparent 18%);
+ pointer-events: none;
+ z-index: -1;
+}
+
* {
box-sizing: border-box;
}
@@ -72,22 +86,36 @@ button {
}
.brand-mark {
- width: 104px;
- height: 104px;
+ width: 120px;
+ height: 120px;
flex: 0 0 auto;
- border-radius: 28px;
- background: rgba(255, 255, 255, 0.88);
- border: 1px solid rgba(16, 16, 16, 0.06);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
+ border-radius: 32px;
+ background: radial-gradient(circle at center, rgba(255, 255, 255, 0.9), rgba(255, 248, 232, 0.72));
+ border: 1px solid rgba(16, 16, 16, 0.04);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.7),
+ 0 16px 32px rgba(83, 61, 24, 0.1);
display: grid;
place-items: center;
overflow: hidden;
+ padding: 0;
+ appearance: none;
+ cursor: default;
+ transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
+}
+
+.brand-mark.is-pressing {
+ transform: scale(0.98);
+ border-color: rgba(246, 179, 62, 0.45);
+ box-shadow:
+ inset 0 1px 0 rgba(255, 255, 255, 0.7),
+ 0 10px 20px rgba(83, 61, 24, 0.14);
}
.brand-mark img {
- width: 100%;
- height: 100%;
- object-fit: cover;
+ width: 88%;
+ height: 88%;
+ object-fit: contain;
}
.brand-copy {
@@ -198,6 +226,36 @@ button {
gap: 1rem;
}
+.mode-hero {
+ display: grid;
+ grid-template-columns: 1fr 220px;
+ gap: 1rem;
+ align-items: center;
+ padding: 1.1rem 1.25rem;
+ border-radius: 26px;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.76), rgba(247, 221, 161, 0.36));
+ border: 1px solid rgba(16, 16, 16, 0.06);
+}
+
+.mode-hero-copy {
+ display: grid;
+ gap: 0.4rem;
+}
+
+.mode-hero-copy h3 {
+ margin: 0;
+ font-size: clamp(1.2rem, 2vw, 1.8rem);
+ line-height: 1.05;
+}
+
+.mode-hero-image {
+ width: 100%;
+ max-height: 220px;
+ object-fit: contain;
+ justify-self: end;
+ filter: drop-shadow(0 22px 24px rgba(83, 61, 24, 0.16));
+}
+
.mode-card {
display: grid;
gap: 0.65rem;
@@ -265,6 +323,10 @@ button {
box-shadow: 0 8px 18px rgba(16, 16, 16, 0.18);
}
+.admin-lock-btn {
+ color: var(--muted);
+}
+
.text-btn {
background: transparent;
}
@@ -309,6 +371,37 @@ button {
text-align: left;
}
+.drink-card {
+ display: grid;
+ grid-template-columns: 74px 1fr auto;
+ align-items: center;
+ gap: 0.9rem;
+}
+
+.drink-card-badge {
+ width: 74px;
+ height: 74px;
+ border-radius: 20px;
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.96), rgba(248, 236, 210, 0.9));
+ border: 1px solid rgba(16, 16, 16, 0.06);
+ display: grid;
+ place-items: center;
+ overflow: hidden;
+}
+
+.drink-card-badge span {
+ font-family: Georgia, "Times New Roman", serif;
+ font-size: 1.15rem;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ color: #191512;
+}
+
+.drink-card-copy {
+ display: grid;
+ gap: 0.2rem;
+}
+
.drink-card h3,
.ticket-line strong,
.queue-card h3 {
@@ -448,6 +541,7 @@ select {
@media (max-width: 980px) {
.topbar,
.screen,
+ .mode-hero,
.mode-grid,
.queue-columns,
.menu-item,
@@ -466,6 +560,15 @@ select {
height: 88px;
}
+ .drink-card {
+ grid-template-columns: 1fr;
+ }
+
+ .drink-card-badge {
+ width: 64px;
+ height: 64px;
+ }
+
.order-toolbar {
align-items: flex-start;
flex-direction: column;