# Kiosk UI Design Specification
**SMMK Hydra · Sprint 4**
**Status: DRAFT — pending museum staff sign-off (Task 4.4 / 4.5)**

---

## 1. Device Target

| Parameter | Value |
|-----------|-------|
| Device | Tablet or all-in-one touch screen, wall-mounted |
| Resolution target | 1280 × 800 px (landscape) |
| Pixel density | ≥ 1.5× (retina-class) |
| Input | Touch only — no keyboard, no mouse |
| Orientation | Landscape fixed |
| OS | Linux / Chrome kiosk mode or Android kiosk launcher |

---

## 2. Colour Palette

| Token | Hex | Usage |
|-------|-----|-------|
| `--clr-header` | `#1e3a5f` | App bar, edit panel header, active state |
| `--clr-bg` | `#f0f4f8` | Page background |
| `--clr-card` | `#ffffff` | Booking cards, panels |
| `--clr-accent` | `#0ea5e9` | Focus rings, links, confirm actions |
| `--clr-upcoming` | `#2563eb` | Upcoming booking left border / badge |
| `--clr-complete` | `#16a34a` | Completed booking left border / badge |
| `--clr-no-show` | `#dc2626` | No-show booking left border / badge |
| `--clr-block` | `#92400e` | Blockage type left border / badge |
| `--clr-warn` | `#f59e0b` | PAX mismatch warning, approximate badge |
| `--clr-offline` | `#dc2626` | Offline banner background |
| `--clr-muted` | `#64748b` | Secondary text, labels |
| `--clr-border` | `#e2e8f0` | Dividers, card borders |
| `--screen-dark` | `#0f172a` | Screensaver / key-entry background |

Status pill backgrounds are 10% opacity versions of the above status colours (e.g. `#dbeafe` for upcoming).

---

## 3. Typography

| Role | Size | Weight | Notes |
|------|------|--------|-------|
| Page / section title | 22 px | 700 | Used in header blocks |
| Booking time (list) | 28 px | 700 | Monospace numeric |
| Booking time (panel) | 28 px | 700 | |
| Screensaver clock | 120 px | 200 | Large, thin, high legibility |
| Section labels (caps) | 12 px | 700 | All-caps, letter-spacing .08em |
| Body / form labels | 15–18 px | 400–600 | Base is 18 px |
| PAX stepper value | 22 px | 700 | Tabular numeric |
| Badge / chip | 12–13 px | 600 | Pill-shaped |
| Notes / secondary | 14 px | 400 | |

Font stack: `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif`

---

## 4. Touch Target Sizes

**Minimum 56 px on any interactive element (WCAG 2.5.5 — AAA).**

| Element | Size | Notes |
|---------|------|-------|
| Booking card | min-height 56 px | Full-width tap area |
| PAX stepper button (large) | 48 × 56 px | Each of ±1 / ±10 |
| Language group stepper button | 36 × 36 px | Smaller, but grouped |
| Status toggle button | flex × 56 px | Three equal columns |
| Numpad key | full-width × 80 px | 3-column grid |
| Nav / header button | 44 × 44 px | Minimum acceptable for secondary controls |
| Edit panel close (✕) | 40 × 40 px | Acceptable: infrequent action |

---

## 5. Layout Structure

### Day View (`day-view.html`)

```
┌─────────────────────────────────────────────────────────────┐
│ [LOGO]          [Date / Weekday]      [Screensaver] [Lock]  │  72 px  header
├─────────────────────────────────────────────────────────────┤
│ [All] [Hafenrundfahrt] [Kino-Tour] [Nachttour]              │  52 px  cal-tabs
├──────────────────────────────┬──────────────────────────────┤
│                              │                              │
│  Scrollable booking list     │  Slide-in edit panel         │
│  (fills remaining width      │  440 px fixed width          │
│   when panel is closed)      │  (hidden when no selection)  │
│                              │                              │
└──────────────────────────────┴──────────────────────────────┘
```

- Edit panel slides in from the right with CSS `transform: translateX(100%)` → `translateX(0)`, 300 ms ease transition
- Booking list gets `margin-right: 440px` when panel opens (same 300 ms transition) so content isn't obscured
- Booking list scroll position is preserved when opening/closing the panel

### Screensaver (`screensaver.html`)

```
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│                    [HH:MM]  ← 120px clock                   │
│                [Weekday, D. Month YYYY]                     │
│                [SMMK · Stadtmuseum Hamburg]                 │
│                                                             │
│  ─────────────────────────────────────────────────────────  │
│                [Bildschirm berühren…]  ← pulsing            │
│                                                             │
│  Nächste Buchungen heute                                    │
│  [Card 1]           [Card 2]           [Card 3]             │
└─────────────────────────────────────────────────────────────┘
```

---

## 6. Edit Panel Sections & Order

When a tour booking (not a block) is selected, the panel shows sections in this order:

1. **Time range** — large text, e.g. "10:30 – 12:00"
2. **Customer name** — muted, secondary
3. **Status toggle** — three-button segmented control (Bevorstehend / Abgeschlossen / Nicht erschienen)
4. **Participant numbers** — three steppers (Gebucht / Erschienen / Abgerechnet) + PAX mismatch warning banner (if applicable) + "ungefähre Angabe" checkbox
5. **Language groups** — table with inline per-language steppers
6. **Late arrival (Verspätung)** — stepper in 5-minute steps (0–120 min)
7. **Notes** — free-text textarea
8. **"Open full form"** — link to the full Symfony edit page for the booking (for fields not exposed in the kiosk panel)

