# Handoff: Days Inn Ops Portal — Staff Screens Redesign

## Overview

A redesign of the three staff-facing screens of the Days Inn Guest Hub ops portal: the **front desk board**, the **housekeeping workspace** (mobile-first plus a supervisor tablet view), and the **request queue**.

The redesign addresses four problems identified in the current build:

1. **Weak visual hierarchy** — the old screens present every stat, tab, and room card at the same weight. The redesign introduces a single "Do this next" panel per screen that names the one action that matters right now, with everything else demoted to supporting evidence.
2. **Mobile experience** — housekeeping is a phone job. The redesign leads with a 390px-wide route view: one room at a time, 54px primary action, 56px bottom tab bar.
3. **Doesn't feel premium** — light neutral canvas, generous card radii, Playfair Display SC for headings, tabular numerals on all figures, and status colour used sparingly as the only accent hue.
4. **Doesn't feel brand-compliant** — the old build used an off-brand deep indigo (`oklch(0.18 0.1 250)`) and amber. The redesign uses Wyndham/Days Inn **Congress Blue `#004986`** and **gold `#D4AF37`** as the entire brand palette.

This is direction **1c ("One next action")**, selected from three explored directions. The two unselected directions are in `Ops Portal - Redesign.dc.html` for reference (`1a` brand light board, `1b` Congress Blue deep).

## About the Design Files

The files in this bundle are **design references created in HTML** — prototypes showing the intended look and behavior. They are **not production code to copy directly**.

The task is to **recreate these designs in the target codebase's existing environment**, using its established patterns and libraries. The source app is a **TanStack Router + React + Tailwind CSS v4** project (`rulloa1/daysinn`), so the implementation should:

- Use the existing route files (`src/routes/front-desk.tsx`, `src/routes/housekeeping.tsx`, `src/routes/staff.tsx`) as the integration points.
- Use the existing `src/components/ui/*` primitives (`button.tsx`, `badge.tsx`, `card.tsx`) rather than introducing new ones.
- Express colours as Tailwind theme tokens in `src/styles.css` (the project already uses an `@theme` block with oklch values) rather than hard-coded hex in components.
- Keep existing data hooks and Supabase queries; only the presentation layer changes.

The prototype files use inline styles because that is what the prototyping environment requires. **Do not port inline styles** — translate them into the codebase's Tailwind classes.

## Fidelity

**High-fidelity.** Colours, typography, spacing, radii, and copy are final and should be matched closely. Every value in this document is exact. Layout structure, component composition, and interaction behaviour are specified below; recreate the UI to match, using the codebase's existing component library where it covers a pattern.

Two things are intentionally *not* final:

- Iconography is inline Lucide SVG paths in the prototype. The codebase already depends on `lucide-react` — use the named components (listed per screen below).
- Data is hard-coded sample data. Wire to the real queries.

---

## Design Tokens

### Colour — brand

| Token | Hex | Use |
| --- | --- | --- |
| `brand-blue` | `#004986` | Congress Blue. Nav rail, primary buttons, all headings, room numbers, section labels. |
| `brand-blue-link` | `#0065AB` | Link default, "Live production" status dot. |
| `brand-gold` | `#D4AF37` | Gold. "Do this next" eyebrow labels, primary action on dark, progress bars, active nav pill, avatar chip. |
| `brand-gold-ink` | `#8A6D1F` | Text on the light gold advisory panel. |

Gold is never used for body text on light backgrounds (fails contrast). On the light advisory panel use `#8A6D1F` instead.

### Colour — neutrals

| Token | Hex | Use |
| --- | --- | --- |
| `canvas` | `#EEF2F7` | Page background, inactive segmented-control track, progress track. |
| `surface` | `#FFFFFF` | Cards, table body, sidebar panels. |
| `surface-subtle` | `#F8FAFC` | Table header row, supervisor stat cards, assistant transcript box. |
| `border` | `#E2E8F0` | Card and panel borders. |
| `border-strong` | `#CBD5E1` | Secondary button borders, device frame borders. |
| `border-nav` | `#D8E0EA` | Top switcher bar border. |
| `divider` | `#F1F5F9` | Table row dividers, list item dividers. |
| `text` | `#1E293B` | Body default. |
| `text-strong` | `#334155` | Table cell primary text. |
| `text-muted` | `#475569` | Descriptive body copy. |
| `text-subtle` | `#64748B` | Secondary metadata. |
| `text-faint` | `#94A3B8` | Eyebrow labels, timestamps, table headers. |

### Colour — room / request status

Each status has a **solid** colour (dot, chip text, left border) and a **tint** (chip background).