For **block** bookings: only the time range, a read-only reason field, and notes are shown.

---

## 7. Interaction Specifications

### 7.1 PAX Steppers

Three steppers for a tour booking:

| Stepper | Field | Increment buttons | Min | Notes |
|---------|-------|-------------------|-----|-------|
| Gebucht | `booked_pax` | −10 / −1 / +1 / +10 | 0 | Set at booking creation |
| Erschienen | `shown_pax` | −10 / −1 / +1 / +10 | 0 | Updated on the day |
| Abgerechnet | `billed_pax` | −10 / −1 / +1 / +10 | 0 | May differ (discounts, no-shows) |

Behaviour:
- Values cannot go below 0
- Tapping any stepper button updates the in-memory state immediately and re-renders the value text without a full panel re-render (performance)
- The PAX mismatch warning re-evaluates after every change to `shown_pax` or any language-group stepper

### 7.2 Language Group Steppers

Smaller inline steppers (−5 / −1 value +1 / +5) per language row.
- Value cannot go below 0
- After each change: recalculate `sum(lang_groups.pax)` and compare to `shown_pax`

### 7.3 PAX Mismatch Warning

Shown as a yellow banner **above** the PAX steppers when:
```
sum(language_groups[].pax) ≠ shown_pax  AND  shown_pax is not null  AND  language_groups is non-empty
```
Banner text: "Summe Sprachgruppen ({sum}) ≠ Erschienene PAX ({shown_pax})"
- Warning does **not** prevent saving — it is informational only
- Disappears as soon as the condition clears

### 7.4 Status Toggle

Three-state segmented button:
- **Bevorstehend** → highlights blue (`#dbeafe` background, `#2563eb` text)
- **Abgeschlossen** → highlights green (`#dcfce7` background, `#16a34a` text)
- **Nicht erschienen** → highlights red (`#fee2e2` background, `#dc2626` text)

Tapping a state applies it immediately to the in-memory edit state (no server call yet). The booking card in the list is **not** updated until Save is tapped.

### 7.5 Late Arrival Stepper

Stepper with −10 / −5 / value / +5 / +10 buttons, minimum 0, no maximum.
Displayed as "{N} min". A value of 0 displays "0 min" and means no late arrival recorded.

### 7.6 Save / Cancel

- **Speichern**: writes the edit state back to the local data store, closes the panel, shows a 2-second "✓ Gespeichert" toast at bottom of screen, triggers a background sync to the Symfony API
- **Abbrechen**: discards all in-memory changes, closes the panel without any server call
- Both buttons are always visible at the bottom of the panel in a sticky footer bar

### 7.7 Screensaver Trigger

- Screensaver activates after **5 minutes** of no touch input (configurable via kiosk config)
- Any touch on the screensaver navigates to the Day View (with ripple animation feedback)
- The screensaver clock updates every second
- If the device is also offline, the screensaver shows an additional small "⚡ Offline" chip in the bottom-right

### 7.8 Lock / Key Entry

- Lock button in header navigates to `key-entry.html`
- 6-digit numeric PIN (configured in kiosk settings, validated server-side in production)
- Incorrect PIN: shake animation on card, PIN dots turn red, entry is cleared after 600 ms
- Correct PIN: brief green overlay flash → navigate to Day View
- After 3 failed attempts: 30-second lockout with countdown (production requirement, not in this mockup)

---

## 8. Offline Behaviour

| State | Behaviour |
|-------|-----------|
| Online | Normal operation; changes synced immediately |
| Offline detected | Red banner appears at top; edit panel still opens; saves go to local queue |
| Offline modal | Shown on app start if no server connection can be established; booking list is read-only from last cached data |
| Reconnect | Banner disappears; queued saves are synced in order; success toast shown per save |

Local data is cached using the browser's `localStorage` or `IndexedDB` (implementation detail for Sprint 5).

---

## 9. Accessibility Considerations

- All interactive elements have ≥ 56 px touch target (except secondary controls which are ≥ 44 px)
- Colour is never the only indicator — status pills use both colour and text label
- Screensaver has "Bildschirm berühren zum Aktivieren" text (not just a visual cue)
- High contrast text: dark text on light backgrounds, white text on dark; no low-contrast grey-on-grey
- Font size ≥ 14 px throughout; primary content ≥ 17 px

---

## 10. Sign-off Checklist (Task 4.5)

To be completed after the review session with museum staff (Task 4.4).

- [ ] Touch target sizes confirmed acceptable on physical device
- [ ] Screensaver timeout duration agreed (currently: 5 minutes)
- [ ] PIN length and lockout policy agreed
- [ ] Status labels confirmed (Bevorstehend / Abgeschlossen / Nicht erschienen)
- [ ] Stepper increments confirmed (1 / 10 for PAX; 5 / 10 for late arrival)
- [ ] Calendar colour coding confirmed with actual calendar names
- [ ] Offline banner wording approved
- [ ] Language list for language group badges confirmed (DE, EN, FR, TR, …)
- [ ] "Vollständiges Formular" link target confirmed (→ full Symfony edit page)
- [ ] Font size legible at expected viewing distance from mounted screen
- [ ] Mockups signed off by museum coordinator: ____________________  Date: ______

---

*Generated as part of Sprint 4. For questions contact the development team.*