| Status key | Solid | Tint | Labels used |
| --- | --- | --- | --- |
| `clean` | `#0F7B4F` | `#E7F4EE` | Ready, Done, Resolved |
| `dirty` | `#B45309` | `#FBF0E2` | To turn, Turn, Now, New |
| `occupied` | `#0065AB` | `#E5F0F9` | In house, Stayover, Stay, In progress |
| `dnd` | `#7C3AED` | `#F1EAFC` | Do not disturb, DND |
| `reserved` | `#0E7490` | `#E4F2F5` | Arriving 4 PM, 4:30 PM |
| `ooo` | `#B91C1C` | `#FBEAE9` | Blocked, Needs maintenance, Breached |

Additional accents used only inside the dark "Do this next" panel (on `#004986`):

| Hex | Use |
| --- | --- |
| `#34D399` | Positive watch chip dot ("12 rooms ready to sell") |
| `#F0705F` | Negative watch chip dot ("122 unassigned") |

Light gold advisory panel (supervisor view): background `#FDFBF4`, border `#E4D9B4`, left border `4px solid #D4AF37`.
Urgent table row background: `#FDFBF4`.

### Typography

Two families, loaded from Google Fonts:

```
Karla: 300, 400, 500, 600, 700 (+ 400 italic)
Playfair Display SC: 400, 700
```

- **Karla** — everything except page/panel headings. `-webkit-font-smoothing: antialiased`.
- **Playfair Display SC** (small caps serif) — `h1` page titles and `h2` "Do this next" headlines only. Always weight 700, colour `#004986` (or `#fff` on the dark panel).

| Role | Family | Size | Weight | Tracking | Other |
| --- | --- | --- | --- | --- | --- |
| Page title (`h1`) | Playfair Display SC | 2rem | 700 | -0.01em | |
| Supervisor title | Playfair Display SC | 1.7rem | 700 | — | |
| Panel headline (`h2`) | Playfair Display SC | 1.6rem (front desk) / 1.45rem (queue) | 700 | — | line-height 1.2–1.25, `text-wrap: pretty` |
| Eyebrow / section label | Karla | 0.6875rem | 700 | 0.18em | uppercase |
| Table column header | Karla | 0.62rem | 700 | 0.16em | uppercase |
| Nav rail label | Karla | 0.55rem | 700 | 0.1em | uppercase |
| Bottom tab label | Karla | 0.6rem | 700 | 0.1em | uppercase |
| Status chip | Karla | 0.65–0.7rem | 700 | 0.08em | uppercase |
| Big metric | Karla | 2rem | 600 | — | `font-variant-numeric: tabular-nums`, line-height 1 |
| Metric (supervisor / sidebar) | Karla | 1.8rem / 1.5rem | 600 | — | tabular-nums |
| Hero room number (mobile) | Karla | 3.4rem | 600 | — | tabular-nums, line-height 0.9 |
| Route room number | Karla | 1.5rem | 600 | — | tabular-nums |
| Queue room number | Karla | 1.4rem | 600 | — | tabular-nums |
| Table room number | Karla | 1.05rem | 600 | — | tabular-nums |
| Body copy | Karla | 0.88–0.92rem | 400 | — | line-height 1.5–1.55 |
| Table cell | Karla | 0.86rem | 400 | — | |
| Button | Karla | 0.8–0.85rem | 600 (secondary) / 700 (primary) | — | |
| Metadata / timestamp | Karla | 0.72–0.78rem | 400 | — | |

**All numeric figures use `font-variant-numeric: tabular-nums`.**

### Spacing

Prototype uses a 4px-derived scale in px. Nearest Tailwind equivalents in brackets.

`4` · `5` · `6` · `7` · `8` [2] · `9` · `10` · `11` · `12` [3] · `14` · `16` [4] · `18` · `20` [5] · `22` · `24` [6] · `26` · `28` [7] · `32` [8] · `36` · `56` [14]

Key applications:
- Main content padding: `28px 32px 56px`
- Card padding: `18px 20px` (sidebar), `22px 24px 28px` (supervisor body), `26px 28px` (dark panel)
- Table cell padding: `13px 20px` (first/last), `13px 12px` (middle); header `11px` equivalents
- Grid/flex gap: `16px` between major sections, `12px` between cards, `8px` between buttons

### Radii

| Value | Use |
| --- | --- |
| `6px` | Segmented control inner button |
| `7px` | Queue filter button |
| `8px` | Logo plate, top-bar switcher button, segmented control track, room chip (supervisor) |
| `10px` | Buttons, inputs, route list item, advisory buttons |
| `12px` | Nav rail icon tile, route list card, mobile primary CTA |
| `14px` | Stat card, queue list item, mobile shift card, supervisor cards |
| `16px` | Section cards, dark "Do this next" panel |
| `18px` | Mobile "Do this next" panel |
| `32px` | Phone device frame |
| `999px` | Pills, chips, dots, avatars, progress bars |

### Shadows

| Value | Use |
| --- | --- |
| `0 1px 2px rgba(0,0,0,0.06)` | Active segmented-control button |
| `0 20px 25px rgba(0,73,134,0.08)` | Supervisor tablet frame |
| `0 20px 25px rgba(0,73,134,0.1)` | Phone frame |

### Other

- Top switcher bar: `background: rgba(255,255,255,0.92)`, `backdrop-filter: blur(12px)`, `position: sticky; top: 0; z-index: 80`.
- Nav rail inactive icon tile: `rgba(255,255,255,0.12)`; active: `#D4AF37` with `#004986` icon.
- Nav rail inactive label: `rgba(255,255,255,0.6)`; active: `#fff`.
- Dark panel secondary button border: `rgba(255,255,255,0.35)`.
- Dark panel divider: `rgba(255,255,255,0.18)`.
- Dark panel body copy: `rgba(255,255,255,0.78)`.
- Watch chip on dark: `rgba(255,255,255,0.12)`.

---

## Screens / Views

The prototype wraps all three screens in one file with a top switcher bar (a prototyping affordance — **do not build it**; these are three routes).

### Shared: left nav rail (desktop)

Present on front desk and request queue. Housekeeping mobile uses a bottom tab bar instead.

- **Layout**: page is `grid-template-columns: 76px minmax(0,1fr)`. Rail is `min-height: 100vh`, `background: #004986`, `display: flex; flex-direction: column; align-items: center; gap: 26px; padding: 22px 0`.
- **Logo**: white plate, `width: 56px`, `border-radius: 8px`, `padding: 7px 6px`, logo image `width: 100%; height: auto`. The Days Inn logo is dark-on-transparent, so it **must** sit on a white plate — never invert it.
- **Items** (in order): Board, Map, Guests, Tickets, Shifts, Reports. Each is a 6px-gap vertical stack of a `40×40px` `border-radius: 12px` tile containing a 20px icon, plus the uppercase label.
- **Active state**: tile `#D4AF37`, icon `#004986`, label `#fff`. Inactive: tile `rgba(255,255,255,0.12)`, icon `#fff`, label `rgba(255,255,255,0.6)`.
- **Footer**: `margin-top: auto`, 34px circular gold avatar with initials in `#004986`, `0.72rem`, weight 700.
- **Lucide icons**: `LayoutPanelTop` (Board — 3-row rect), `Map` (Map), `Users` (Guests), `Wrench` (Tickets), `Calendar` (Shifts), `BarChart3` (Reports), `ListFilter` (Queue, on the request-queue rail).

Front desk shows Board active; request queue shows Queue active (a `ListFilter` item replacing Tickets' position in the active slot — see prototype).

---

### 1. Front desk board

**Route**: `/front-desk` · **Purpose**: the shift lead's home. Answer "what needs me right now?" in under three seconds, then support that with the room board.

**Layout**: nav rail + `<main style="padding: 28px 32px 56px">` as a 20px-gap vertical flex column of four blocks.

#### 1.1 Header row

`display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 20px`

- Eyebrow: `Front desk · Dana P. · Thursday, Aug 27 · 2:45 PM`
- `h1`: `Today at Wildwood I-75`
- Buttons (`white-space: nowrap`): **New booking** (primary, `#004986` bg, `#fff`, 700, `12px 20px`, radius 10) and **Export metrics** (secondary, `#fff` bg, `1px solid #CBD5E1`, `#004986`, 600)

#### 1.2 Do this next + metrics

`display: grid; grid-template-columns: minmax(0,1.15fr) minmax(0,1fr); gap: 16px`

**Left — dark action panel.** `background: #004986`, radius 16, padding `26px 28px`, `color: #fff`.

- Gold eyebrow: `Do this next`
- `h2` (Playfair SC 1.6rem): `3 arrivals in 90 minutes, 2 rooms not ready`
- Body (`max-width: 38rem`, `rgba(255,255,255,0.78)`): `Rooms 114 and 122 are still vacant dirty and both are assigned to arriving guests. Marisol is one room away from 114; room 122 is unassigned.`
- Buttons: **Prioritise 114 & 122** (gold, `#004986` text, 700), **Message housekeeping** (outline `rgba(255,255,255,0.35)`), **Dismiss** (text only, `rgba(255,255,255,0.65)`)
- Watch chips row, above a `rgba(255,255,255,0.18)` top divider, `padding-top: 16px`. Each is a pill (`rgba(255,255,255,0.12)`, `7px 13px`) with a 7px dot:
  - `Marisol finishing 113 · ~8 min` — dot `#D4AF37`
  - `122 unassigned` — dot `#F0705F`
  - `12 rooms ready to sell` — dot `#34D399`

**Right — 2×2 metric grid.** `gap: 12px`. Each card: `#fff`, `1px solid #E2E8F0`, radius 14, padding `18px 20px`. Eyebrow label, 2rem figure in `#004986`, then a `0.74rem` trend line in a semantic colour.

| Label | Value | Trend | Trend colour |
| --- | --- | --- | --- |
| Occupancy | 68% | +6 pts vs last Thursday | `#0F7B4F` |
| Ready to sell | 12 | 9 still to turn | `#B45309` |
| Avg turnover | 38m | 7m under target | `#0F7B4F` |
| Open requests | 5 | avg response 9m | `#64748B` |

#### 1.3 Room board + sidebar

`display: grid; grid-template-columns: minmax(0,1fr) minmax(260px,320px); gap: 16px; align-items: start`

**Room board card.** `#fff`, `1px solid #E2E8F0`, radius 16, `overflow: hidden`.

- Header bar (`padding: 16px 20px`, bottom `1px solid #F1F5F9`, `flex-wrap: wrap`, `justify-content: space-between`, `gap: 12px`):
  - Left: eyebrow `Room board · 58 rooms` + segmented control (track `#EEF2F7`, radius 8, `padding: 3px`; buttons `6px 12px`, radius 6, `0.72rem`; active `#fff` + `#004986` + 700 + shadow; inactive transparent + `#64748B` + 600). Options: **List** (active), **Grid**, **Map**.
  - Right: legend, `flex-wrap: wrap`, `gap: 12px`. Each entry: 8px dot + `0.72rem` weight-600 `#64748B` label + count in `#334155` tabular-nums. Ready 12 · To turn 9 · In house 28 · Arriving 5 · Blocked 1.
- Table, wrapped in `overflow-x: auto`; table `width: 100%; min-width: 560px; border-collapse: collapse`.
  - Header row `background: #F8FAFC`. Columns: Room, Status, Guest, Housekeeper, Wing, Updated (right-aligned).
  - Rows: `border-top: 1px solid #F1F5F9`, `cursor: pointer`. **Urgent rows get `background: #FDFBF4`**; all others `#fff`.
  - Room cell: `1.05rem`, weight 600, tabular-nums, `#004986`.
  - Status cell: pill with tint background, 7px dot, solid-colour text, `0.7rem`, weight 700, `padding: 5px 11px`.
  - Sorted by urgency, not room number. Footer strip (`border-top: 1px solid #F1F5F9`, `13px 20px`, `0.78rem`, `#94A3B8`): `Showing 12 of 58 · sorted by urgency`.

Row data (first three are the urgent/tinted rows):

| Room | Status | key | Guest | Housekeeper | Wing | Updated |
| --- | --- | --- | --- | --- | --- | --- |
| 114 | Arriving 4 PM | reserved | Nguyen | Marisol R. (next) | Main · Floor 1 | 6m ago |
| 122 | Arriving 4:30 PM | reserved | Ferris | Unassigned | Bldg 2 · Floor 1 | 9m ago |
| 118 | Needs maintenance | ooo | Barrett | — | Bldg 2 · Floor 1 | 12m ago |
| 113 | To turn | dirty | Departed 11:04 | Marisol R. | Main · Floor 1 | 3m ago |
| 119 | To turn | dirty | Departed 10:12 | Unassigned | Bldg 2 · Floor 1 | 31m ago |
| 202 | To turn | dirty | Departed 9:48 | Ana G. | Main · Floor 2 | 44m ago |
| 110 | Do not disturb | dnd | Whitfield | — | Main · Floor 1 | 1h ago |
| 111 | In house | occupied | Ortiz | — | Main · Floor 1 | 1h ago |
| 112 | Ready | clean | Available | Ana G. | Main · Floor 1 | 1h ago |
| 117 | Ready | clean | Available | Teresa L. | Main · Floor 1 | 4h ago |
| 121 | Ready | clean | Available | Teresa L. | Bldg 2 · Floor 1 | 4h ago |
| 116 | Blocked | ooo | AC unit ordered | — | Main · Floor 1 | 1d ago |

**Sidebar.** Three `#fff` cards, radius 16, `1px solid #E2E8F0`, `padding: 18px 20px`, `gap: 12px`.

1. **Guest requests · 5** — 14px-gap list. Each item: `Room {n}` (`0.9rem`, 700, `#004986`) + `{ago}` (`0.7rem`, `#94A3B8`) on a baseline-aligned row, then `0.81rem` `#475569` body. Footer button **Open request queue** (full width, `min-height: 42px`, outline, `#004986`, 700).
   - 118 / 12m ago / `AC running warm — room isn't cooling down.`
   - 214 / 4m ago / `Two bath towels and an extra pillow.`
   - 236 / 26m ago / `Housekeeping refresh, best after 3 PM.`
   - 142 / 41m ago / `Printed receipt for last night's stay.`
2. **Housekeeping progress** — per person: name (`0.86rem`, 600, `#334155`) + count (tabular-nums, `#64748B`), then a 5px `#EEF2F7` track with a `#D4AF37` fill. Marisol R. 4/11 (36%), Ana G. 7/12 (58%), Teresa L. 9/10 (90%).
3. **Arrivals & departures** — rows divided by `1px solid #F1F5F9`, `padding: 9px 0`. Each: kind label (`0.62rem`, 700, uppercase, tracking 0.1em, status colour) + room (600, tabular-nums, `#004986`) on the left, guest string `#64748B` on the right.
   - In 114 Nguyen · 4:00 PM (reserved) · In 122 Ferris · 4:30 PM (reserved) · In 207 Alvarez · 5:15 PM (reserved) · Out 205 Whitfield · checked out (dirty) · Out 119 Barrett · checked out (dirty)

---

### 2. Housekeeping workspace

**Route**: `/housekeeping` · **Purpose**: a housekeeper on a phone needs to know one thing — which room is next — and to close it out in one tap. A supervisor on a tablet needs to see and rebalance the whole turn plan.

The prototype shows both side by side (`display: flex; justify-content: center; gap: 32px; padding: 36px 32px 64px; flex-wrap: wrap`). **In production these are one responsive route**: the phone layout below a `lg` breakpoint, the supervisor layout at and above it.

#### 2.1 Phone route view (390px)

Frame: `width: 390px`, `1px solid #CBD5E1`, radius 32, `overflow: hidden`, `background: #EEF2F7`.

- **Header** (`#fff`, bottom `1px solid #E2E8F0`, `16px 20px`, space-between): logo at `height: 28px` (light background — no plate needed) and `Marisol R.` (`0.6875rem`, 700, uppercase, tracking 0.16em, `#94A3B8`) + 28px circular `#EEF2F7` avatar with `#004986` initials.
- **Do this next card**: `#004986`, radius 18, `padding: 22px`.
  - Gold eyebrow `Do this next` + right-aligned pill `Front desk priority` (`rgba(212,175,55,0.2)` bg, `#D4AF37` text, `0.65rem`, 700).
  - Room number `114` at `3.4rem`, weight 600, tabular-nums, line-height 0.9; beside it `Guest arriving 4 PM` (`0.78rem`, 700, uppercase, tracking 0.12em, `#D4AF37`).
  - Body: `Vacant dirty · 1 King · linens flagged. Main building, floor 1 — two doors from where you are.`
  - **Primary CTA `Start room 114`**: full width, `min-height: 54px`, gold, `#004986` text, `1.02rem`, 700, radius 12.
  - Below it a 2-column 8px-gap pair of `min-height: 46px` outline buttons: **Flag issue**, **Skip**.
- **Your shift card**: `#fff`, radius 14, `1px solid #E2E8F0`, `16px 18px`. Eyebrow + `4 of 11 done · since 8:02 AM`; 7px progress track `#EEF2F7` with 36% `#D4AF37` fill; then a 3-column mini-stat row (`0.62rem` label / `1.3rem` figure): To clean 9 · Yours left 7 · DND 3.
- **Rest of your route · 6**: 8px-gap list. Each item: `#fff`, radius 12, `1px solid #E2E8F0`, `padding: 14px 16px`, `display: flex; align-items: center; gap: 14px` — a `40×5px` rounded status bar, the room number at `1.5rem`, a flexible `0.8rem` `#64748B` note, and a status chip (tint bg, solid text, `0.65rem`, 700).

| Room | Chip | key | Note |
| --- | --- | --- | --- |
| 122 | 4:30 PM | reserved | Second arrival · needs a full turn |
| 113 | Turn | dirty | Departed 11:04 AM · linens flagged |
| 119 | Turn | dirty | Departed 10:12 AM · unassigned |
| 121 | Stayover | occupied | Linen change only · guest out until 6 |
| 110 | DND | dnd | Skip until the flag clears |
| 117 | Ready | clean | Inspected 10:48 AM · no action |

- **Bottom tab bar**: `#fff`, top `1px solid #E2E8F0`, four equal-flex items, each `min-height: 56px`, `padding: 12px 0 18px`, 5px-gap column of a 21px icon + `0.6rem` uppercase 700 label. Active `#004986`, inactive `#94A3B8`. Tabs: **Route** (`Footprints`), **Map** (`Map`), **Issues** (`Wrench`), **Shift** (`Clock`).

All touch targets are ≥46px; the primary action is 54px.

#### 2.2 Supervisor tablet view (820px)

Frame: `width: 820px`, `1px solid #CBD5E1`, radius 16, `overflow: hidden`, `#fff`.

- **Header bar**: `#004986`, `padding: 16px 24px`, space-between. Left: logo on a white plate (`border-radius: 8px`, `padding: 6px 10px`, logo `height: 26px`), a `20×1px` `rgba(255,255,255,0.28)` divider, then `Housekeeping · supervisor view`. Right: `Tablet & desktop` (`0.75rem`, `rgba(255,255,255,0.65)`).
- **Body** `padding: 22px 24px 28px`:
  - Header row: eyebrow `Thursday, Aug 27 · 2:45 PM`, `h1` `Turn plan` (Playfair SC 1.7rem); buttons **Auto-assign 9 rooms** (primary) and **Print sheet** (secondary).
  - **Light advisory panel** — the supervisor's "Do this next". `background: #FDFBF4`, `1px solid #E4D9B4`, `border-left: 4px solid #D4AF37`, radius 14, `padding: 18px 20px`. Eyebrow in `#8A6D1F`; then `1.02rem` weight-600 `#004986` copy: `Room 122 has a 4 PM arrival and no housekeeper assigned. Teresa finishes 209 in about 10 minutes.` Buttons: **Assign 122 to Teresa** (gold) and **See all unassigned** (`#fff` bg, `1px solid #D9C88E`, `#8A6D1F` text).
  - **4-column stat grid**, `gap: 12px`. Cards `#F8FAFC`, `1px solid #E2E8F0`, radius 14, `16px 18px`: label, `1.8rem` figure, `0.72rem` `#64748B` sub.
    - Rooms to turn 9 — `3 tied to arrivals`
    - Unassigned 2 — `122 and 119`
    - Avg turnover 38m — `target 45m`
    - Done today 20 — `of 29 planned`
  - **Assignments by housekeeper** — one card per person (`#fff`, radius 14, `1px solid #E2E8F0`, `16px 18px`, 10px gap):
    - Top row: 34px circular `#EEF2F7` avatar with initials, name (`0.95rem`, 700, `#004986`), zone (`0.74rem`, `#94A3B8`); right side count (`0.78rem`, tabular-nums) + a `140×6px` `#EEF2F7` track with `#D4AF37` fill.
    - Below: wrapping row of room chips — `1px solid #E2E8F0`, radius 10, tint background, `padding: 8px 12px`; 8px status dot + room number (`0.85rem`, 600, tabular-nums, `#004986`) + short status (`0.64rem`, 700, uppercase, status colour).

| Person | Zone | Done | Rooms |
| --- | --- | --- | --- |
| MR · Marisol R. | Main building · Floor 1 | 4 / 11 (36%) | 114 4 PM (reserved), 113 Turn (dirty), 121 Stay (occupied), 117 Done (clean), 110 DND (dnd) |
| AG · Ana G. | Main building · Floor 2 | 7 / 12 (58%) | 202 Turn, 205 Turn (dirty), 201 Done, 206 Done (clean), 209 DND (dnd) |
| TL · Teresa L. | Building 2 · Floors 1–2 | 9 / 10 (90%) | 209 Now (dirty), 121 Done, 124 Done (clean), 125 Stay (occupied) |

---

### 3. Request queue

**Route**: `/staff` · **Purpose**: triage. The SLA breach is the headline; the rest of the queue is a scannable list. Ops Assistant and system status move out of the primary column into the sidebar.

**Layout**: nav rail + `<main style="padding: 28px 32px 56px; display: grid; grid-template-columns: minmax(0,1fr) 330px; gap: 20px; align-items: start">`.

#### 3.1 Primary column (16px-gap flex column)

- **Header row**: eyebrow `Live shift · 5 open · avg response 9m`, `h1` `Request queue`; on the right a filter group — `#fff`, `1px solid #E2E8F0`, radius 10, `padding: 4px`, 6px gap. Buttons `9px 15px`, radius 7, `0.78rem`, 700. Active `#004986` bg + `#fff`; inactive transparent + `#475569`. Options: **Urgent 3** (active), **New 3**, **In progress 2**, **Done 11**.
- **Do this next panel**: `#004986`, radius 16, `padding: 24px 26px`.
  - Gold eyebrow; `h2` (Playfair SC 1.45rem): `Room 118 has waited 12 minutes with no AC`
  - Body (`max-width: 40rem`): `Maintenance requests over 10 minutes breach the service standard. Luis is free and two buildings away.`
  - Buttons: **Dispatch Luis to 118** (gold), **Call guest** (outline).
- **Queue list** — 10px-gap `<ul>`. Each `<li>`: `#fff`, radius 14, `1px solid #E2E8F0`, **`border-left: 4px solid {status colour}`**, `padding: 18px 20px`.
  - Left block: baseline row of room number (`1.4rem`, 600, tabular-nums, `#004986`), type (`0.95rem`, `#334155`), status chip (tint bg, `0.68rem`, 700, uppercase), meta (`0.74rem`, `#94A3B8`). Then body copy (`0.88rem`, `#475569`, `max-width: 40rem`) and a `0.74rem` `#94A3B8` timeline line.
  - Right block: two `min-height: 42px` buttons — primary (`#004986`, `#fff`, 700) and secondary (outline, `#004986`, 600).

| Room | Type | Status | key | Meta | Body | Timeline | Buttons |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 118 | Maintenance & Repairs | Breached | ooo | Barrett · 12m ago | AC is running warm — room isn't cooling down. | Opened 2:33 PM · not yet assigned | Dispatch / Call guest |
| 214 | Fresh Towels & Linens | New | dirty | Whitfield · 4m ago | Two bath towels and an extra pillow, please. | Opened 2:41 PM | Start / Assign |
| 236 | Housekeeping Refresh | In progress | occupied | Reyes · 26m ago | Best time to stop by is after 3 PM. | Started 2:26 PM · Marisol R. | Complete / Reassign |
| 142 | Front Desk Assistance | In progress | occupied | 41m ago | Need a printed receipt for last night's stay. | Started 2:11 PM · Dana P. | Complete / Reassign |
| 205 | Late Checkout | Done | clean | Nguyen · 2h ago | 1:00 PM departure approved. | Resolved 1:04 PM · Dana P. | Reopen / Notes |

#### 3.2 Sidebar (330px, 12px gap)

Three `#fff` cards, radius 16, `1px solid #E2E8F0`, `padding: 18px 20px`.

1. **Today** — 2×2 grid, 14px gap. `0.62rem` labels / `1.5rem` figures: Opened 16 · Resolved 11 · Avg response 9m · Breached 1.
2. **Ops Assistant** — eyebrow with a 15px `#D4AF37` `Sparkles` icon. Transcript box: `#F8FAFC`, `1px solid #E2E8F0`, radius 12, `padding: 14px`, `0.85rem` `#475569` — `Ask about rooms, requests, or tell me to update a status.` Below: an input (`min-height: 42px`, `1px solid #CBD5E1`, radius 10, placeholder `Ask about rooms or requests…`) plus a `#004986` send button (Lucide `Send`, 16px, `#fff`). Then suggestion pills (`#fff`, `1px solid #E2E8F0`, radius 999, `7px 12px`, `0.74rem`, 600, `#475569`): `Property summary`, `Dirty rooms on floor 2`, `Mark 214 clean`.
3. **System** — 9px-gap `0.8rem` rows, label `#64748B` left, value right: Database → 8px dot + `Configured` (`#0F7B4F`, 700); Mode → dot + `Live production` (`#0065AB`, 700); Last sync → `12s ago` (`#94A3B8`).

---

## Interactions & Behavior

### Navigation
- Nav rail items navigate between staff routes. Active item is derived from the current route.
- Bottom tab bar (phone) switches housekeeping sub-views: Route (default), Map, Issues, Shift.
- Segmented control on the room board switches List / Grid / Map presentation of the same 58 rooms — client-side, no refetch.

### Do this next panel
This is a computed recommendation, not a static banner. Derivation rules, in priority order:

1. **Arrival at risk** — any room with an arrival inside 120 minutes whose status is not `clean`. Headline counts arrivals in window and unready rooms. Actions: prioritise those rooms in the housekeeping queue; message housekeeping.
2. **SLA breach** — any open request older than its type threshold (maintenance 10 min; others 20 min). Headline names room and elapsed time. Action: dispatch the nearest free staff member.
3. **Unassigned urgent room** — a room tied to an arrival with no housekeeper. Action: assign to the housekeeper closest to finishing.

If none fire, the panel shows a calm state instead of being hidden — keep the slot occupied so the layout doesn't jump.

- **Prioritise / Assign / Dispatch** → optimistic mutation, panel recomputes, affected table rows lose their `#FDFBF4` tint.
- **Dismiss** → hides for the remainder of the shift (persist per user + shift, not globally).
- **Message housekeeping** → opens the existing messaging surface prefilled with the room numbers.

### Room table
- Sorted by urgency (arrival proximity → SLA breach → time since departure), not room number. Column headers do not currently sort — if the codebase already has sortable tables, wiring them is fine, but urgency must remain the default.
- Row click opens the existing room detail panel.
- Rows meeting an urgency rule get `background: #FDFBF4`. Tint is the only row treatment — no extra icons or badges.
- Horizontal scroll (`overflow-x: auto`, `min-width: 560px`) below ~900px of available width.

### Housekeeping phone view
- **Start room 114** → sets the room in progress, starts the timer, advances the card to the in-progress state with a **Mark clean** action.
- **Flag issue** → opens a maintenance ticket prefilled with the room.
- **Skip** → moves the room to the end of the route and promotes the next one. Requires a reason for arrival-linked rooms.
- Route list reorders live when the front desk reprioritises; animate reorder rather than hard-swapping.
- Progress bar and mini-stats update on every completion.

### Supervisor view
- **Auto-assign 9 rooms** → distributes unassigned rooms by zone and current load; show the proposed assignment for confirmation before committing.
- **Assign 122 to Teresa** → single-click accept of the recommendation.
- Room chips are drag-and-drop between housekeeper cards (nice-to-have; a menu-based reassign is an acceptable first pass).

### Request queue
- Filter buttons filter client-side; counts come from the full set, not the filtered view.
- Primary/secondary button labels are state-dependent (New → Start/Assign; In progress → Complete/Reassign; Done → Reopen/Notes; Breached → Dispatch/Call guest).
- Ops Assistant: existing behaviour, just relocated. Suggestion pills populate the input.

### Live updates
All three screens are live boards. Room status, request state, and progress push in over the existing realtime channel. On update: recompute the recommendation, re-sort the table, and update counts — no full reload, no scroll jump.

### Hover / focus states
Not specified in the prototype (inline styles). Apply the codebase's existing conventions:
- Cards and rows that are clickable: subtle background shift toward `#F8FAFC`.
- Primary buttons: darken `#004986` ~8%; gold buttons darken `#D4AF37` ~8%.
- Secondary buttons: border to `#94A3B8`.
- All interactive elements need a visible focus ring — use the existing ring token, keyed to `#004986`.

### Responsive behaviour

| Breakpoint | Front desk | Housekeeping | Request queue |
| --- | --- | --- | --- |
| ≥1280px | As specified | Supervisor view | As specified |
| 1024–1280px | Metrics grid drops to 1 column beside the panel; sidebar to 260px | Supervisor view, stats to 2 columns | Sidebar to 280px |
| 768–1024px | Panel + metrics stack; table scrolls horizontally; sidebar moves below | Supervisor view, single-column assignments | Sidebar below primary column |
| <768px | Rail becomes a bottom tab bar; cards single-column | Phone route view | Single column, filters scroll horizontally |

---

## State Management

Presentation state (new):

| State | Type | Notes |
| --- | --- | --- |
| `roomBoardView` | `'list' \| 'grid' \| 'map'` | Segmented control. Default `list`. |
| `queueFilter` | `'urgent' \| 'new' \| 'in_progress' \| 'done'` | Default `urgent`. |
| `recommendationDismissed` | `boolean` | Scoped to user + shift. |
| `activeRouteRoom` | `roomId \| null` | Phone view's current room. |
| `hkTab` | `'route' \| 'map' \| 'issues' \| 'shift'` | Phone bottom tabs. |

Derived (compute, don't store):

- `recommendation` — from rooms + requests + arrivals, per the rules above.
- `urgentRoomIds` — drives the row tint.
- `sortedRooms` — urgency sort.
- `housekeeperProgress` — done/total per person.
- `queueCounts` — per-status counts from the unfiltered set.

Data requirements (all already present in the source app — no new endpoints):

- Rooms with status, wing, floor, bed type, assigned housekeeper, last-updated timestamp.
- Requests with type, status, room, guest, opened/started/resolved timestamps and actor.
- Reservations with arrival/departure times and assigned rooms.
- Staff with role, zone, and current assignments.
- Realtime subscription on rooms and requests.

---

## Assets

| Asset | Source | Notes |
| --- | --- | --- |
| `assets/days-inn-logo.png` | `public/__l5e/assets-v1/…/days-inn-logo.png` in `rulloa1/daysinn` | 1045×543 PNG with transparency. Dark artwork — **always place on a white plate**, never `filter: invert()`. |

Property photography (`property.webp`, `room.webp`, `lobby.webp`, etc.) exists in the repo but is **not used** on the staff screens — ops boards carry no imagery by design.

Icons: `lucide-react`, already a dependency. Used: `LayoutPanelTop`, `Map`, `Users`, `Wrench`, `Calendar`, `BarChart3`, `ListFilter`, `Footprints`, `Clock`, `Sparkles`, `Send`.

---

## Files

| File | What it is |
| --- | --- |
| `Ops Portal v2.dc.html` | **The design to build.** Direction 1c, all three screens. Switch screens with the top bar. |
| `Ops Portal - Current.dc.html` | Recreation of the current build, for before/after comparison. |
| `Ops Portal - Redesign.dc.html` | The three explored directions side by side (1a light board, 1b Congress Blue deep, 1c selected). |
| `assets/days-inn-logo.png` | Logo asset. |
| `support.js` | Prototyping runtime. **Not part of the design** — ignore it. |
| `screenshots/01-front-desk-board.png` | Front desk board, top of screen. |
| `screenshots/02-housekeeping-phone.png` | Housekeeping phone route view. |
| `screenshots/03-request-queue.png` | Request queue, top of screen. |

Screenshots are captured at the prototype preview width and show the top of each screen — open the HTML files for the full layouts.

Open the HTML files directly in a browser. The three-way switcher at the top of `Ops Portal v2.dc.html` is a prototyping affordance only.

### Where this lands in the source repo

| Design screen | Repo file |
| --- | --- |
| Front desk board | `src/routes/front-desk.tsx` |
| Housekeeping (phone + supervisor) | `src/routes/housekeeping.tsx` |
| Request queue | `src/routes/staff.tsx` |
| Colour + font tokens | `src/styles.css` (`@theme` block) |
| Shared primitives | `src/components/ui/button.tsx`, `badge.tsx`, `card.tsx` |
| Brand lockup | `src/components/brand-lockup.tsx` |

The existing `src/styles.css` theme uses off-brand indigo/amber values. Replacing those tokens with the palette in this document will shift the guest-facing screens too — decide up front whether to migrate the whole app or scope new tokens to the staff routes.
