On this page 00 — Day brief · 2026-08-19 · MATIZ 0%

00-day-brief.md

00 — Day brief · 2026-08-19 · MATIZ

Decisiones ALS-2 BRIEF

CampoValor
Fecha2026-08-19
NombreMATIZ
ComplejidadNivel 2 (no L1 consecutivo tras ZAGUÁN)
SectorBelleza / oficio de colorista (salón de barrio, no reserva de cita)
TipoPWA cuaderno de fórmulas + fichas + editor de fórmula + JWT COLORISTA
PlataformaPWA mobile-first (bottom tabs) · editor también en desktop
RegistroR-HY — chrome de herramienta (tabs) + receta editorial (capilla)
ShellS-BOTTOM — Hoy · Fichas · Cuenta
HomeH-BENTO — sillas de hoy + última fórmula + swatches
FlujoF-EDIT — el núcleo es escribir y guardar una fórmula, no una cola
Por qué no ZAGUÁNNo S-WIZ / H-WIZARD / F-CMP; no inmobiliaria; no wizard
Por qué no MARGENNo S-TOP / H-GALLERY / F-STORY; no editorial; no lookbook
Por qué no CESTANo S-RAIL / H-TABLE / F-LEAD; no ONG
Por qué no CANTERAS-BOTTOM se reutiliza a propósito (cooldown 5: CANTERA es N−8); F-EDIT ≠ F-ONB; belleza ≠ parte de obra

Terna unicidad

R-HY · S-BOTTOM · H-BENTO · F-EDIT

0 códigos iguales a N−1 (ZAGUÁN: R-IN · S-WIZ · H-WIZARD · F-CMP).
≥2 distintos vs N−2 (MARGEN: R-CL · S-TOP · H-GALLERY · F-STORY) y N−3 (CESTA: R-CL · S-RAIL · H-TABLE · F-LEAD).
Ventana 4: R-IN + R-CL + R-CL + R-HY (hay clásico e innovador).

S-BOTTOM no está en los últimos 5 (S-WIZ, S-TOP, S-RAIL, S-DOCK, S-SPLIT). H-BENTO y F-EDIT tampoco.

Ground (CRON §5.6)

  • Familia: G-CHAPEL
  • Hex: #E6E4ED
  • Por qué: yeso de capilla, no hormigón (ZAGUÁN), no blanco de kiosco (MARGEN), no musgo (CESTA), no cromo (RONDA), no agua (DERIVA). El acento es amatista de vidriera, no óxido ni cadmio.

Mood visual (Paper)

  • Candidatos: candlelit (primer instinto salón), chapel, bookish, nocturnal, mineral
  • Elegido: chapel — no el primer instinto candlelit (ámbar × oxblood de “spa de lujo”). Un salón de Usera no es un spa; es un oficio con luz de vidriera y pizarra.
  • Paleta:
    • #E6E4ED yeso de capilla (ground)
    • #2B2838 pizarra del banco
    • #6B4C9A amatista de vidriera
    • #F7F5FA lino de card
    • #6E6A78 peltre
    • #C4A35A hoja de oro (acento secundario, poco)
  • Tipo: Libre Baskerville display · Nunito Sans UI (evita Instrument Serif / Sora / Cormorant / Public Sans / Spectral / Manrope / Zilla / Mulish / Lora / Karla / Newsreader / Outfit)

Alcance L2 must

  1. Login JWT COLORISTA (Nora Beltrán, Salón Amparo)
  2. Hoy — bento: 3 sillas de hoy + última fórmula + aviso de oxidante
  3. Fichas — lista de clientas con último matiz
  4. Ficha de clienta + historial de fórmulas
  5. Editor de fórmula (must-have inventivo): líneas de color (marca, tono, gramos) + oxidante 10/20/30 vol + tiempo + notas + “guardar ≠ aplicar”
  6. Success / empty / error / loading
  7. Cuenta (salón, rol, cerrar sesión)
  8. ≥12 UX + ≥10 UI

Microcopy duro

  • Fórmula ≠ receta comercial. 6/7 + 20 vol no es “el bote de la caja”.
  • Ficha ≠ cita. MATIZ no agenda. La silla de hoy es un asiento, no un slot Booksy.
  • Guardado ≠ aplicado. Guardar la fórmula no significa que el color esté en el pelo.
  • MATIZ no cobra. El ticket lo hace la caja; el producto no es un TPV.

Supuestos

  • S1: Un salón de barrio tiene 1–2 coloristas y 40–80 fichas vivas; la demo tiene 1 COLORISTA + 5 clientas + 8 fórmulas.
  • S2: La colorista escribe la fórmula en el móvil, junto al lavacabezas, con las manos aún húmedas: bottom tabs, targets grandes, sin wizard.
  • S3: No hay reserva online ni pasarela. El “hoy” lo rellena Nora por la mañana (quién viene), no la clienta.
  • S4: La dueña (Inma) no usa el producto el día 1; el JWT es solo COLORISTA.

Hipótesis

IDSeñal
H1Un editor de gramos + oxidante reduce “WhatsApp + foto del tazón”
H2Separar ficha de cita evita que MATIZ se lea como Booksy
H3JWT solo en colorista basta; la clienta no necesita cuenta

Anti-patrones evadidos

  • AP-12: no hero 2-col + 3 cards + lista badges
  • AP-16: ≥10 UI incluyendo cada estado y el editor completo
  • AP-20: ground chapel, no beige por inercia
  • AP-21: Hoy bento con 3 sillas reales en el fold (nombre, silla, último matiz)

00-paper-reference.md

Paper reference · MATIZ

URLhttps://app.paper.design/file/01M0CDJZPXVWCVYZC5Z5V9ZRQE
File ID01M0CDJZPXVWCVYZC5Z5V9ZRQE

UX-count: 12 UI-count: 12

UX-00 Cover · UX-01 Stakeholders · UX-02 Personas · UX-03 JTBD · UX-04 Stories · UX-05 Journey · UX-06 Blueprint · UX-07 Site map · UX-08 Flujos · UX-09 Datos+permisos · UX-10 Métricas · UX-11 Research UI-00 Tokens · UI-01 Login · UI-02 Hoy bento · UI-03 Fichas · UI-04 Ficha · UI-05 Editor móvil · UI-06 Editor desktop · UI-07 Success · UI-08 Empty · UI-09 Error · UI-10 Loading · UI-11 Cuenta

UX process (prefijo UX- · mínimo 12)

IDNombreContenido
UX-00CoverPortada MATIZ · eslogan «La fórmula, no la cita.» · terna R-HY · S-BOTTOM · H-BENTO · F-EDIT · L2 belleza oficio · mood chapel · 2026-08-19
UX-01StakeholdersCOLORISTA (Nora), DUEÑA (Inma, no JWT día 1), clientas (Marisol, Lucía, Amina, Pilar), caja — no Booksy
UX-02PersonasNora Beltrán 34 colorista · Inma Soto 52 dueña · Marisol Vega 47 clienta (no usa la app)
UX-03JTBDJob «escribir los gramos antes de que se me olvide el tazón» + job «ver qué le hice hace 8 semanas»
UX-04StoriesMust: login, hoy, fichas, ficha, editor, success, empty/error/loading, cuenta
UX-05JourneyAbrir Hoy → silla de Marisol → última fórmula → nueva fórmula → guardar ≠ aplicar
UX-06BlueprintFrontstage móvil junto al lavacabezas · backstage caja y cita de WhatsApp · Nest/Neon/JWT
UX-07Site map / IAAuth-first PWA; tabs Hoy · Fichas · Cuenta; editor contextual
UX-08FlujosF-EDIT: ficha → editor → POST fórmula; F-DESK: login → hoy
UX-09Datos + permisosUser COLORISTA · Client · Formula · Sitting; JWT en todo salvo login
UX-10MétricasNorth star: % de sillas de hoy con fórmula guardada el mismo día
UX-11Research notesComprobado / supuesto / hipótesis / decisión — sin entrevistas inventadas

UI producto (prefijo UI- · mínimo 10)

IDNombreFlujo
UI-00TokensDS chapel: yeso, pizarra, amatista, lino, peltre, oro; Libre Baskerville + Nunito Sans
UI-01LoginAcceso JWT COLORISTA. nora@matiz.salon / password123
UI-02Hoy bentoH-BENTO: 3 sillas + última fórmula + aviso oxidante. Lead + 3 datos en fold
UI-03FichasLista clientas con último matiz
UI-04FichaHistorial + CTA nueva fórmula. Ficha ≠ cita
UI-05Editor móvilF-EDIT: líneas de color + oxidante + tiempo + notas
UI-06Editor desktopMisma receta en 1440; staff secundario
UI-07SuccessGuardado ≠ aplicado
UI-08EmptySin sillas / sin fichas
UI-09ErrorRed / validación
UI-10LoadingSkeletons bento
UI-11CuentaSalón Amparo, rol, salir

Regla: el núcleo F-EDIT ocupa UI-05 + UI-06. Hoy es H-BENTO denso (AP-21).

1. Mapa de canvas (bandas)

§BandaPropósitoArtboards
1UX PROCESSModelo de oficio coloristaUX-00…UX-11
2DESIGN SYSTEMTokens chapelUI-00
3PWA COLORISTALogin, hoy, fichas, ficha, editor, estadosUI-01…05, 07–11
4DESKTOPEditor 1440UI-06

Layout canvas: origen (0,0) · gaps ~100–120px · UX 3×4 · móviles en 2 filas · desktop editor debajo. D-P0-04 / D-P1-04.

2. Mapeo Paper → Angular

ArtboardRuta appComponente
UI-00 Tokenstokens Tailwind + fuentes
UI-01 Login/loginLoginPage
UI-02 Hoy/HoyPage
UI-03 Fichas/fichasFichasPage
UI-04 Ficha/fichas/:slugFichaPage
UI-05 Editor/fichas/:slug/formulaFormulaPage
UI-06 Editor deskmisma ruta desktopFormulaPage
UI-07 Success/ok/:idOkPage
UI-08 EmptyHoy/Fichas 0estados
UI-09 Errortoast / pageestados
UI-10 Loadingskeletonsestados
UI-11 Cuenta/cuentaCuentaPage

01-project-definition.md

01 — Definición de proyecto — MATIZ

1. Identidad

CampoValor
NombreMATIZ
SignificadoMatiz es el matiz del color y el matiz del oficio: un tono, no una receta de caja. Ni el Booksy del barrio ni el TPV de la caja. Se entra a escribir gramos; se sale con una fórmula guardada.
Eslogan«La fórmula, no la cita.»
Una frasePWA auth-first: cuaderno de fórmulas + fichas + editor + JWT COLORISTA.
SectorBelleza / oficio de colorista (salón de barrio, no reserva de cita)
TipoPWA L2 — Hoy bento + fichas + editor de fórmula + cuenta — Nivel 2, herramienta de oficio usable
PlataformaPWA mobile-first (bottom tabs) · editor también en desktop
Mercado demoEspaña · Madrid · Usera · Salón Amparo, Calle Amparo 22 (un solo salón, no cadena)
Idiomaes-ES
Fecha caso2026-08-19
TernaR-HY · S-BOTTOM · H-BENTO · F-EDIT

[COMPROBADO] Nombre, terna, nivel, sector, ground y paleta salen de docs/00-day-brief.md.

2. Problema

Principal (hipótesis de diseño)

[HIPÓTESIS] La colorista de un salón de barrio no necesita otro Booksy: necesita escribir los gramos antes de que se le olvide el tazón. Hoy eso vive en WhatsApp + foto del tazón + una libreta que se moja junto al lavacabezas. La ficha se lee como cita, el guardado se lee como «ya está en el pelo» y el producto se lee como TPV. [SUPUESTO] Un salón de barrio tiene 1–2 coloristas y 40–80 fichas vivas; no opera un PMS de cadena ni una pasarela.

Secundarios

ProblemaQuién lo sufreEfecto
«WhatsApp + foto del tazón»COLORISTA (Nora)El matiz de hace 8 semanas se pierde; se improvisa el siguiente
Ficha se lee como citaCOLORISTA + DUEÑA (Inma)Esperan un slot Booksy que el producto no tiene
Guardado se lee como aplicadoCOLORISTA + clienta (fuera)«Ya me lo has puesto» cuando solo se apuntó la fórmula
Receta comercial se lee como fórmulaCOLORISTA6/7 + 20 vol no es «el bote de la caja»
La dueña pide caja y agenda el día 1InmaEl JWT se diluye; MATIZ se vuelve TPV + Booksy

Supuestos (no investigación primaria propia)

  • S1: Un salón de barrio tiene 1–2 coloristas y 40–80 fichas vivas; la demo tiene 1 COLORISTA + 5 clientas + 8 fórmulas. [SUPUESTO]
  • S2: La colorista escribe la fórmula en el móvil, junto al lavacabezas, con las manos aún húmedas: bottom tabs, targets grandes, sin wizard. [SUPUESTO]
  • S3: No hay reserva online ni pasarela. El «hoy» lo rellena Nora por la mañana (quién viene), no la clienta. [SUPUESTO]
  • S4: La dueña (Inma) no usa el producto el día 1; el JWT es solo COLORISTA. [SUPUESTO]

Hipótesis de producto

IDHipótesisSeñal de validación (futura)
H1Un editor de gramos + oxidante reduce «WhatsApp + foto del tazón»% de sillas de hoy con fórmula guardada el mismo día
H2Separar ficha de cita evita que MATIZ se lea como Booksy↓ «pensé que ya tenía hora» / cero CTA de agenda
H3JWT solo en colorista basta; la clienta no necesita cuenta0 cuentas de clienta; ficha completa sin login de clienta

No se afirman estadísticas de salones inventadas. Todo lo anterior es razonamiento de diseño etiquetado.

3. Propuesta de valor

ParaValor
COLORISTA (Nora Beltrán)Ve las 3 sillas de hoy; abre la ficha; escribe marca, tono, gramos, oxidante y tiempo; guarda sin fingir que el color ya está en el pelo.
DUEÑA (Inma Soto)El oficio queda escrito. No entra el día 1. No pide caja ni agenda aquí.
Clientas (Marisol, Lucía, Amina, Pilar, Diego)No usan la app. Su matiz vive en la ficha que Nora consulta.
Salón AmparoCuaderno mínimo: sillas + fichas + fórmulas, sin Booksy ni TPV.

No es MATIZ

ExcluidoPor qué
Reserva / slot / BooksyFicha ≠ cita (≠ F-BOOK)
TPV / cobro / ticketMATIZ no cobra; la caja es de Inma, fuera
Receta comercial / «el bote de la caja»Fórmula ≠ receta comercial
«Ya está aplicado» al guardarGuardado ≠ aplicado
Lead / cuenta de clienta / «pide cita online»S3 + H3: la clienta no entra
Semanario / lookbook de looksEso fue MARGEN (H-GALLERY / F-STORY)
Tabla de necesidades / compromiso de cajaEso fue CESTA (H-TABLE / F-LEAD)
Parte de obra / wizard de oficioEso fue CANTERA (S-BOTTOM / F-ONB); F-EDIT ≠ F-ONB
Wizard de criterios + comparador de pisosEso es ZAGUÁN (N−1: S-WIZ / H-WIZARD / F-CMP)
Feed de «colores nuevos»Terna H-BENTO, no H-FEED
Hero 2-col + 3 cards de «cómo coloreamos»Anti-patrón AP-12; contradice H-BENTO
Pasarela, suscripción, marketplace de tintesS3 / S4

4. Objetivos

Negocio / caso de estudio

  • Demostrar vertical slice L2 belleza oficio con Client + Formula + Sitting + JWT COLORISTA.
  • Portfolio coherente: Paper (12 UX + 12 UI) + docs + app runnable.
  • Terna R-HY · S-BOTTOM · H-BENTO · F-EDIT frente a ZAGUÁN (wizard/comparador), MARGEN (galería/masthead) y CESTA (tabla/rail).

Usuario

RolObjetivo medible en demo
COLORISTAAbrir Hoy, tocar silla de Marisol, escribir una fórmula y guardarla en < 90 s
DUEÑANo encontrar login propio; no encontrar caja ni agenda
ClientaNo existe superficie; no hay CTA «mi cita»

No objetivos v1 (explícitos)

  • Reserva, calendario, slot, confirmación de cita
  • Cuenta de clienta, recordatorios, WhatsApp outbound
  • TPV, ticket, comisión, pasarela
  • Multi-colorista, turnos, comisiones por silla
  • Recetario de marca (L’Oréal / Wella como catálogo comercial)
  • Foto del resultado como UGC de clienta
  • App nativa ni notificaciones push
  • Rol DUEÑA autenticado (S4)

5. Roles y permisos (resumen)

AcciónAnónimoCOLORISTADUEÑA / clienta
Ver loginNo hay cuenta
Ver Hoy / fichas / editorNo (401 / redirect)No
GET /me, /hoy, /clients401
POST fórmula401
PATCH fórmula401
Agendar sillaNo (no existe UI pública)Seed / fuera de app (S3)No
CobrarNoNoCaja física (fuera)
Login JWTEmite si valeNo hay fila

[DECISIÓN] Un solo rol autenticado: COLORISTA. La dueña no es un rol de sistema el día 1. Las clientas no son usuarias: son filas de clients.

6. Métricas (modelo, no instrumentadas en v1 salvo base)

TipoMétricaDefinición
North Star% de sillas de hoy con fórmula guardada el mismo díaOficio escrito, no pageviews
Activación colorista1.ª fórmula SAVEDH1
Integridad de expectativaReclamaciones «ya tenía cita» / «ya está aplicado» / «pásame el TPV»H2 + microcopy
Integridad de authGET /hoy y /clients sin token → 401Auth-first
SaludError rate API formulas / hoy4xx/5xx

7. Alcance funcional v1 (L2)

MóduloIncluido
AuthPOST /api/auth/login → JWT COLORISTA · GET /api/me
HoyGET /api/hoy · bento: 3 sillas + última fórmula + aviso oxidante
FichasGET /api/clients · lista con último matiz
FichaGET /api/clients/:slug · historial + CTA nueva fórmula
EditorPOST /api/clients/:slug/formulas · líneas + oxidante 10/20/30 + minutos + notas
FórmulaGET / PATCH /api/formulas/:id
Success/ok/:id · «guardado ≠ aplicado»
CuentaSalón, rol, cerrar sesión
Estados UIEmpty (0 sillas / 0 fichas), loading skeleton, error de red
MobileBottom tabs · editor a ~390px · targets ≥44px
Seed1 COLORISTA, 5 clientas, 8 fórmulas, 3 sittings de hoy
SaludGET /api/health

8. Criterios de aceptación de producto

  1. Sin token, / redirige a /login. No hay cara pública de lead.
  2. Una COLORISTA puede iniciar sesión y ver Hoy con 3 sillas reales (nombre, silla, último matiz) en el fold.
  3. Una COLORISTA puede abrir una ficha, escribir una fórmula (líneas + oxidante + minutos) y guardarla.
  4. La UI dice explícitamente que fórmula no es receta comercial, que ficha no es cita, que guardado no es aplicado, que MATIZ no agenda y que MATIZ no cobra.
  5. Sin token, GET /api/hoy, /api/clients, POST/PATCH de fórmulas responden 401.
  6. Un POST de fórmula crea status=SAVED. Guardar no marca la silla como teñida.
  7. /ok/:id muestra «Guardado no es aplicado».
  8. La home es el bento de Hoy: no hay hero 2-col + 3 cards como superficie principal.
  9. Fotos reales en las 5 fichas (assets del case).
  10. El editor muestra marca, tono, gramos por línea + chips 10 / 20 / 30 vol + minutos + notas.
  11. Bottom tabs Hoy · Fichas · Cuenta visibles en autenticado.
  12. Rutas solo las del doc 03. No existen /cita, /agenda, /cobrar, /tienda.

9. Stack y artefactos

CapaDetalle
FrontendAngular + Tailwind · puerto 4200
BackendNestJS · puerto 3019
DBNeon PostgreSQL · project mute-wave-74707210
AuthJWT (COLORISTA)
DiseñoPaper 01M0CDJZPXVWCVYZC5Z5V9ZRQE
Repo app/Users/cristian/orca/matiz-app/ · GitHub Criscode2022/matiz-app

[COMPROBADO] Puerto, Neon, Paper file ID, repo y path de app constan en el encargo.

10. Riesgos y mitigaciones

RiesgoImpactoMitigación v1
Expectativa de cita confirmadaReclamaciones «ya tenía hora»Copy «Ficha ≠ cita»; sin botón Agendar
Expectativa de color ya puesto«Ya me lo has aplicado»Copy «Guardado ≠ aplicado» en editor y success
Expectativa de receta de caja«Ponle el 6/7 de Wella»Copy «Fórmula ≠ receta comercial»
El producto «agenda»Expectativa F-BOOK«MATIZ no agenda. El WhatsApp de Inma agenda.»
El producto «cobra»Expectativa TPV«MATIZ no cobra. El ticket lo hace la caja.»
JWT de dueña el día 1Diluye S4Un rol COLORISTA; Inma no tiene fila
Home percibida como landing de salónPérdida de craft / H-BENTOHoy es bento; no hero 2-col
Confundir con ZAGUÁN (wizard inmobiliario)Expectativa de comparadorAuth-first; sillas, no fincas
Confundir con CANTERA (parte de obra)Expectativa de «enviar el día»F-EDIT, no F-ONB; fórmula, no parte
Leftover listings / compare / mesa en repoApp enseña otro productoContrato de esta suite gana; I01 de build
PII de clienta expuesta en públicoFicha en la calleAuth-first; GET clients 401 sin token
0 sillas hoyAbandonoEmpty «Hoy no hay sillas. El hoy lo rellena Nora por la mañana.»

11. Glosario

TérminoDefinición en MATIZ
FórmulaLíneas de color (marca, tono, gramos) + oxidante + minutos + notas; no es una receta comercial
FichaRegistro de clienta (clients); no es una cita
Silla / sittingAsiento de hoy (sittings); no es un slot Booksy
GuardarPersistir fórmula SAVED; no significa que el color esté en el pelo
AplicarGesto físico en el lavacabezas; fuera de la app en v1
Oxidante10 / 20 / 30 vol; no es «el bote de la caja»
Línea{ brand, tone, grams } dentro de lines JSON
MatizÚltimo tono memorable de la ficha (p. ej. «honey balayage»)
HoySuperficie H-BENTO: sillas + última fórmula + aviso oxidante
CuentaSalón Amparo, rol COLORISTA, salir
COLORISTAUsuario autenticado que opera el cuaderno
DUEÑAStakeholder; no es rol de sistema el día 1
Clienta / clienteFila de clients; no usa la app
CapillaSuperficie de ground #E6E4ED, no el fondo de card
LinoSuperficie de card #F7F5FA
PizarraTexto #2B2838
AmatistaAcento #6B4C9A
Bottom tabsShell S-BOTTOM: Hoy · Fichas · Cuenta

12. Decisiones de diseño (cierre de brief)

IDDecisiónAlternativa descartada
D1Home = Hoy bento (H-BENTO)Hero 2-col + 3 cards (AP-12) · galería (MARGEN) · wizard (ZAGUÁN) · tabla (CESTA)
D2Shell bottom tabs (S-BOTTOM)Masthead · rail · dock flotante · wizard chrome · sidebar CRM
D3Registro híbrido chapel (R-HY)Candlelit / spa ámbar (primer instinto) · rusted ZAGUÁN · gallery
D4Flujo F-EDIT (ficha → editor → guardar)F-CMP, F-STORY, F-LEAD, F-ONB, F-BOOK
D5Mood chapelCandlelit (vetado como primer instinto), rusted ZAGUÁN, moss CESTA
D6Un rol autenticado COLORISTAMulti-rol dueña / clienta / caja / admin
D7Estados de fórmula solo SAVEDAPPLIED / MIXED / BILLED (fingiría aplicación o cobro)
D8Editor de escrituraWizard de 4 pasos, recetario de marca, cola de sillas
D9Clienta sin cuentaLead, «mi cita», recordatorio push
D10PATCH /api/formulas/:idSubruta /apply como único contrato
D11Ground G-CHAPEL #E6E4EDG-CONCRETE, G-GALLERY, G-MOSS, G-CHROME, G-WATER
D12Success de fórmula en /ok/:idToast único sin pantalla
D13Oxidante = 10 | 20 | 30Campo libre de volúmenes o «el de la caja»
D14Sittings de hoy = seed de mañana, no UI de agendaCalendario de sillas (rompería H2)
D15Auth-first; cero cara públicaLanding de salón + «pide cita»

13. Relación con el día anterior

CaseTernaPor qué MATIZ no lo copia
ZAGUÁN (N−1)S-WIZ / H-WIZARD / F-CMPNo inmobiliaria, no wizard, no comparador, no mesa pública
MARGEN (N−2)S-TOP / H-GALLERY / F-STORYNo editorial, no lookbook, no masthead, no agenda de crónicas
CESTA (N−3)S-RAIL / H-TABLE / F-LEADNo ONG, no tabla de necesidades, no compromiso, no rail
CANTERA (N−8)S-BOTTOM / F-ONBS-BOTTOM se reutiliza (cooldown 5); F-EDIT ≠ F-ONB; belleza ≠ parte de obra

[COMPROBADO] El brief fija terna R-HY · S-BOTTOM · H-BENTO · F-EDIT. 0 códigos iguales a N−1 (ZAGUÁN). ≥2 distintos vs N−2 (MARGEN) y N−3 (CESTA). Ventana 4 días: R-IN + R-CL + R-CL + R-HY. Ground G-CHAPEL #E6E4ED, no G-CONCRETE.

02-ux-research-strategy.md

02 — Estrategia de investigación UX — MATIZ

Importante: este documento contiene artefactos de diseño y razonamiento secundario.
No hay entrevistas de campo primarias ni estadísticas inventadas presentadas como dato medido.
Etiquetas: [COMPROBADO] en producto, [SUPUESTO], [HIPÓTESIS], [DECISIÓN].

1. Objetivos de investigación (del caso)

ObjetivoMétodo en este casoSalida
Entender actores del salón de barrioModelado de stakeholders + personas§3–4
Definir job de «escribir los gramos» y de consultar la fichaJTBD + stories Must§5
Mapear fricción WhatsApp / tazón → editorJourney + service blueprint§6–7
Traducir a requisitos L2 PWA + editorMatriz hallazgo → requisito → feature§8

2. Fuentes y límites

Fuentes admisibles (secundarias / operativas)

  • Conocimiento general de oficio colorista en salón de barrio (fórmula en gramos, oxidante en volúmenes, tiempo de pose, ficha de clienta).
  • Analogía operativa con PWA auth-first + editor + JWT de la serie daily.
  • Restricciones ALS-2: no reserva, no TPV, no lookbook editorial, no tabla de necesidades, no parte de obra, no wizard inmobiliario.

Límites éticos de verdad

ProhibidoPermitido
«El 73% de coloristas de Usera fotografían el tazón» sin fuente«[SUPUESTO] la colorista escribe con las manos húmedas junto al lavacabezas»
Citas de entrevistas ficticias como campo realQuotes de persona etiquetadas como constructo de diseño
Ticket medio medido «42 € el balayage» como KPI de mercadoFórmulas de seed de producto, etiquetadas como demo
Horario oficial de un salón concreto en Calle Amparo 22Local de seed; Nora e Inma son personas de diseño
«Nora existe y nos dijo…»Nora Beltrán es persona de diseño

3. Stakeholders

StakeholderInfluenciaInterésNecesidad principal
COLORISTA (Nora)AltaMuy altaEscribir gramos y ver qué hizo hace 8 semanas
DUEÑA (Inma)Alta formal, baja en producto día 1AltaQue el oficio quede escrito; no operar el JWT
Clientas (Marisol, Lucía, Amina, Pilar)Baja formalAlta (en el pelo, no en la app)Que Nora acierte el matiz; no usar MATIZ
Cliente (Diego)Baja formalAltaIgual: el matiz vive en la ficha
Caja del salónMediaMediaFuera: MATIZ no cobra
Agenda / WhatsApp de InmaMediaAltaFuera: MATIZ no agenda
Booksy / TreatwellCompetencia / canal paraleloNo clonar la reserva; sí sustituir la foto del tazón
Marcas de color (Wella, L’Oréal, Igora)Contexto de oficioNo son un recetario comercial dentro de MATIZ

Mapa de poder (resumen)

  • Decisor de adopción: la colorista (quien escribe) y, en difusión, la dueña (quien decide si hay otra app).
  • Usuario frecuente de escritura: COLORISTA (Hoy + ficha + editor).
  • Usuario frecuente de lectura: COLORISTA (historial de la ficha).
  • Riesgo de rechazo: si Hoy parece una agenda; si Guardar parece Aplicar; si hay un CTA de cobro; si Inma exige login el día 1.

4. Personas

P1 — Nora Beltrán · COLORISTA

CampoDetalle
Edad / contexto34 años; colorista de Salón Amparo, Calle Amparo 22, Usera; 1–2 sillas a la vez
DigitalFluida en móvil; escribe junto al lavacabezas con las manos aún húmedas
GoalsVer las sillas de hoy, abrir la ficha, escribir marca / tono / gramos / oxidante / tiempo, guardar sin fingir que ya está en el pelo
PainsWhatsApp + foto del tazón; la libreta se moja; no recuerda el 7.1 de Diego; Booksy no le sirve para los gramos
Quote de diseño«Quiero los gramos antes de que se me olvide el tazón. No me des otra cita.»
Email demonora@matiz.salon / password123

Escenario: Login → Hoy → silla 1 Marisol → última fórmula honey balayage → Nueva fórmula → 9.3 + 8.3, 20 vol, 25 min → Guardar → «Guardado no es aplicado».

[COMPROBADO] Credencial demo sale del encargo. Nombre, edad y salón son constructo de diseño. [DECISIÓN]

P2 — Inma Soto · DUEÑA

CampoDetalle
Edad / contexto52 años; dueña de Salón Amparo; lleva la caja y el WhatsApp de citas
DigitalMedia; móvil para WhatsApp; desconfía de «otra app de reservas»
GoalsQue Nora no pierda el matiz; que el producto no le robe la caja ni la agenda
PainsCada herramienta nueva se convierte en Booksy + TPV; le piden un login el día 1
Quote de diseño«Que apunte el color. Las horas las llevo yo por WhatsApp. La caja es mía.»

Escenario: No se loguea. Pregunta a Nora «¿está apuntado lo de Marisol?». No encuentra /caja ni /agenda.

[DECISIÓN] Inma no es rol de sistema el día 1 (S4). No hay login DUEÑA en L2.

P3 — Marisol Vega · CLIENTA (no usa la app)

CampoDetalle
Edad / contexto47 años; honey balayage; silla 1 de hoy
DigitalNo entra en MATIZ; pide hora por WhatsApp a Inma
GoalsQue Nora recuerde el honey; que no le «inventen» un 6/7 de caja
Pains«El otro día me lo pusieron más oscuro»; no tiene por qué saber los gramos
Quote de diseño«Que se acuerde de lo de la miel. Yo no abro ninguna app.»

Escenario: Se sienta en la silla 1. Nora abre su ficha. Marisol no ve MATIZ.

[DECISIÓN] Marisol no es rol de sistema. Las cinco clientas son filas, no cuentas.

Clientas / cliente de seed (no personas primarias)

NombreEdadMatizSlugHoy
Marisol Vega47honey balayagemarisolSilla 1
Lucía Campos29blue-blackluciaNo
Amina El Idrissi38copperaminaSilla 2
Pilar Núñez61silverpilarSilla 3
Diego Rivas33ashy browndiegoNo

[COMPROBADO] Nombres, edades, matices y slugs salen del encargo.

Anti-personas

QuiénPor qué no es target v1
Community manager de cadena de salonesS1: 1–2 coloristas; no es PMS
Clienta que quiere «pedir cita online»S3 / H2: no hay reserva
Cajera que quiere TPV en el mismo sitioS4; MATIZ no cobra
Formadora de marca que quiere recetario oficialFórmula ≠ receta comercial
Jefe de producto de Booksy (40 estados de cita)S3: el hoy lo rellena Nora
Buscador de ZAGUÁN que pregunta «tres fincas»Otro job, otro día

5. JTBD y user stories

Job principal (COLORISTA)

Cuando tengo el tazón delante y las manos húmedas,
quiero escribir los gramos, el oxidante y el tiempo,
para no depender de una foto de WhatsApp ni de la memoria de hace 8 semanas.

Job secundario (COLORISTA, lectura)

Cuando se sienta Marisol,
quiero ver qué le hice hace 8 semanas,
para no inventar el honey ni copiar el bote de la caja.

Job de la DUEÑA (fuera de app)

Cuando el salón abre,
quiero que Nora apunte el color y yo siga con el WhatsApp y la caja,
para no meter otra reserva ni otro TPV.

Jobs secundarios

JobRol
Distinguir guardado de aplicadoCOLORISTA
Ver las 3 sillas de hoy de un vistazoCOLORISTA
Avisar si el último oxidante era 30 volCOLORISTA
No enseñar la ficha a la calleCOLORISTA / Inma
Entender que MATIZ no cobra ni agendaInma
No crear cuentaClienta

Stories Must (v1)

IDStoryAC
US1Como colorista, quiero entrar con email/passwordJWT + redirect /
US2Como colorista, quiero ver las sillas de hoyGET /api/hoy · 3 sillas en el fold
US3Como colorista, quiero ver la última fórmula y un aviso de oxidanteTile + aviso si 30 vol
US4Como colorista, quiero listar fichas con último matizGET /api/clients
US5Como colorista, quiero abrir una ficha y su historialGET /api/clients/:slug · sin CTA de cita
US6Como colorista, quiero escribir una fórmulaEditor: líneas + oxidante + minutos + notas
US7Como colorista, quiero guardar sin aplicarPOST → SAVED · success «guardado ≠ aplicado»
US8Como colorista, quiero corregir una fórmulaPATCH /api/formulas/:id
US9Como colorista, quiero ver mi cuenta y salir/cuenta · salón, rol, logout
US10Como cualquiera sin token, no quiero ver fichas401 / redirect login
US11Como colorista, quiero empty / error / loadingUI-08, UI-09, UI-10
US12Como colorista junto al lavacabezas, quiero targets ≥44pxBottom tabs + editor 390

MoSCoW (v1 L2)

PrioridadÍtems
MustLogin, Hoy bento, fichas, ficha, editor móvil, success, empty/error/loading, cuenta
ShouldEditor desktop 1440; aviso oxidante 30 vol; badge SAVED; pie «MATIZ no agenda / no cobra»
CouldPATCH notas tras guardar; recuento de fórmulas en ficha; skeleton bento
Won’tAgenda, TPV, cuenta clienta, recetario de marca, rol DUEÑA, aplicar

6. Journey (COLORISTA · día de Marisol)

FaseActorAcciónTouchpointEmoción [HIPÓTESIS]
1 EntrarNoraLogin JWT; aterriza en Hoy/login/Oficio, «esto no es Booksy»
2 Mirar el díaNoraVe silla 1 Marisol, silla 2 Amina, silla 3 Pilar/ H-BENTOControl (3 sillas, no 12 slots)
3 RecordarNoraAbre ficha de Marisol; lee honey de junio/fichas/marisolAlivio («esto le hice»)
4 EscribirNoraNueva fórmula: líneas + 20 vol + 25 min/fichas/marisol/formulaConcentración (manos húmedas)
5 Guardar ≠ aplicarNoraGuarda; lee el disclaimer/ok/:idControl (H1 + microcopy)
6 No cobrarNoraNo hay TPV; Inma cobra en cajaFueraOficio, no euforia de ticket
7 ComprobarNoraVuelve a la ficha: la nueva está SAVED/fichas/marisolConfianza

Momentos de verdad

  1. 3 sillas en el primer viewport — si hay que «descubrir la marca», se rompe H-BENTO / AP-21. [HIPÓTESIS]
  2. Editor sin wizard — si aparecen 4 pasos de «elige el look», F-EDIT falla.
  3. Success dice que guardado no es aplicado — evita la mentira del color puesto.
  4. Cero CTA de cita o cobro — si aparece «Agendar» o «Cobrar», H2 / microcopy fallan.
  5. Tabs abajo, no arriba — S2: manos húmedas, pulgar. [HIPÓTESIS]

7. Service blueprint (resumen)

CapaElementos
Frontstage COLORISTALogin, Hoy bento, fichas, ficha, editor, success, cuenta
Frontstage clientaNo existe. La clienta se sienta; no abre MATIZ
BackstageWhatsApp de Inma (hora), caja física (ticket), tazón y oxidante reales, lavacabezas
SistemasNest API :3019, Neon mute-wave-74707210, JWT, Angular PWA
SoportesSeed 1 COLORISTA + 5 fichas + 8 fórmulas + 3 sittings + fotos, Paper, docs
Fallos0 sillas; 401 sin token; 404 slug; red caída → UI error; validación de gramos

Fallos de servicio y respuesta de diseño

FalloEvidencia de UIRecuperación
0 sillas hoyUI-08 Empty Hoy«Hoy no hay sillas. El hoy lo rellena Nora por la mañana.»
0 fichasUI-08 Empty Fichas«Aún no hay fichas.» (seed no debería; útil si se vacía)
API caídaUI-09 ErrorReintentar
Latencia HoyLoading (doc 16)Skeleton bento (3 tiles + 1 fórmula)
Form inválidoInline fieldNo navegar a /ok
Slug inexistente404«Esa ficha no está.» + volver a Fichas
Sin token en /Redirect/login
Oxidante 30 vol en última de hoyAvisoTile amatista: «Revisa el oxidante: 30 vol no es 20.»

8. Matriz hallazgo → requisito → feature

HallazgoTipoRequisitoFeature v1
Escribe con manos húmedas[SUPUESTO] S2Bottom tabs + targets ≥44S-BOTTOM · UI-05
WhatsApp + foto del tazón[HIPÓTESIS] H1Editor de gramos + oxidanteF-EDIT · POST formulas
Ficha se lee como cita[HIPÓTESIS] H2Cero CTA de agendaUI-04 solo historial
Guardado se lee como aplicado[DECISIÓN]Copy irrenunciableUI-07 + editor
Receta de caja ≠ fórmula[DECISIÓN]Copy irrenunciableEditor + ficha
Dueña no entra día 1[SUPUESTO] S4Un rol COLORISTALogin + guards
Clienta no necesita cuenta[HIPÓTESIS] H3Auth-first, sin signup públicoJWT only
Hoy lo rellena Nora[SUPUESTO] S3Sittings seed, no calendarioGET /hoy
L2 compacto, no PMS[DECISIÓN]5 fichas + 8 fórmulas + 3 sillasSin rol DUEÑA, sin TPV

9. Preguntas abiertas (no bloquean v1)

IDPreguntaCómo se resolvería después
Q1¿APPLIED llega en L+1 o nunca?[DECISIÓN v1] solo SAVED. Aplicar es gesto físico. Could: toggle «puesta» sin fingir el resultado
Q2¿Quién escribe las sillas de mañana?[DECISIÓN v1] seed / backstage. UI de agenda = L3 y rompe H2
Q3¿Inma tendrá JWT en L+1?S4 lo deja fuera. L+1 Could: rol DUEÑA de solo lectura
Q4¿Las marcas del seed son oficiales?No. Catálogo demo. [SUPUESTO de catálogo]
Q5¿El Salón Amparo de Calle Amparo 22 existe?No. Atrezzo de persona. No se afirma como negocio existente
Q6¿Las fotos de clientas son personas reales identificables?Assets de case; no se presentan como clientela real. [SUPUESTO de catálogo]

10. Plan de research futuro (si hubiera campo real)

MétodoMuestra orientativaPregunta
Test de usabilidad editor5–6 coloristas 25–45¿Guardan una fórmula en < 90 s con el pulgar?
Shadowing junto al lavacabezas1 colorista de barrio¿El editor sustituye la foto del tazón?
Revisión de expectativa de citaClics en ficha vs «¿a qué hora?»Validar H2
Editor mobile 390 con manos ocupadas5 sesiones¿Los targets de 44px bastan con el pulgar húmedo?

Estos métodos no se han ejecutado. No se reportan hallazgos como si lo hubieran sido.

11. Síntesis

MATIZ se diseña como cuaderno de oficio que escribe y guarda, no como Booksy, no como TPV y no como recetario de marca.
La investigación del caso es constructiva y etiquetada: personas, JTBD y journey son artefactos de diseño; las métricas de validación quedan para uso real futuro.

03-information-architecture.md

03 — Arquitectura de información — MATIZ

1. Principios de IA

PrincipioAplicación
Auth firstSin token no hay Hoy. No hay landing de marca por delante.
Hoy → ficha → editor → okProfundidad 2 desde la silla hasta la fórmula; no hay modal de cita
Solo COLORISTATodas las superficies autenticadas salvo login
Lenguaje de dominioFórmula, ficha, silla, matiz, oxidante — no «cita», «SKU», «lead», «listing»
Ficha no es citaLa ficha no tiene slot, hora ni botón Agendar
Guardar se miraSuccess no tiene «Aplicar» ni «Cobrar» (≠ F-BOOK / ≠ TPV)
Bottom tabs siempre a manoS-BOTTOM: Hoy · Fichas · Cuenta; no masthead, no rail, no wizard chrome

2. Sitemap

/login                                 Login JWT COLORISTA                         público
/                                      Hoy bento (auth)                            H-BENTO
/fichas                                Lista de clientas (auth)
/fichas/:slug                          Ficha + historial (auth)
/fichas/:slug/formula                  Editor de fórmula (auth)                    F-EDIT
/ok/:id                                Success post-guardado (auth)
/cuenta                                Salón, rol, salir (auth)
/**                                    → redirect /  (si hay token) o /login

Árbol por audiencia

AudienciaNodos relevantes
COLORISTALogin → Hoy → Ficha → Editor → Ok · Fichas · Cuenta
DUEÑA / clientaNinguno. No hay cara pública

[COMPROBADO] Siete rutas del encargo L2. No se inventan /cita, /agenda, /cobrar, /tienda, /aplicar.

3. Navegación

Bottom tabs (S-BOTTOM)

SlotDestinoVisibilidadNotas
Hoy/AuthAmatista cuando activo
Fichas/fichasAuthLista; no es agenda
Cuenta/cuentaAuthSalón + salir

[DECISIÓN] El bottom tab es la única navegación primaria autenticada. Encaja L2 PWA. No hay masthead (MARGEN), no rail (CESTA), no chrome de pasos (ZAGUÁN).

Desktop ≥960px: tabs pueden subir a una barra inferior fija de 64px o a un rail corto inferior centrado; no se convierten en top-nav de marketing.
Mobile ~390px: tabs 56–64px + safe-area; labels Hoy / Fichas / Cuenta siempre visibles. [DECISIÓN] En 390 no se introduce hamburger.

El editor y el success conservan los tabs (la colorista no se pierde). Un «atrás» contextual vive en el header de la pantalla, no sustituye al tab.

Header contextual (no es nav primaria)

SuperficieIzquierdaCentroDerecha
HoyWordmark MATIZ
FichasWordmark
Ficha← FichasNombreNueva fórmula
Editor← Ficha«Nueva fórmula»
Success
CuentaWordmark
LoginWordmark

[DECISIÓN] El wordmark en autenticado lleva a / (Hoy), no reinicia un wizard (no hay wizard).

Login (único público)

ElementoDestinoNotas
Email / password / EntrarPOST login → /JWT
Errorno navegaInline
Pieno «crear cuenta»No hay signup

[DECISIÓN] No hay /registro. No hay «Soy clienta». No hay «Pedir cita».

4. Inventario de contenido

PantallaContenidos
Login /loginWordmark, eslogan, email, password, submit, error
Hoy /Chrome S-BOTTOM, 3 tiles de silla (nombre, nº, último matiz), tile última fórmula, aviso oxidante, empty, loading, error
Fichas /fichasLista: foto, nombre, último matiz, fecha de última fórmula, empty, error
Ficha /fichas/:slugFoto, nombre, edad/matiz, historial de fórmulas, CTA Nueva fórmula, disclaimer «ficha ≠ cita»
Editor /fichas/:slug/formulaNombre de ficha, líneas (marca, tono, gramos, quitar/añadir), oxidante 10/20/30, minutos, notas, Guardar, disclaimer «fórmula ≠ receta» + «guardado ≠ aplicado»
Success /ok/:idMensaje, id/slug, «guardado ≠ aplicado», CTA ver ficha / Hoy
Cuenta /cuentaSalón Amparo, Calle Amparo 22, rol COLORISTA, nombre Nora, Salir

5. Taxonomía

Role

Status APILabel UISemántica
COLORISTAColoristaÚnico rol autenticado v1

[DECISIÓN] No hay DUENA / CAJA / CLIENTA.

FormulaStatus

Status APILabel UISemántica
SAVEDGuardadaPersistida; no implica aplicada

Orden de historial: createdAt descendente (lo último escrito primero).
[DECISIÓN] No hay APPLIED / MIXED / BILLED: S4 + microcopy.

Oxidant

APILabel UI
1010 vol
2020 vol
3030 vol

Clients (líneas seed)

slugNombreEdadMatizHoy
marisolMarisol Vega47honey balayageSilla 1
luciaLucía Campos29blue-black
aminaAmina El Idrissi38copperSilla 2
pilarPilar Núñez61silverSilla 3
diegoDiego Rivas33ashy brown

[COMPROBADO] Cinco fichas con slugs del encargo. [DECISIÓN]

Sittings (hoy)

chairClient slugSemántica
1marisolAsiento, no slot
2aminaAsiento, no slot
3pilarAsiento, no slot

[COMPROBADO] Encargo: silla 1 Marisol, 2 Amina, 3 Pilar.

6. Modelo mental vs UI

Modelo mentalRepresentación
«Quién se sienta hoy»Hoy, 3 tiles de silla
«Qué le hice a Marisol»Ficha /fichas/marisol + historial
«Anoto el tazón»Editor de líneas + oxidante + minutos
«¿Ya está en el pelo?»No. Copy: guardado ≠ aplicado
«¿A qué hora viene?»No hay UI. Copy: ficha ≠ cita. MATIZ no agenda
«¿Cobro desde aquí?»No hay UI. MATIZ no cobra
«¿Es el 6/7 de la caja?»No. Copy: fórmula ≠ receta comercial
«Salgo del cuaderno»Cuenta → Salir

7. Query string y estado

RutaQueryUso
/login?next=/fichas/marisol (o /, /fichas, /cuenta, /ok/:id, /fichas/:slug/formula)Redirect post-login sanitizado
RestoSin ?q= de búsqueda Must

[DECISIÓN] Sin búsqueda facetada en v1. 5 fichas se recorren a ojo. L+1: filtro por matiz.

El editor no persiste borrador en sessionStorage como Must. Si se sale sin guardar, se pierde. Could L+1: draft local.

8. Rutas API alineadas a IA

UIAPI
LoginPOST /api/auth/login
Cuenta / headerGET /api/me JWT
HoyGET /api/hoy JWT
Lista fichasGET /api/clients JWT
FichaGET /api/clients/:slug JWT
Guardar fórmulaPOST /api/clients/:slug/formulas JWT
Leer / editar fórmulaGET / PATCH /api/formulas/:id JWT
SaludGET /api/health

[COMPROBADO] Contrato del encargo. No se documentan citas, cobros ni POST /api/appointments como Must.

9. Decisiones de IA descartadas

IdeaPor qué no en L2 v1
/explorar looks + grid de colorRompe H-BENTO / F-EDIT; eso es lookbook
Área «mis citas» de clientaCuenta de facto; contradice H3
/sillas calendario semanalRompe H2 / ficha ≠ cita
Search como homeH-SEARCH de otros días
Feed de «fórmulas nuevas»H-FEED de RONDA
Tabla de clientas como homeH-TABLE de CESTA
Lookbook editorialH-GALLERY de MARGEN
/cita/:slugRompe H2 / microcopy Ficha ≠ cita
Wizard de 4 pasos de fórmulaF-ONB de CANTERA; el editor es 1 pantalla
Wizard de criterios (zona / tope)ZAGUÁN; otro oficio

04-user-flows.md

04 — Flujos de usuario — MATIZ

Convenciones

  • Actor: Guest (anónimo) | COLORISTA
  • Éxito: resultado observable
  • Errores: UI + código HTTP cuando aplica
  • Flujo canónico: F-EDIT (ficha → editor → POST fórmula → ok)
  • Flujo de escritorio: F-DESK (login → Hoy)
  • Dueña / clienta: no tienen flujo de sistema

F0 — Entrada anónima (Guest)

cualquier ruta auth  →  sin token  →  /login?next=<path sanitizado>
/login               →  si hay token válido  →  /  (o next)
PasoAcciónSistema
1Aterriza sin JWTGuard Angular
2Ve solo el loginUI-01
3No encuentra «pedir cita» ni «crear cuenta»IA

Éxito: cero cara pública.
AC: no hay landing, hero ni lead. Auth-first.


F1 — Login JWT (COLORISTA)

/login → POST /api/auth/login { email, password }
      → 200 { accessToken, user } → localStorage → /  (o ?next sanitizado)
      → 401 → mensaje error en form
CampoValidación clienteAPI
emailrequired, email@IsEmail()
passwordrequired, min 6@MinLength(6)

Éxito: token guardado; user role=COLORISTA, nombre Nora Beltrán.
Credencial demo: nora@matiz.salon / password123.

CasoComportamiento
Credenciales inválidas401 + mensaje
Red caídaError de red en UI
Token caducado en Hoy401 en GET → re-login
next externo / //Ignorado; va a /

[COMPROBADO] Credencial del encargo.


F2 — Hoy bento (COLORISTA)

/ (token)
  → GET /api/hoy
  → { sittings[3], lastFormula, oxidantWarning }
  → 3 tiles + última fórmula + aviso  |  empty  |  error  |  skeleton
  → tap silla → /fichas/:slug
  → tap última fórmula → /fichas/:slug  (de esa fórmula)
PasoAcciónSistema
1Lee las 3 sillas (nombre, nº, último matiz)sittings
2Lee la última fórmula guardadalastFormula
3Lee el aviso si alguna silla de hoy trae 30 voloxidantWarning
4Abre fichaRouter

Éxito: 3 sillas reales en el fold, o empty accionable.
AC tiles: cada una muestra nombre, silla, último matiz. Cero botones Agendar / Cobrar.

Guión Nora (demo): silla 1 Marisol (honey balayage) · silla 2 Amina (copper) · silla 3 Pilar (silver). Última fórmula: Diego, ashy brown, 2026-08-01. Aviso: Amina trae 30 vol. [SUPUESTO de catálogo] alineado al seed de doc 05.

Errores / estados

CasoComportamiento
0 sittingsUI-08: «Hoy no hay sillas. El hoy lo rellena Nora por la mañana.»
Red / 5xxUI-09 + Reintentar (repite GET)
Primera cargaSkeleton bento (doc 16)
Sin tokenRedirect /login
401 mid-sessionRedirect /login

[DECISIÓN] GET /api/hoy es el Must de producto. No hay POST de silla en v1 (S3).


F3 — Lista de fichas (COLORISTA)

/fichas → GET /api/clients
        → filas: foto, nombre, último matiz, fecha
        → tap fila → /fichas/:slug
CasoComportamiento
Lista con items5 filas seed
Lista vacíaEmpty: «Aún no hay fichas.»
Fallo red / 401Error + reintentar / re-login

Orden: updatedAt de última fórmula desc; quien no tiene fórmula, al final. [DECISIÓN]

Seed: marisol, lucia, amina, pilar, diego.


F4 — Ficha de clienta (COLORISTA)

/fichas/:slug → GET /api/clients/:slug
              → foto, nombre, matiz, historial de fórmulas
              → “La ficha no es una cita. MATIZ no agenda.”
              → CTA Nueva fórmula → /fichas/:slug/formula
              → tap fórmula → /fichas/:slug/formula?id=  (Could) o solo lectura en la misma ficha
              → 404 slug inexistente → mensaje + volver a /fichas
PasoAcciónSistema
1Lee nombre y matizClient
2Recorre historial (fecha, oxidante, líneas resumidas)formulas[]
3Nueva fórmulaRouter
4Lee disclaimerUI

Éxito: ficha leíble; disclaimer visible; no hay hora, slot ni cobro.
Error: 404 si slug inexistente.

AC ficha

#Criterio
1Foto real del cover (assets/clients/:slug.jpg)
2Nombre en Libre Baskerville; matiz en Nunito Sans peltre
3Historial visible; si 0 fórmulas, empty honesto + CTA
4Disclaimer «ficha ≠ cita» visible sin scroll en desktop
5Sin CTA de cita, cobro o «te llamamos»

[DECISIÓN] v1 no tiene /fichas/:slug/formula/:id de detalle. El historial se lee en la ficha; el editor crea nueva. PATCH vive si se reabre Could con ?id=; Must = POST nueva.


F5 — Editor de fórmula (COLORISTA) · F-EDIT

/fichas/:slug/formula
  → si slug 404 → volver a /fichas
  → líneas [ { brand, tone, grams } ]  (mín. 1)
  → oxidante 10 | 20 | 30
  → minutes
  → notes
  → disclaimer “Esto es una fórmula, no la receta de la caja.”
  → disclaimer “Guardar no es aplicar.”
  → POST /api/clients/:slug/formulas
  → 201 Formula { id, status: SAVED, ... }
  → /ok/:id
CampoValidación cliente (mín.)API
lines≥1; cada una brand min 2, tone min 1, grams > 0@ArrayMinSize(1)
oxidantrequired, enum 10/20/3010 | 20 | 30
minutesrequired, 1–90@Min(1) @Max(90)
notesopcional, max 500@MaxLength(500)
statusno en POST; siempre SAVEDservidor fuerza SAVED

Éxito: registro status=SAVED + pantalla success.
No hay «Aplicar» ni «Cobrar» en v1.

GestosResultado
Añadir líneaPush { brand:'', tone:'', grams: null }
Quitar líneaSplice; no deja 0 líneas (bloquea)
Chip oxidanteRadio 10 / 20 / 30
GuardarPOST; pending en botón
Atrás/fichas/:slug; descarta si no hay confirm Must

Errores

CasoComportamiento
Validación DTO400 + mensaje de campo
Red caídaError de red en form; no navegar a /ok/…
Doble submitBotón disabled mientras pending
lines vacías / grams ≤ 0Inline; no POST
Sin token401
Slug inexistente404

[DECISIÓN] Crear siempre como SAVED. No existe transición a APPLIED.

[COMPROBADO] Entidad Formula según encargo (lines JSON, oxidant 10|20|30, minutes, notes, status SAVED).

Variante desktop (UI-06)

Misma ruta /fichas/:slug/formula a 1440. El editor ocupa una columna ~720 + staff de ficha a la derecha (nombre, último matiz, disclaimer). No es otra app. [DECISIÓN]


F6 — Success de guardado (COLORISTA)

/ok/:id → GET /api/formulas/:id
        → copy de acuse + clientName / id
        → “Guardado no es aplicado. El color aún no está en el pelo.”
        → CTA ver ficha / volver a Hoy
PasoAcciónSistema
1Lee acuseUI
2Vuelve a la ficha y comprueba el historial/fichas/:slug
3Sigue con la siguiente silla/

AC: el disclaimer es visible sin scroll en desktop.
Si se entra a /ok/algo-que-no-existe: mensaje genérico + volver a Hoy; no se inventa un registro. [DECISIÓN]


F7 — Editar fórmula (COLORISTA) · PATCH

Desde ficha (Could: «corregir»)
  → GET /api/formulas/:id
  → mismos campos que F5
  → PATCH /api/formulas/:id { lines?, oxidant?, minutes?, notes? }
  → 200 Formula
  → /ok/:id  o vuelta a la ficha

[DECISIÓN] PATCH no acepta { status: "APPLIED" }. Status permanece SAVED. Corregir gramos no es aplicar.

Must de producto = POST nueva. PATCH es Should (el contrato API existe).


F8 — Cuenta y logout (COLORISTA)

/cuenta → GET /api/me
        → Salón Amparo · Calle Amparo 22, Usera · COLORISTA · Nora Beltrán
        → “MATIZ no agenda. MATIZ no cobra.”
        → Salir → limpia localStorage → /login
CasoComportamiento
200Nombre, email, rol, salón
401Re-login
SalirBorra matiz_token + matiz_user

F9 — Health (ops)

GET /api/health → 200 { ok: true }  (o { status: 'ok' })

Sin auth. No es una pantalla. Smoke de arranque.


Matriz de errores global

CódigoCuándoUI
400DTO inválidoMensaje campo / genérico
401Sin/mal tokenRe-login
404slug o id no existeMensaje + volver a Fichas / Hoy
5xx / networkAPI caídaError + retry

Flujos fuera de alcance v1

  • Registro / recuperación de password de COLORISTA
  • Cuenta de clienta / «mi cita» / recordatorios
  • Reserva o confirmación de hora
  • Cobro / ticket / TPV
  • Alta de ficha nueva (seed fijo; Could L+1)
  • Alta de silla de mañana (agenda)
  • Aplicar / marcar puesta
  • Notificación push «Marisol en 10 min»
  • Multi-colorista / turnos

05-data-model.md

05 — Modelo de datos — MATIZ

1. Visión general

Dominio L2 de oficio colorista + cuaderno autenticado:

EntidadTabla SQLPropósito
UserusersIdentidad de oficio (rol COLORISTA)
SessionsessionsSesión JWT / token persistido (Should)
ClientclientsFicha de clienta / cliente
FormulaformulasFórmula de color (líneas + oxidante + tiempo)
SittingsittingsSilla de un día (asiento, no cita)

Base: PostgreSQL (Neon project mute-wave-74707210) · IDs: cuid() o texto estable (usr_nora, cli_marisol).
[DECISIÓN] ensureSchema o Prisma son válidos si el contrato de tablas se cumple; el brief de stack no exige Prisma.

2. Enums (aplicación; persistidos como TEXT + check)

Role

ValorDescripción
COLORISTAOperadora del cuaderno; único rol autenticado v1

FormulaStatus

ValorDescripción
SAVEDPersistida; no implica aplicada

Oxidant

10 | 20 | 30

3. Diagrama ER (texto)

users
  id, email, password_hash, name, role(COLORISTA)
  salon_name, salon_address
  created_at, updated_at
  1 ──< sessions
  1 ──< formulas          -- author
  1 ──< sittings          -- who wrote today's chairs (Could)

sessions
  id, user_id → users
  token_hash, expires_at
  created_at

clients
  id, slug (unique)
  name, age, shade          -- shade = último matiz memorable
  cover                     -- filename
  created_at, updated_at
  1 ──< formulas
  1 ──< sittings

formulas
  id
  client_id → clients
  author_id → users
  lines (JSONB)             -- [{ brand, tone, grams }]
  oxidant (10|20|30)
  minutes
  notes
  status (SAVED)
  created_at, updated_at

sittings
  id
  client_id → clients
  chair (1|2|3)
  day (DATE)                -- Europe/Madrid
  created_at

v1: una sola COLORISTA opera sobre el conjunto global.
No hay FK de clienta-usuaria: la clienta no se loguea.

4. Tablas

users

CampoTipoConstraints
idTEXTPK
emailTEXTUNIQUE NOT NULL
password_hashTEXTNOT NULL (bcrypt)
nameTEXTNOT NULL
roleTEXTNOT NULL DEFAULT 'COLORISTA'
salon_nameTEXTNOT NULL DEFAULT 'Salón Amparo'
salon_addressTEXTNOT NULL DEFAULT 'Calle Amparo 22, Usera, Madrid'
created_atTIMESTAMPTZDEFAULT now()
updated_atTIMESTAMPTZDEFAULT now()

sessions

CampoTipoConstraints
idTEXTPK
user_idTEXTNOT NULL FK → users(id) ON DELETE CASCADE
token_hashTEXTNOT NULL
expires_atTIMESTAMPTZNOT NULL
created_atTIMESTAMPTZDEFAULT now()

[DECISIÓN] El JWT viaja en Authorization: Bearer. sessions permite invalidar (logout) sin esperar expiración. Si el slice usa JWT stateless, documentar el hueco; D-P1-05 se cumple igual con guard.

clients

CampoTipoConstraints
idTEXTPK
slugTEXTUNIQUE NOT NULL, kebab / un solo token (marisol)
nameTEXTNOT NULL
ageINTEGERNOT NULL CHECK > 0
shadeTEXTNOT NULL · matiz memorable (honey balayage)
coverTEXTfilename (marisol.jpg)
created_at / updated_atTIMESTAMPTZ

formulas

CampoTipoConstraints
idTEXTPK
client_idTEXTNOT NULL FK → clients(id) ON DELETE CASCADE
author_idTEXTNOT NULL FK → users(id)
linesJSONBNOT NULL · array { brand: string, tone: string, grams: number } ≥ 1
oxidantINTEGERNOT NULL CHECK IN (10, 20, 30)
minutesINTEGERNOT NULL CHECK BETWEEN 1 AND 90
notesTEXTNULL
statusTEXTNOT NULL DEFAULT 'SAVED'
created_at / updated_atTIMESTAMPTZ

Validación de lines: cada ítem tiene brand (min 2), tone (min 1), grams (> 0, número). [DECISIÓN] JSONB, no tabla formula_lines en v1.

sittings

CampoTipoConstraints
idTEXTPK
client_idTEXTNOT NULL FK → clients(id)
chairINTEGERNOT NULL CHECK IN (1, 2, 3)
dayDATENOT NULL · día civil Europe/Madrid
created_atTIMESTAMPTZ

[DECISIÓN] Unique (day, chair) y unique (day, client_id): una silla, una persona; una persona, una silla ese día.

5. Reglas de integridad y negocio

ReglaImplementación
Lectura de Hoy / fichas / fórmulasJWT COLORISTA → 401 sin token
Escritura de fórmulasJWT COLORISTA → 401 sin token
Login / healthPúblicos
Slug únicoseed fijo; alta de clienta no es Must
Status default fórmulaSAVED al crear
PasswordNunca en claro; solo password_hash
Orden clientsúltima fórmula created_at desc
Orden historialcreated_at desc
Orden sittingschair asc
Guardar Formulano marca sitting como teñida; no cobra
Público nunca lista clientsno hay GET público de fichas
Oxidantesolo 10 / 20 / 30
Aviso oxidante en Hoytrue si alguna silla de day=hoy tiene última fórmula con oxidant=30

6. Contratos API (resumen)

JSON camelCase hacia el web. [DECISIÓN]

POST /api/auth/login

Body: { email, password }
Response 200: { accessToken, user: { id, email, name, role, salonName, salonAddress } }
401 credenciales inválidas. Response sin hash.

GET /api/me (JWT)

Response 200: { id, email, name, role, salonName, salonAddress }
Sin token → 401.

GET /api/hoy (JWT)

Response 200

{
  "day": "2026-08-19",
  "sittings": [
    {
      "id": "sit_1",
      "chair": 1,
      "client": {
        "id": "cli_marisol",
        "slug": "marisol",
        "name": "Marisol Vega",
        "age": 47,
        "shade": "honey balayage",
        "cover": "marisol.jpg"
      },
      "lastFormula": {
        "id": "frm_marisol_2",
        "oxidant": 20,
        "minutes": 25,
        "status": "SAVED",
        "createdAt": "2026-06-24T10:00:00.000Z",
        "lines": [
          { "brand": "L'Oréal Dia Light", "tone": "9.3", "grams": 20 },
          { "brand": "L'Oréal Dia Light", "tone": "8.3", "grams": 20 }
        ]
      }
    }
  ],
  "lastFormula": {
    "id": "frm_diego_1",
    "clientSlug": "diego",
    "clientName": "Diego Rivas",
    "oxidant": 20,
    "minutes": 30,
    "status": "SAVED",
    "createdAt": "2026-08-01T11:00:00.000Z",
    "lines": [
      { "brand": "L'Oréal Majirel", "tone": "6.1", "grams": 20 },
      { "brand": "L'Oréal Majirel", "tone": "7.1", "grams": 20 }
    ]
  },
  "oxidantWarning": {
    "active": true,
    "chair": 2,
    "clientSlug": "amina",
    "oxidant": 30,
    "message": "Revisa el oxidante: Amina trae 30 vol. 30 no es 20."
  }
}

Si 0 sittings: { day, sittings: [], lastFormula, oxidantWarning: { active: false } } + empty UI-08.
lastFormula puede ser null si no hay ninguna.
Sin token → 401.

GET /api/clients (JWT)

Response 200: ClientCard[]

[
  {
    "id": "cli_marisol",
    "slug": "marisol",
    "name": "Marisol Vega",
    "age": 47,
    "shade": "honey balayage",
    "cover": "marisol.jpg",
    "lastFormulaAt": "2026-06-24T10:00:00.000Z",
    "lastOxidant": 20
  }
]

GET /api/clients/:slug (JWT)

Client + formulas[] (historial completo, createdAt desc) o 404.

{
  "id": "cli_marisol",
  "slug": "marisol",
  "name": "Marisol Vega",
  "age": 47,
  "shade": "honey balayage",
  "cover": "marisol.jpg",
  "formulas": [
    {
      "id": "frm_marisol_2",
      "lines": [
        { "brand": "L'Oréal Dia Light", "tone": "9.3", "grams": 20 },
        { "brand": "L'Oréal Dia Light", "tone": "8.3", "grams": 20 }
      ],
      "oxidant": 20,
      "minutes": 25,
      "notes": "Balayage miel. No bajar del 8.",
      "status": "SAVED",
      "createdAt": "2026-06-24T10:00:00.000Z"
    }
  ]
}

POST /api/clients/:slug/formulas (JWT)

Body

CampoTipoReq
lines{ brand, tone, grams }[]sí · ≥1
oxidant10 | 20 | 30
minutesnumber 1–90
notesstringno

Response 201: Formula + id + status: "SAVED" + clientSlug.
Slug inexistente → 404. Sin token → 401. DTO inválido → 400.

GET /api/formulas/:id (JWT)

Formula completa o 404.

PATCH /api/formulas/:id (JWT)

Body: { lines?, oxidant?, minutes?, notes? }
No acepta status.
Response 200: Formula actualizada. Sin token → 401.

[COMPROBADO] PATCH sobre :id. [DECISIÓN] sin transición de status.

GET /api/health

Response 200: { ok: true }

7. Seed de referencia (2026-08-19)

COLORISTA

NombreEmailPasswordSalón
Nora Beltránnora@matiz.salonpassword123Salón Amparo · Calle Amparo 22, Usera, Madrid

[COMPROBADO] Credencial del encargo. Nombre de pila es constructo de diseño.

Clients

slugNombreEdadMatizCoverAsset
marisolMarisol Vega47honey balayagemarisol.jpgassets/clients/marisol.jpg
luciaLucía Campos29blue-blacklucia.jpgassets/clients/lucia.jpg
aminaAmina El Idrissi38copperamina.jpgassets/clients/amina.jpg
pilarPilar Núñez61silverpilar.jpgassets/clients/pilar.jpg
diegoDiego Rivas33ashy browndiego.jpgassets/clients/diego.jpg

[COMPROBADO] Slugs, nombres, edades y matices del encargo. Fotos en el case.

Formulas (8 · todas SAVED)

Las marcas, tonos y gramos son catálogo demo, no recetas oficiales de fabricante ni historial clínico real. [SUPUESTO de catálogo]

idClientFecha (demo)OxidanteMinLíneasNotas
frm_diego_1diego2026-08-012030Majirel 6.1 20 g + Majirel 7.1 20 gCeniza. No calentar.
frm_lucia_1lucia2026-07-301035Koleston 2/0 30 g + Koleston 2/8 10 gBlue-black. Cubrir canas del nacimiento.
frm_amina_2amina2026-07-083035Igora 7-77 25 g + Igora 8-77 15 gCobre. 30 vol: avisar en Hoy.
frm_marisol_2marisol2026-06-242025Dia Light 9.3 20 g + Dia Light 8.3 20 gHoney. No bajar del 8.
frm_pilar_2pilar2026-06-101020Illumina 10/81 20 g + Illumina 9/81 20 gPlata. Solo matizar.
frm_amina_1amina2026-05-202035Igora 7-77 40 gPrimer cobre de la temporada.
frm_marisol_1marisol2026-04-152030Dia Light 9.3 30 g + Dia Light 10.3 10 gMás claro; tiró a paja.
frm_pilar_1pilar2026-03-181015Illumina 10/1 40 gPrimera plata.

lastFormula de Hoy = frm_diego_1 (la más reciente).
oxidantWarning = Amina silla 2, frm_amina_2, 30 vol.

Sittings (hoy = 2026-08-19)

idchairClientday
sit_11marisol2026-08-19
sit_22amina2026-08-19
sit_33pilar2026-08-19

Lucía y Diego no tienen sitting hoy.

Las cinco fichas, las ocho fórmulas y las tres sillas son seed de producto. No se presentan como clientela real de un salón de Usera. [SUPUESTO de catálogo]

8. Evolución posible (no implementada)

CambioNivel
Status APPLIED explícito (gesto, no resultado)L+1
Alta de clienta + fotoL+1
Rol DUEÑA solo lecturaL+1
Segunda colorista + author_id filtradoL+1 / L3
Soft delete + audit logL3
Agenda / slotsFuera (H2)
TPV / ticketFuera (microcopy)
Recetario oficial de marcaFuera (fórmula ≠ receta)

06-tech-stack.md

06 — Stack tecnológico — MATIZ

1. Visión

CapaTecnologíaNotas
FrontendAngular standalone + signalsPuerto 4200 · PWA mobile-first
EstilosTailwind CSSTokens chapel (yeso, pizarra, amatista, lino, peltre, oro)
BackendNestJS TypeScriptPuerto 3019, prefijo /api
PersistenciaPostgreSQL (ensureSchema o Prisma)Tablas users, clients, formulas, sittings (+ sessions Should)
DBNeon PostgreSQLproject mute-wave-74707210
AuthJWTRol COLORISTA
DiseñoPaper01M0CDJZPXVWCVYZC5Z5V9ZRQE
Repo/Users/cristian/orca/matiz-app/GitHub Criscode2022/matiz-app

Stack fijo del cron (D-P0-03). No React/Next/Firebase.
[COMPROBADO] Puerto 3019, Neon, Paper y repo constan en el encargo.

2. Estructura

matiz-app/
├── apps/api/     # Nest (paquete independiente)
├── apps/web/     # Angular + Tailwind (paquete independiente)
└── package.json  # scripts api / start · apps independientes

D-P1-03: npm --prefix o pnpm --filter. Prohibido npm workspaces que rompen Angular (AP-06).

Scripts raíz esperados:

ScriptAcción
apiArranca Nest en :3019
start / webArranca Angular en :4200

3. API pública vs JWT

MétodoRutaAuth
POST/api/auth/loginPúblico
GET/api/healthPúblico
GET/api/meJWT COLORISTA
GET/api/hoyJWT COLORISTA
GET/api/clientsJWT COLORISTA
GET/api/clients/:slugJWT COLORISTA
POST/api/clients/:slug/formulasJWT COLORISTA
GET/api/formulas/:idJWT COLORISTA
PATCH/api/formulas/:idJWT COLORISTA

CORS: origen http://localhost:4200 en local.

[COMPROBADO] Superficie API del encargo. No se añaden Must extra (/appointments, /payments, /sittings POST).

4. Justificación

ElecciónRazón
Angular + NestAlineación con la serie daily y el handoff del estudio
Neon serverlessVolumen bajo (S1: 40–80 fichas; demo 5 + 8 fórmulas); no cola Redis
JWT COLORISTAD-P1-05: herramienta interna cerrada desde el día 1
Auth-first en servidorSource of truth = guard; el cliente no «esconde» fichas
Puerto 3019Evitar colisión con ZAGUÁN :3018 / MARGEN :3017 / CESTA :3016
PWA (no nativa)S2: móvil junto al lavacabezas; install Could L+1
JSONB lines1–4 líneas por fórmula; no merece tabla hija en v1

5. Variables

.env local (nunca git):

VariableServicioDescripción
DATABASE_URLAPINeon mute-wave-74707210
JWT_SECRETAPIFirma tokens
PORTAPIopcional, 3019
JWT_EXPIRESAPIopcional (p. ej. 12h)

.env.example solo placeholders.
Web: URL de API en ApiService (default http://localhost:3019/api).

6. Dependencias de producto (mínimas)

API

  • @nestjs/common / core / platform-express
  • @nestjs/jwt (o verificación manual)
  • bcrypt (o bcryptjs)
  • cliente Postgres (pg) o Prisma
  • class-validator + class-transformer

Web

  • Angular standalone
  • Tailwind 3
  • fuentes: Libre Baskerville + Nunito Sans (Google Fonts o self-host)

7. Arranque (contrato)

cd /Users/cristian/orca/matiz-app
# instalar apps independientes (npm --prefix o pnpm --filter)
# apps/api/.env → DATABASE_URL + JWT_SECRET + PORT=3019
# migrate / ensureSchema + seed
# api → http://localhost:3019
# web → http://localhost:4200

8. Lo que este stack no es

NoPor qué
npm workspacesAP-06
Firebase AuthStack fijo JWT + Neon
Next.js / ReactD-P0-03
Pasarela de pago / TPVMATIZ no cobra
Calendario / Booksy APIMATIZ no agenda
Elasticsearch5 fichas no lo necesitan
PMS de cadenaS1: 1–2 coloristas

9. Nota sobre el repo existente

[COMPROBADO] Existe /Users/cristian/orca/matiz-app y el remoto previsto Criscode2022/matiz-app.
El scaffold contiene leftover de ZAGUÁN (listings, compare, mesa, modelos listing.ts, páginas wizard / comparar / finca). Esta suite define el contrato MATIZ; el código debe alinearse a las rutas y API de los docs 03–05, no al leftover. [DECISIÓN]

07-creative-direction.md

07 — Dirección creativa — MATIZ

1. Concepto

MATIZ es un cuaderno de capilla en pantalla: se entra a escribir un matiz y se sale con una fórmula, no una landing de «salón digital». El ground es yeso de capilla. Las sillas se apoyan como bancos. La amatista es vidriera, no un botón de marketing.

Mood elegido: chapel.
Primer instinto descartado: candlelit (ámbar × oxblood de «spa de lujo»). Un salón de Usera no es un spa; es un oficio con luz de vidriera y pizarra.

[COMPROBADO] Mood, ground y paleta salen de docs/00-day-brief.md.

2. Territorio visual

EjeNo
SuperficieYeso #E6E4ED, lino de card, pizarraHormigón ZAGUÁN, papel MARGEN, musgo CESTA, cromo RONDA, agua DERIVA
TipoLibre Baskerville display + Nunito Sans UIInstrument Serif, Sora, Cormorant, Public Sans, Newsreader, Outfit, Spectral, Manrope, Zilla, Mulish, Lora, Karla
FotoRetrato de ficha, luz quieta, oficioStock de spa, «pareja feliz con mechas», lookbook de pasarela
GridBento de sillas + editor de recetaHero 2-col + 3 cards de features (AP-12)
AcentoAmatista #6B4C9A en tab activo y GuardarÓxido inmobiliario, cadmio editorial, ámbar de vela
ChromeBottom tabs Hoy · Fichas · CuentaMasthead de periódico, rail, wizard 1—2—3, sidebar CRM

3. Paleta

TokenHexRol
Yeso / G-CHAPEL#E6E4EDGround de página. Violeta frío, no beige.
Pizarra#2B2838Texto, wordmark, cuerpo. Casi negro, no púrpura saturado.
Amatista#6B4C9ATab activo, acento, CTA Guardar, aviso 30 vol
Lino#F7F5FASolo cards (silla, ficha, línea). Nunca el fondo de página.
Peltre#6E6A78Meta, labels, tab idle, matiz, minutos
Hoja de oro#C4A35AAcento secundario, poco: badge SAVED, filete de success

Jerarquía de color: el yeso carga; la amatista puntúa; el lino encuadra la ficha. Si la amatista pinta fondos enteros, deja de ser vidriera y se vuelve «brand purple». El oro no decora el header: solo puntual. [DECISIÓN]

4. Tipografía

UsoFamiliaTamaños de referencia
WordmarkLibre Baskerville 70024–28 desktop · 22 mobile
Titular de Hoy / fichaLibre Baskerville 70032–40
Nombre de clientaLibre Baskerville 70024–32 desktop · 22 mobile
Gramos / oxidanteNunito Sans 70020–24
Cuerpo de notasNunito Sans 40016–17 / 26–28 lh
UI, tabs, form, listaNunito Sans 400/600/70014 / 16
Meta (matiz, silla, fecha)Nunito Sans 60012–13, tracking 0.04em, peltre

Prohibido: Libre Baskerville por debajo de 20px (se rompe). Nunito Sans en el wordmark (se vuelve startup).

Fuentes vetadas de la serie: Instrument Serif / Sora / Cormorant / Public Sans / Spectral / Manrope / Zilla / Mulish / Newsreader / Outfit / Lora / Karla.

5. Fotografía

Las imágenes del case son el sistema, no un moodboard:

AssetLectura visualUso
hero.jpgCapilla / vidriera / oficio quietoCover UX · atmósfera, no hero de marketing
clients/marisol.jpgMarisol, 47, honeyFicha + silla 1
clients/lucia.jpgLucía, 29, blue-blackFicha
clients/amina.jpgAmina, 38, copperFicha + silla 2
clients/pilar.jpgPilar, 61, silverFicha + silla 3
clients/diego.jpgDiego, 33, ashy brownFicha + tile última fórmula

Crop: fichas y tiles en 1:1 (retrato). No recorte 16:10 editorial ni 4:3 de fachada. [DECISIÓN] 1:1 de oficio, distinto del 4:3 de ZAGUÁN y del 16:10 de MARGEN.

Tratamiento: saturación real, sin LUT «salon gold», sin piel HDR. La amatista no se superpone como filtro: vive en UI.

Swatches del bento (H-BENTO): círculos 24–32px con hex aproximados de oficio (miel, cobre, plata), no fotografías recortadas en círculo. [DECISIÓN]

6. Layout

Bottom tabs (S-BOTTOM)

┌─────────────────────────────────┐
│  (contenido · yeso)             │
├─────────────────────────────────┤
│  Hoy      Fichas      Cuenta    │  56–64px + safe-area
└─────────────────────────────────┘

Tab activo: Nunito 12 amatista + filete 2px amatista arriba del ítem.
Idle: peltre. Sin iconos outline de tijera si el label basta; si hay icono, trazo 1.5 pizarra/amatista.

Home (H-BENTO)

  1. Wordmark + fecha civil («Miércoles 19»).
  2. Lead en el fold: 3 tiles de silla (nombre Libre, silla meta, matiz peltre, swatch).
  3. Tile de última fórmula (Diego · 6.1 + 7.1 · 20 vol).
  4. Aviso oxidante si aplica (Amina, 30 vol).

No hay portada fotográfica a 60vh. hero.jpg vive en UX-00 Cover, no como hero de producto. [DECISIÓN] AP-21: 3 sillas reales en el fold.

Editor (F-EDIT)

Una columna. Líneas apiladas (marca · tono · gramos). Chips de oxidante. Minutos. Notas. CTA amatista Guardar. Disclaimers encima del CTA.

Desktop 1440: editor + staff de ficha. Misma receta, no otro producto.

Ficha

Retrato 1:1 a ~160–200px, no full-bleed. Nombre → matiz → historial. Blancos de capilla, no de landing.

Cuenta

Lista densa sobre yeso. No imita el bento. Es trastienda: salón, rol, salir.

7. Motion

SuperficieSpec
Hover tileRule pizarra; no elevación 8px ni scale 1.04
Cambio de tabFade 160ms; off si reduced-motion
SkeletonPulso 1.2s en peltre/lino; off si reduced-motion
GuardarBadge oro, no confetti ni «gotas de tinte»

Prohibido: parallax de vidriera, scroll-jacking, page transitions de 400ms, sticker de «NUEVO» parpadeante, animación de mechas.

8. Anti-clichés (serie ALS-2)

ClichéPor qué no
Spa candlelit ámbar × oxbloodPrimer instinto del brief; vetado
Hormigón + óxido (ZAGUÁN)Finca, no oficio de color
Papel + cadmio kiosco (MARGEN)Editorial, no tazón
Botanical / musgo (CESTA)Despensa
Chrome / neón (RONDA)Gaming
«Pareja feliz con mechas» stockMentira social del «ya eres otra»
Booksy rosa + slotRompe S3 / H2
Iconos outline de tijera 2014Chrome tipográfico basta

9. Tono de marca (visual + verbal, puente a doc 09)

Cercano de oficio y de lavacabezas. Tuteo. Segunda persona cuando habla la UI. Tercera cuando habla la ficha.

El producto se escribe y se guarda. No agenda, no cobra, no vende el bote de la caja.

10. Criterios de aceptación creativos

  1. Ground de página = #E6E4ED. Cards = #F7F5FA. Nunca al revés.
  2. Amatista en el tab activo y en el CTA, no un header púrpura lleno.
  3. 3 sillas visibles en el primer viewport mobile (tras el wordmark).
  4. Ninguna fuente de la lista vetada.
  5. Home reconocible como bento de Hoy, no como hero 2-col de startup.
  6. Success no celebra un «color aplicado» ni un cobro.
  7. Oro solo en badge / filete de success, no en el chrome.

08-design-system.md

08 — Design system — MATIZ

1. Tokens

--color-chapel:   #E6E4ED;   /* G-CHAPEL ground */
--color-slate:    #2B2838;
--color-amethyst: #6B4C9A;
--color-linen:    #F7F5FA;   /* cards only — lino */
--color-pewter:   #6E6A78;
--color-gold:     #C4A35A;   /* acento secundario */
--color-saved:    #C4A35A;   /* badge SAVED */

--font-display: "Libre Baskerville", "Iowan Old Style", serif;
--font-ui:      "Nunito Sans", system-ui, sans-serif;

--radius-card: 4px;          /* lino de ficha, no pill */
--radius-cta:  4px;
--rule-tab:    2px solid var(--color-amethyst);
--space:       4 / 8 / 12 / 16 / 24 / 32 / 48 / 64;

Tailwind (referencia):

KeyValor
colors.chapel#E6E4ED
colors.slate#2B2838
colors.amethyst#6B4C9A
colors.linen#F7F5FA
colors.pewter#6E6A78
colors.gold#C4A35A
fontFamily.displayLibre Baskerville
fontFamily.sansNunito Sans

[COMPROBADO] Hex y tipo = brief.

2. Escala tipográfica

TokenFamiliaSize / lhUso
display-lLibre 70040 / 44Titular Hoy desktop · nombre ficha desktop
display-mLibre 70028 / 32Nombre tile · wordmark
display-sLibre 70022 / 28Nombre 390
gramsNunito 70020 / 24Gramos · volúmenes
bodyNunito 40016 / 26Notas, disclaimers
uiNunito 40016 / 24Form, lista, editor
metaNunito 60013 / 18Silla, matiz, fecha
tabNunito 70012 / 16 · tracking 0.04emHoy · Fichas · Cuenta

3. Componentes

Bottom tabs (S-BOTTOM)

SpecValor
Alto 39056px + safe-area
Alto desktop64px fijo inferior
FondoLino / yeso con rule 1px peltre 30%
Tab activoNunito 12 · amatista + filete 2px amatista arriba
Tab idlePeltre
Targets≥44×44; 3 columnas iguales

Slots: Hoy / · Fichas /fichas · Cuenta /cuenta.
Login no lleva tabs.

Tile de silla (Hoy)

┌─────────────────────────────┐
│ ①  Marisol Vega             │  nº silla meta + nombre Libre
│ honey balayage        ●     │  matiz peltre + swatch
└─────────────────────────────┘
  • Fondo lino, sin sombra.
  • Toda la tile es un <a> a /fichas/:slug.
  • Hover: rule 1px pizarra. Sin lift.
  • Swatch 24px, aria-hidden (el matiz ya está en texto).

Tile última fórmula

Mismas reglas. Muestra clienta, resumen de líneas (6.1 + 7.1), oxidante, fecha. Enlace a /fichas/:slug.

Aviso oxidante

Banner lino + filete amatista 2px izquierda. Texto Nunito 16. No es alert agresivo; es oficio.

Fila de ficha (lista)

ColumnaContenido
Cover48×48 1:1, radius 4
NombreLibre 22 / Nunito 16 en 390
Matizmeta peltre
Fechameta

Toda la fila es un <a> a /fichas/:slug.

Ficha de clienta

BloqueSpec
Columnamax-width 720px
Cover160–200px 1:1
Nombredisplay-l / display-s
Matiz + edadmeta
Historiallista: fecha, oxidante, líneas, notas
Disclaimer«La ficha no es una cita. MATIZ no agenda.»
CTANueva fórmula · primary amatista

Editor (F-EDIT)

PiezaSpec
Línea3 campos: marca (flex), tono (8ch), gramos (6ch + «g»)
Añadir / quitarGhost; quitar disabled si queda 1
Oxidante3 chips radio 10 / 20 / 30 vol · target ≥44
Minutosinput number + «min»
Notastextarea 4+ filas
Guardarprimary amatista, h-44, full-width en 390
Disclaimers2 bloques body sobre el CTA

Desktop 1440: grid 2 col (editor 7 / staff 5). Staff: retrato, nombre, último matiz, disclaimer.

Botones

VarianteUsoSpec
PrimaryGuardar, Entrar, Nueva fórmulaFondo amatista, texto lino, h-44, radius 4
SecondaryAñadir línea, ver fichaRule pizarra, texto pizarra, fondo yeso
GhostSalir, Reintentar, Quitar líneaTexto peltre
DangerNo hay borrado Must

Pending: disabled + aria-busy. No spinner de marca.

Form

Labels visibles (no placeholder-only). Inputs h-44, radius 4, rule peltre 40%, foco outline 2px amatista offset 2.

Chips de oxidante: <button aria-pressed>. Selected: amatista texto o rule 2px amatista. Idle: lino + rule.

Estados

EstadoReceta
Empty HoyTitular Libre 28 + 1 línea Nunito + sin CTA de agenda
Empty FichasTitular + (seed no debería; útil si se vacía)
Empty historial«Aún no hay fórmulas.» + CTA Nueva
ErrorBanner filete amatista + texto + Reintentar
LoadingSkeleton bento: 3 tiles + 1 barra; lista: 5 filas
404 ficha«Esa ficha no está.» + link a Fichas

4. Grid y breakpoints

ViewportComportamiento
≥1200Hoy bento 3+1 tiles; editor 2 col; fichas lista
960–1199Bento 2+2; editor 2 col compacto
600–959Bento 1 col; editor 1 col; tabs inferiores
~390 (PWA)1 col; tiles full; editor full; tabs + safe-area

Padding horizontal: 24 desktop, 16 mobile.
Main autenticado: padding-bottom: calc(64px + env(safe-area-inset-bottom) + 16px) para no tapar el CTA.

5. Iconografía

Mínima. El cuaderno no es un set de iconos outline de tijera.

SitioRecurso
TabsTexto. Icono trazo 1.5 de 20px opcional (banco / ficha / persona)
SwatchCírculo de color, no icono
EmptyNada o una rule amatista de 32px
StatusTexto de badge SAVED, no semáforo

Si hace falta un icono (salir, error): trazo 1.5, 20px, pizarra. No pack Remix/Lucide a color.

6. Elevación y borde

SuperficieTratamiento
PáginaYeso plano
Card / tileLino plano + rule 1px peltre 30%
ListaFilas separadas por rule
ModalNo hay modal Must

Cero sombras shadow-lg. La capilla no flota.

7. Mapa componente → ruta

ComponenteRutas
BottomTabsComponent/, /fichas, /fichas/:slug, /fichas/:slug/formula, /ok/:id, /cuenta
ChairTile/
LastFormulaTile/
OxidantWarning/
ClientRow/fichas
FichaArticle/fichas/:slug
FormulaEditor/fichas/:slug/formula
LineRoweditor
EmptyState / ErrorBanner / SkeletonBento/, /fichas

8. Criterios de aceptación DS

  1. Tokens Tailwind = hex del brief, sin «casi blanco» #FAFAFA como ground.
  2. Cards en lino; página en yeso.
  3. Primary CTA nunca en peltre sobre yeso (contraste).
  4. Libre Baskerville no aparece en labels de form ni en tabs.
  5. Recorte de ficha aspect-square, no 16:10 editorial ni 4:3 de finca.
  6. Targets de tabs, chips y CTA ≥44px.
  7. Tabs no tapan el CTA Guardar (padding-bottom).

09-content-guide.md

09 — Guía de contenido — MATIZ

1. Voz

Cercana de oficio y de lavacabezas. Tuteo en UI. Segunda persona cuando la interfaz habla. Tercera cuando habla la ficha.

Sin jerga de spa («experiencia cromática», «tu mejor versión», «no te lo pierdas»). Sin sede electrónica. Sin «reserva tu slot».

El producto se escribe y se guarda. No agenda, no cobra, no vende el bote de la caja.

2. Palabras permitidas / prohibidas

UsarNo usar
Fórmula / líneas / gramosReceta de caja, «el bote», «kit»
Ficha / clienta / clienteCita, slot, «appointment», paciente
Silla / hoyAgenda, reserva, Booksy, «próxima hora»
Guardar / guardadaAplicar, «ya está en el pelo», «listo el color»
Oxidante 10 / 20 / 30 vol«El revelador de la caja», «peróxido» en UI
Cuaderno / oficioInbox, CRM, backoffice, TPV
ColoristaStylist, artist, «beauty pro»
MATIZ no agenda / no cobraPide cita, pagar ahora, ticket

3. Microcopy duro (literales irrenunciables)

El brief fija oposiciones. Aparecen donde el usuario puede confundirse.

OposiciónDóndeCopy
Fórmula ≠ receta comercialEditor sobre las líneas · ficha«Esto es una fórmula, no la receta de la caja. 6/7 + 20 vol no es el bote.»
Ficha ≠ citaFicha bajo el nombre · Hoy pie«La ficha no es una cita. MATIZ no agenda.»
Guardado ≠ aplicadoEditor sobre Guardar · success«Guardado no es aplicado. El color aún no está en el pelo.»
MATIZ no agendaHoy empty · cuenta · ficha«MATIZ no agenda. El WhatsApp de Inma agenda.»
MATIZ no cobraCuenta · success pie«MATIZ no cobra. El ticket lo hace la caja.»

[DECISIÓN] Los cinco textos son obligatorios en UI. No se diluyen a un tooltip.

4. Microcopy por superficie

SuperficieCopy
EsloganLa fórmula, no la cita.
Línea de salónSalón Amparo · Calle Amparo 22, Usera
Login titularEntrar al cuaderno
Login ayudaSolo colorista. No hay cuenta de clienta.
Login submitEntrar
Login errorEmail o contraseña no valen.
Hoy titularHoy
Hoy fechaMiércoles 19 de agosto
Tile sillaSilla 1 · Marisol Vega · honey balayage
Tile últimaÚltima fórmula · Diego Rivas · 6.1 + 7.1 · 20 vol
Aviso oxidanteRevisa el oxidante: Amina trae 30 vol. 30 no es 20.
Empty HoyHoy no hay sillas. El hoy lo rellena Nora por la mañana.
Fichas titularFichas
Empty FichasAún no hay fichas.
Ficha CTANueva fórmula
Ficha volver← Fichas
Ficha disclaimerLa ficha no es una cita. MATIZ no agenda.
Empty historialAún no hay fórmulas en esta ficha.
Editor titularNueva fórmula
Editor líneasMarca · Tono · Gramos
Añadir líneaAñadir línea
Quitar líneaQuitar
Oxidante10 vol · 20 vol · 30 vol
MinutosMinutos de pose
NotasNotas de oficio (no del ticket)
Editor disclaimer 1Esto es una fórmula, no la receta de la caja.
Editor disclaimer 2Guardar no es aplicar.
GuardarGuardar fórmula
Success titularGuardada.
Success cuerpoGuardado no es aplicado. El color aún no está en el pelo.
Success CTAVer la ficha · Volver a Hoy
ErrorNo hemos podido cargar el cuaderno.
Error retryReintentar
Loading (doc 16)Abriendo el cuaderno…
404 fichaEsa ficha no está.
Cuenta titularCuenta
Cuenta rolColorista
Cuenta pieMATIZ no agenda. MATIZ no cobra.
SalirSalir
TabsHoy · Fichas · Cuenta

5. Fichas seed (tono)

Las fichas demo se escriben como nota de oficio, no como folleto de marca ni como crónica de lookbook.

FichaTono
MarisolObservación: honey, no bajar del 8, el 10.3 tiró a paja. Sin «luminoso y dorado».
LucíaBlue-black, nacimiento, 10 vol. Sin «intenso y misterioso».
AminaCobre, 30 vol en la última: se avisa, no se celebra.
PilarPlata, solo matizar. Sin «plata de revista».
DiegoCeniza, no calentar. Sin «cool brown editorial».

No inventar declaraciones de clientas reales. Si hay una voz, es de la nota (neutra) o de persona de diseño etiquetada.

6. Nombres propios del seed

NombreUso
Nora BeltránCOLORISTA; nora@matiz.salon
Inma SotoDUEÑA; no es cuenta
Marisol VegaFicha marisol; silla 1; no usa la app
Lucía CamposFicha lucia
Amina El IdrissiFicha amina; silla 2
Pilar NúñezFicha pilar; silla 3
Diego RivasFicha diego; última fórmula
Salón AmparoCalle Amparo 22, Usera; atrezzo de persona

No inventar CIF, número de colegiada, ni precios de servicio. [SUPUESTO de catálogo demo]
Marcas seed (Dia Light, Majirel, Koleston, Igora, Illumina) son nombres de oficio reconocibles, no recetas oficiales ni partnership. No se afirman como ficha real 2026.

7. Tono por estado

StatusFrase UIFrase que Nora podría decir (fuera de app)
SAVEDGuardada«Está apuntada. Aún no te la he puesto.»
(aplicar)— no existe«Ahora sí: al lavacabezas.»

La app no envía esos mensajes en v1. El copy solo prepara el habla.

8. Capitalización y números

  • Wordmark: MATIZ en versales Libre Baskerville.
  • Nombres: Marisol Vega, Amina El Idrissi (respetar partículas).
  • Gramos: 20 g (espacio).
  • Oxidante: 20 vol (no «20 volúmenes» en chip; sí en aviso largo).
  • Minutos: 25 min.
  • Edad: 47 junto al matiz, no «47 años de edad».
  • Silla: Silla 1 (mayúscula S).
  • Fecha: 24 jun en lista; miércoles 19 de agosto en Hoy.
  • No mezclar «20vol» pegado ni «g.» con punto.

9. Idioma

es-ES. Fácil lectura. EN no en v1.
No mezclar «formula» en UI. En API sí: formulas, oxidant, shade.

«Clienta» en UI cuando el seed es mujer; «cliente» para Diego; «fichas» para el conjunto. [DECISIÓN]

10. Accesibilidad de copy

  • No transmitir estado solo con color («la amatista significa 30 vol»).
  • Badge lleva texto: Guardada.
  • Aviso de oxidante lleva nombre y número, no solo el banner púrpura.
  • Alt de fotos: nombre + matiz, no «imagen1».
    • hero: «Luz de capilla y pizarra; oficio de color, no spa.»
    • marisol: «Marisol Vega, ficha honey balayage.»
    • lucia: «Lucía Campos, ficha blue-black.»
    • amina: «Amina El Idrissi, ficha copper.»
    • pilar: «Pilar Núñez, ficha silver.»
    • diego: «Diego Rivas, ficha ashy brown.»

11. Piezas que no se escriben

NoMotivo
«Reserva tu hueco» / «Pedir cita»H2 · Ficha ≠ cita
«Ya está aplicado» como hecho de guardarGuardado ≠ aplicado
«6/7 de Wella, como en la caja»Fórmula ≠ receta comercial
«Pagar 42 €» / «Ticket»MATIZ no cobra
Ranking de los looks más vistosLikes, otro producto
Lorem / «lorem ipsum mechas»Prohibido en el case
Declaraciones inventadas de un salón realVerdad de catálogo

10-accessibility.md

10 — Accesibilidad — MATIZ

Objetivo: WCAG 2.2 AA. No se declara conformidad legal certificada.
Este documento cubre la UI (login, Hoy, fichas, editor, cuenta). No audita la accesibilidad física del Salón Amparo (lavacabezas, umbral, silla). [SUPUESTO]

1. Decisiones

TemaDecisión
Tipo UINunito Sans ≥16px body
DisplayLibre Baskerville no se usa por debajo de 20px
ContrastePizarra #2B2838 sobre #E6E4ED / #F7F5FA
CTA amatistaTexto lino sobre #6B4C9A
Badge SAVEDTexto pizarra sobre oro claro o oro sobre lino ≥14px bold
FocoOutline 2px amatista, offset 2px, visible (no outline-none global)
Tabs<nav> + aria-current="page"
EditorLabels visibles; chips aria-pressed; líneas en <fieldset>
Imágenesalt = nombre + matiz
FormLabels visibles, no placeholder-only
ErroresTexto, no solo color
MobileTargets ≥44px (tabs, chips, Guardar) · S2 manos húmedas
MovimientoFade 160ms; prefers-reduced-motion: reduce → 0
Live regionsaria-live="polite" en empty/error de Hoy y editor

2. Contraste (comprobación de diseño)

ParUsoNota
#2B2838 / #E6E4EDBody sobre yesoPasa AA y AAA cuerpo
#2B2838 / #F7F5FABody sobre linoPasa AA y AAA
#F7F5FA / #6B4C9ACTA / tab activoPasa AA para texto ≥14px bold
#6E6A78 / #E6E4EDMeta peltreVerificar ≥13px; no usar en 11px
#6E6A78 como texto 12px sobre linoProhibido para body
Tab idle peltre 50%Solo si no es el único indicador+ label textual Hoy / Fichas / Cuenta
#2B2838 / #C4A35ABadge SAVEDVerificar bold ≥14; si falla, pizarra sobre lino + rule oro

3. Teclado

FlujoOrden
LoginEmail → password → submit
HoyWordmark → tiles silla 1–3 → última fórmula → aviso
FichasFilas en orden
FichaAtrás → Nueva fórmula → historial (lectura)
EditorAtrás → líneas (marca, tono, gramos, quitar) → añadir → oxidante → minutos → notas → Guardar
CuentaSalir
TabsAl final del documento o landmark navigation; Tab cicla Hoy / Fichas / Cuenta
  • Enter en tile = navegar a ficha.
  • Enter en chip oxidante = selecciona (aria-pressed).
  • Escape no cierra el editor (no es modal). Confirm de descarte no es Must.
  • Focus trap: no hay modal Must.

4. Semántica

PiezaMarkup
Tabs<nav aria-label="Cuaderno"> · activo aria-current="page"
Hoy<section aria-label="Sillas de hoy">
Tile sillaun <a> por silla; incluye nombre, silla, matiz en texto
Swatcharia-hidden="true" (decorativo)
Aviso oxidanterole="status" + texto completo
Oxidanterole="group" + aria-label="Oxidante"
Líneas<fieldset> + <legend> «Líneas de color»
Gramos<label> + input type="number" + texto «gramos»
DisclaimerNo es alert; es texto normal + borde
Badgestexto visible; no solo icono
Skeletonsaria-busy="true" en el contenedor; aria-hidden en placeholders

5. Lector de pantalla — copy

SituaciónAnuncio
Tile«Silla 1, Marisol Vega, honey balayage»
Chip«20 vol, pulsado» / «30 vol, no pulsado»
Empty Hoy«Hoy no hay sillas. El hoy lo rellena Nora por la mañana.»
ErrorAnuncio en live region; foco al banner
Success«Guardada. Guardado no es aplicado.»
Aviso«Revisa el oxidante: Amina trae 30 volúmenes. 30 no es 20.»

6. Producto vs salón físico

Lo que MATIZ puede hacerLo que no afirma
Targets 44px junto al lavacabezasQue el lavacabezas de Amparo 22 sea accesible de hecho
Editor usable con teclado y zoom 200%Que el local tenga rampa
Auth-first para no exponer fichasQue Inma haya hecho un DPIA

[DECISIÓN] v1 no incluye auditoría a11y de sede. El campo age es dato de ficha demo, no un certificado.

7. Riesgos

RiesgoMitigación
Tabs tapan Guardar en 390padding-bottom del main
Libre Baskerville en meta 12pxProhibido; meta = Nunito 13
Amatista como único estado de tab+ aria-current="page" + label
Foto sin altChecklist seed: 5 alts + hero
Chip 30 vol leído solo por colorLabel «30 vol» siempre
Manos húmedas (S2)Targets ≥44; no gestos swipe; no hover-only
Input number sin unidad«g» y «min» visibles junto al campo

8. QA a11y (mínimo)

#PruebaPasa si
1Teclado login → Hoy → ficha → editor → okSin trampa; foco visible
2Teclado tabs Hoy / Fichas / CuentaAcciones alcanzables
3Zoom 200% móvil 390Chips usables; no solapa inputs; tabs no tapan CTA
4Lighthouse a11y ≥ 90 en / autenticadoSin contrast fails de tokens
5VoiceOver/NVDA: tile anunciadaNombre, silla, matiz comprensibles
6prefers-reduced-motionSin stagger ni slide
7Contraste badge SAVED y CTATexto bold ≥14

9. Criterios de aceptación

  1. Ningún control de tab, chip o CTA mide menos de 44×44 CSS px.
  2. El disclaimer no depende del color amatista para ser entendido.
  3. Las 5 fotos seed + hero tienen alt no vacío y no genérico.
  4. El foco no se pierde al reintentar un error de red.
  5. En 390 el CTA Guardar queda por encima de los tabs.
  6. Una ficha no se anuncia en Hoy sin token (no hay HTML público de clientas).

11-privacy-security.md

11 — Privacidad y seguridad — MATIZ

1. Datos que existen

DatoDóndeBase / nota
Email y hash de la coloristausersContrato interno del cuaderno
Fichas (nombre, edad, matiz, foto)clientsCatálogo de producto; seed demo · PII de atrezzo
Fórmulas (líneas, oxidante, notas)formulasOficio; no es historial clínico certificado
Sillas de un díasittingsAsiento, no cita médica
Sesión JWTAuthorization + sessions?Solo COLORISTA

No se pide cuenta a la clienta. No hay teléfono, email ni DNI de Marisol en producto. No hay tracking de menores. Las fotos seed no se presentan como clientela real.

2. Minimización

Qué no se recoge en v1Por qué
Teléfono / email de la clientaH3 · no lead · no recordatorio
DNI / alergias / ficha sanitariaMATIZ no es historial clínico
Geolocalización del dispositivoEl salón es uno
Hora de la citaFicha ≠ cita
Importe / tarjetaMATIZ no cobra
Fotos subidas por la clientaNo hay UGC

Edad en seed es dato de ficha demo, no un requisito de negocio. L+1 podría omitirla. [DECISIÓN]

3. Seguridad L2

ControlSpec
AuthJWT Bearer · role === COLORISTA
Passwordbcrypt cost 12; nunca en response
LecturaGET hoy / clients / formulas / me → 401 sin token
EscrituraPOST / PATCH formulas → 401 sin token
Rate limit/auth/login (anti stuffing; Should si no llega al slice)
CORSOrigen http://localhost:4200 en local
SecretosSolo .env (no git)
Cover allowlistFilenames seed; no URL arbitraria
next post-loginPaths relativos allowlist (/, /fichas, /fichas/:slug, /fichas/:slug/formula, /ok/:id, /cuenta)
Auth-first webGuard Angular en todas las rutas salvo /login

No se declara cumplimiento RGPD definitivo. Sí: minimización, auth-first y derecho a borrar la cuenta de colorista (manual L2). Las fichas seed se tratan como datos de demo, no como encargo de tratamiento de un salón real.

4. Superficies de riesgo

RiesgoMitigación v1
Enumeración de slugs401 sin token; 404 con token si no existe (no 403 que filtre)
JWT en localStorageAceptable en demo; fichas son atrezzo, no clientela real
Foto de clienta en public/Inevitable para el seed; no hay cara pública que las liste sin auth. HTML autenticado
Leftover listings / cuentas ZAGUÁN en DBI01 de build: schema propio, no mezclar
Notas de fórmula con PII extraCopy: «notas de oficio»; no pedir teléfono
Inma pide un loginS4: no hay fila; no se crea

5. Roles y autorización (recordatorio)

RecursoAnónimoCOLORISTA
POST login / GET health
GET me / hoy / clients / :slug / formulas401
POST / PATCH formulas401
Agenda / cobroNo existeNo existe

Inma, Marisol, Lucía, Amina, Pilar y Diego no tienen fila en users.

6. Criterios de aceptación

  1. Response de login no incluye password ni password_hash.
  2. GET /api/clients y GET /api/hoy sin Bearer son 401, no 200 [].
  3. GET /api/clients/marisol sin Bearer es 401, no 200.
  4. No hay formulario público que pida teléfono o email de clienta.
  5. / sin token redirige a /login (no renderiza sillas en HTML anónimo).
  6. .env no se commitea.

12-analytics.md

12 — Analítica — MATIZ

North star: % de sillas de hoy con fórmula guardada el mismo día (hoy_view con sittings + formula_save ligado a clientSlug de esas sillas).

Instrumentación modelo. No hay proveedor conectado en v1 salvo que el build añada un logger local. [DECISIÓN] No se inventan cifras de conversión.

1. Eventos

EventoCuándoProps mín.
colorista_loginJWT ok
hoy_viewRender de / autenticadosittings (0–3)
ficha_openApertura de fichaslug
editor_openEntrada a /fichas/:slug/formulaslug
formula_savePOST 201id, slug, oxidant, lineCount
hoy_empty0 sittings
error_bannerUI-09surface

2. Qué no se mide

No eventoMotivo
signup_click / lead_submitNo existe CTA
appointment_bookedFicha ≠ cita
payment_okMATIZ no cobra
formula_appliedGuardado ≠ aplicado; el evento no existe
like / favoriteFuera
Identidad de Marisol como usuariaH3 · no es usuaria

Hipótesis H2: ficha_open sin appointment_click (el CTA no existe). Si en un test de 5 coloristas alguien busca «hora», se anota en research futuro, no como KPI inventado.

3. Funnel modelo (COLORISTA)

colorista_login  →  hoy_view  →  ficha_open  →  editor_open  →  formula_save

Activación = primer formula_save.
Completud del día = formula_save cuyo slug ∈ slugs de hoy_view.sittings / nº de sittings.

North star de sesión de demo: de 3 sillas, cuántas tienen un formula_save con createdAt del mismo día civil Europe/Madrid.

4. Integridad (producto, no vanidad)

SeñalCómo se observa
Auth-firstGET /hoy 401 sin token (QA, no analytics)
Expectativa de citaComentarios cualitativos; cero evento de reserva
Expectativa de aplicadoCero evento applied; copy en success
Salud4xx/5xx de hoy / formulas

5. Privacidad de analítica

Sin PII extra. slug es identificador de seed (marisol), no un email. No se guarda IP como usuario. Ver doc 11.
No se envían notes ni lines completas a un tercero.

6. Criterios de aceptación (si se instrumenta)

  1. Los 7 eventos de §1 tienen nombre estable.
  2. No se dispara ningún evento de lead / cita / cobro / aplicado.
  3. Un bloqueador de cookies no rompe el editor (analítica no es Must de runtime).

13-qa-test-plan.md

13 — Plan de pruebas — MATIZ

1. Smoke obligatorio (D-P1-06)

#AcciónEsperado
1GET /api/health200
2POST /api/auth/login nora@matiz.salon / password123200 · accessToken · user.role=COLORISTA
3GET /api/hoy con Bearer200 · 3 sittings · chairs 1 Marisol, 2 Amina, 3 Pilar
4GET /api/clients con Bearer200 · 5 slugs marisol, lucia, amina, pilar, diego
5ng build o serve webWeb arranca o build OK

[COMPROBADO] Smoke = health + login JWT + hoy + clients.

2. Casos funcionales

IDCasoEsperado
S1Slugs seedGET clients incluye los 5
S2Hoy sin JWT401 (no 200 [])
S3Clients sin JWT401
S4Clients/:slug sin JWT401
S5Slug inexistente (con JWT)404
S6Hoy UI/ muestra 3 sillas (nombre, nº, matiz) + última fórmula + aviso; cero CTA Agendar / Cobrar
S7Ficha ACnombre, matiz, historial, «ficha ≠ cita»
S8Home es bento/ autenticado es H-BENTO; no hero 2-col
S9Rutas auth/, /fichas, /cuenta sin token → /login
S10POST fórmula201 · status=SAVED · lines persistidas
S11PATCH fórmula200 · notes/líneas cambian; status sigue SAVED
S12PATCH no aplicaBody { status: "APPLIED" } ignorado o 400; no existe APPLIED
S13Login malo401 · mensaje en form
S14Empty Hoy0 sittings → UI-08
S15API caídaUI-09 + Reintentar conserva la vista
S16LoadingSkeleton bento antes del primer paint de Hoy
S17Mobile 390Tabs + editor; CTA ≥44px; Guardar no tapado; no hero 2-col
S18Fotos seed5 covers resuelven a assets/clients/*
S19Password no viaja de vueltalogin response sin hash
S20Body POST fórmulaacepta lines[], oxidant 10/20/30, minutes, notes
S21Success/ok/:id copy «Guardado no es aplicado»
S22GET me200 · Nora · Salón Amparo · Calle Amparo 22
S23Oxidante inválidoPOST oxidant 40 → 400
S24lines vacíasPOST lines: [] → 400
S25Microcopy ×5fórmula≠receta · ficha≠cita · guardado≠aplicado · no agenda · no cobra

3. Datos de prueba

UsoValor
COLORISTAnora@matiz.salon / password123
Ficha felizmarisol
Ficha 30 volamina
Última fórmuladiego · frm_diego_1
Sillas hoy1 marisol · 2 amina · 3 pilar
POST feliz{ lines:[{brand:"Dia Light",tone:"9.3",grams:20}], oxidant:20, minutes:25 }
Empty Hoyborrar sittings del día (solo en entorno de test)

4. Regresión de diversidad / craft

CheckFalla si
Home es bentoHay hero 2-col + 3 cards de marca (AP-12) o wizard (ZAGUÁN) o lookbook (MARGEN)
Shell es S-BOTTOMHay masthead, rail, chrome de pasos 1—2—3 o command search como nav primaria
Copy honestoSuccess dice «aplicado» o ficha dice «cita» o editor dice «receta de la caja» o hay «pagar»
PaletaSe cuelan óxido ZAGUÁN / cadmio MARGEN / musgo CESTA / ámbar spa
TipoInstrument Serif / Sora / Cormorant / Public Sans / Newsreader / Outfit / Spectral / Manrope / Zilla / Mulish / Lora / Karla
RutasAparecen /cita, /agenda, /cobrar, /comparar, /mesa, /visita
GroundPágina en lino #F7F5FA en vez de yeso #E6E4ED

5. Hipótesis (no se «prueban» en la demo)

H1, H2, H3 requieren uso real. El QA verifica que existen las superficies que permitirían medirlas (editor, Hoy con 3 sillas, disclaimer de cita, ausencia de signup clienta, 401 público).

6. Criterios de salida QA v1

  • Smoke §1 en verde.
  • S2, S6, S8, S10, S17, S21, S25 en verde.
  • Cero blockers de copy deshonesto (cita / aplicar / receta de caja / cobrar / agendar).
  • Sin .env en el repo.

7. Fuera de este plan

Playwright e2e completo, carga, fuzzing, auditoría de un salón real.

14-dev-handoff.md

14 — Handoff desarrollo — MATIZ

1. Arranque

cd /Users/cristian/orca/matiz-app

# apps independientes (npm --prefix o pnpm --filter)
# apps/api/.env
#   DATABASE_URL=  # Neon mute-wave-74707210
#   JWT_SECRET=
#   PORT=3019

# migrate / ensureSchema + seed
# api → http://localhost:3019
# web → http://localhost:4200

Demo: nora@matiz.salon / password123.

2. Paridad Paper

Debe verse en AngularArtboard
Tokens chapel + Libre Baskerville / Nunito SansUI-00
Login COLORISTAUI-01
Hoy bento 3 sillas + última + avisoUI-02
FichasUI-03
Ficha + historialUI-04
Editor móvilUI-05
Editor desktop 1440UI-06
Success «guardado ≠ aplicado»UI-07
EmptyUI-08
ErrorUI-09
Loading skeletonsUI-10
CuentaUI-11

No clonar ZAGUÁN (wizard + comparador), MARGEN (lookbook + masthead), CESTA (tabla + rail), RONDA (feed + dock) ni CANTERA (parte + wizard de oficio).

Paper se construye en paralelo; los nombres de artboard son el inventario planificado (doc 00).

3. Contratos

TemaContrato
Login{ accessToken, user } camelCase (no access_token)
MeGET /api/me Bearer
HoyGET /api/hoy{ day, sittings, lastFormula, oxidantWarning }
ClientsGET /api/clients · GET /api/clients/:slug
Alta fórmulaPOST /api/clients/:slug/formulas · body lines, oxidant, minutes, notes
FórmulaGET / PATCH /api/formulas/:id
HealthGET /api/health
Status enumSAVED
Oxidante enum10 | 20 | 30
RoleCOLORISTA (no AGENT, no EDITOR)

Campos JSON en camelCase hacia el web aunque SQL sea snake_case. [DECISIÓN]

4. Mapa de rutas web

PathGuard clientePágina
/loginno (si hay token → /)Login
/authHoy
/fichasauthLista
/fichas/:slugauthFicha
/fichas/:slug/formulaauthEditor
/ok/:idauthSuccess
/cuentaauthCuenta

Guard: si no hay token, /login?next=. La API es la autoridad.

[COMPROBADO] Solo estas rutas. No implementar /cita, /agenda, /cobrar, /comparar, /mesa.

5. Assets

Copiar desde el case:

2026-08-19-matiz/assets/hero.jpg
2026-08-19-matiz/assets/clients/marisol.jpg
2026-08-19-matiz/assets/clients/lucia.jpg
2026-08-19-matiz/assets/clients/amina.jpg
2026-08-19-matiz/assets/clients/pilar.jpg
2026-08-19-matiz/assets/clients/diego.jpg
  → apps/web/public/assets/clients/   (hero a public/assets/)

cover del seed = filename (marisol.jpg, …).

Quitar leftover public/assets/listings/* y páginas wizard / comparar / mesa si siguen en el repo.

6. DoD implementación L2

CheckOK
API sirve :3019
Seed 1 COLORISTA + 5 clients + 8 formulas + 3 sittings
Smoke health + login + hoy + clients
JWT protege hoy / clients / POST / PATCH
Web login + Hoy + fichas + ficha + editor + ok + cuenta
Microcopy ×5
Tokens Tailwind = paleta brief
Home es bento (no leftover wizard / comparador)
Bottom tabs Hoy · Fichas · Cuenta
ng build o equivalente OK
README del repo app con credencial

7. No hacer

ProhibidoMotivo
npm workspacesAP-06
Commit de .envSecreto
Prometer cita, aplicación o cobroMicrocopy duro / AP-09
Estado APPLIED / SEATED / PUBLISHED / DRAFTVocabulario de otros días / mentira de aplicado
Role AGENT / EDITOREso es ZAGUÁN / MARGEN
Search bar o wizard como homeRompe H-BENTO
Hero 2-col + 3 cardsAP-12
Lookbook + mastheadMARGEN
Comparador 3 colsZAGUÁN
Parte de obraCANTERA
image_gen como captura de productoD-P0-09
Body POST con zona / maxRent / listingLeftover

8. Contactos de diseño (artefactos)

ArtefactoPath
Briefux-projects/2026-08-19-matiz/docs/00-day-brief.md
Paperhttps://app.paper.design/file/01M0CDJZPXVWCVYZC5Z5V9ZRQE
Datosdocs/05-data-model.md
Flujosdocs/04-user-flows.md
IXdocs/16-interaction-specs.md
Builddocs/20-implementation.md

15-roadmap.md

15 — Roadmap — MATIZ

Este documento no es un cajón de trabajo L2 a medias.
El vertical slice L2 del brief está especificado como done-scope (docs 01, 18, 19).
Aquí solo viven ideas de L+1 / L3 y pulido posterior.

1. Hecho en L2 (esta ejecución documental)

EntregaEvidencia
Definición producto + ternadocs 00-brief, 01
Personas Nora / Inma / Marisol, JTBD, journeydocs 02
IA auth-first + chrome S-BOTTOMdocs 03
Flujos F-EDIT + Hoy + cuentadocs 04
Modelo users / clients / formulas / sittings + seeddocs 05
Stack Angular/Nest/Neon/JWT :3019docs 06, 20
Dirección chapel + DSdocs 07, 08
Copy Fórmula ≠ receta · Ficha ≠ cita · Guardado ≠ aplicado · no agenda · no cobradocs 09
Paper ref 12 UX + 12 UIdocs 00-paper-reference
Suite 00–20 + README + executivecase folder

2. Implementación L2 (misma complejidad, código)

Contrato en docs 14 y 20. No es deuda silenciosa del case documental: es el build del repo matiz-app. El alcance L2 ya está cerrado en spec; el código alinea leftover (listings / compare / mesa) al contrato.

Eso no se reabre aquí como «próximo trimestre de producto».

3. L+1 (misma familia, subida de pulido)

ÍtemNotas
Alta de ficha nueva + cover allowlistSigue sin cuenta de clienta
PATCH desde historial (?id=)Status sigue SAVED
Gesto «puesta» (no resultado)Copy: no es el color en el pelo
Rol DUEÑA solo lecturaS4 se relaja con brief corto
Rate limit loginAnti stuffing
Playwright S1–S25CI
API URL por environmentDeploy
prefers-reduced-motion verificadoa11y
PWA install / manifestS2
Borrador local del editorsessionStorage; no es SAVED

Estas son subidas de pulido, no parches del L2 documental.

[DECISIÓN] No dejar backlog del vertical slice como «mañana». Lo de arriba es L+1.

4. L3 (requiere brief nuevo de diversidad)

  • Multi-colorista / turnos / comisiones
  • Agenda / slots / F-BOOK
  • TPV / ticket / pasarela
  • Cuenta de clienta y «mi cita»
  • Recetario oficial de marca
  • Foto del resultado como UGC
  • Historial clínico / alergias
  • Cadena de salones

5. Explícitamente fuera (no backlog disfrazado)

ÍtemPor qué
Reserva / slot / confirmación de citaH2 · ≠ F-BOOK
Lookbook editorial / agenda de looks≠ MARGEN
Tabla de necesidades / compromiso≠ CESTA
Parte de obra / wizard de oficio≠ CANTERA
Wizard + comparador de fincas≠ ZAGUÁN
App nativaFuera de stack daily
Hero 2-col + 3 cardsAP-12

6. Orden de ataque si hay continuidad de código

  1. Contratos 05–06 + seed 05 + smoke 13 (alinear leftover)
  2. Web paridad UI-00…11
  3. Alta de ficha + PATCH historial (L+1)
  4. Rate limit si hay tráfico
  5. Evaluar brief L3 (no parche silencioso)

16-interaction-specs.md

16 — Especificación de interacción — MATIZ

1. Convenciones

TokenValor
Duración corta120–160ms
Easingease-out
Reduced motion0ms
Target≥44×44
Pendingcontrol disabled + aria-busy

Loading (skeleton de UI-02 / UI-03) y editor desktop (UI-06) viven en este doc junto a los gestos; el inventario Paper ya reserva UI-06 y UI-10. [DECISIÓN]


2. Bottom tabs (S-BOTTOM)

GestoResultado
Tap Hoy/ · aria-current="page"
Tap Fichas/fichas
Tap Cuenta/cuenta
Tap tab actualScroll to top (Should); no recarga dura
TecladoTab al landmark; Enter navega
Scroll de contenidoTabs fijos; no hide-on-scroll v1

Estado activo: label amatista + filete 2px amatista arriba.
Estado default: peltre.

390: 56px + env(safe-area-inset-bottom).
Desktop: 64px inferior centrado o full-bleed; no se convierte en top-nav.

Login no muestra tabs.


3. Login (UI-01)

GestoResultado
SubmitPOST login; pending en botón
200Guarda token + user; navega next sanitizado o /
401Texto bajo el form; password no se limpia
EnterSubmit
Primera cargaForm visible; no skeleton de marca

next permitido: paths que empiezan por / y no // ni http. Allowlist: /, /fichas, /fichas/:slug, /fichas/:slug/formula, /ok/:id, /cuenta.


4. Hoy bento (UI-02) + loading

GestoResultado
Primera cargaLoading: 3 tiles lino + 1 barra peltre; aria-busy
200 con 3tiles + última fórmula + aviso si aplica
200 con 0UI-08 empty · sin CTA Agendar
Error 5xx / redUI-09 + Reintentar (repite GET)
Tap silla/fichas/:slug (tile completa clicable)
Tap última fórmula/fichas/:slug de esa fórmula
Hover desktopRule 1px pizarra; no elevación
Pull-to-refreshNo en v1

AC: nombre, silla y último matiz visibles en cada tile sin hover. 3 sillas en el fold a 390 (AP-21). Cero botones Agendar / Cobrar.

Aviso 30 vol: visible bajo las tiles; no es dismissible Must (Could: «entendido» solo oculta en sesión).


5. Fichas (UI-03)

GestoResultado
Tap fila/fichas/:slug
EmptyTitular + 1 línea
ErrorBanner + Reintentar
Loading5 filas skeleton

Sin swipe-to-archive. Sin búsqueda Must (5 filas).


6. Ficha (UI-04)

GestoResultado
Tap ← Fichas/fichas
Tap Nueva fórmula/fichas/:slug/formula
Scroll historialLectura normal; sin scroll-jacking
Tap una fórmula vieja[DECISIÓN] no navega en Must; se lee in situ. Could: ?id= al editor
FotoNo lightbox v1

Sin «llamar ahora». Sin WhatsApp click-to-chat. Sin «agendar».


7. Editor móvil (UI-05) · F-EDIT

CampoInteracción
brandtext
tonetext (8ch)
gramsnumber > 0
oxidant3 chips radio
minutesnumber 1–90
notestextarea
GestoResultado
Añadir líneaNueva fila al final; foco al brand
Quitar líneaSplice; disabled si queda 1
Chip oxidantearia-pressed único
GuardarPOST; pending; no navegar si inválido
Atrás/fichas/:slug; descarta sin confirm Must
Submit inválidoFoco al primer campo error; no POST
Error redBotón Reintentar; no limpia campos
Doble tap GuardarIgnorado mientras pending

Submit deshabilitado si 0 líneas, grams ≤ 0, oxidant vacío o minutes vacío.

[DECISIÓN] Un solo botón primario: Guardar fórmula. No existe «Aplicar» ni «Guardar y aplicar».

390: CTA full-width h-44 por encima de los tabs (padding-bottom). Teclado iOS no debe tapar el input activo: scroll-margin en campos.


8. Editor desktop (UI-06)

Misma ruta, viewport ≥1200.

GestoResultado
LayoutEditor 7 / staff 5
StaffRetrato, nombre, matiz, disclaimer; no es otra navegación
GuardarMismo POST; CTA no full-width (min 200px)

No hay atajos de teclado extra Must. Cmd+Enter Could L+1.


9. Success (UI-07)

GestoResultado
CTA primarioVer la ficha /fichas/:slug
CTA secundarioVolver a Hoy /
CopyVisible «Guardado no es aplicado»

Sin confetti. Sin «¡ya está en el pelo!». Sin «cobrar».


10. Empty (UI-08) · Error (UI-09) · Loading (UI-10)

EstadoInteracción
Empty HoyTitular + 1 línea. Sin CTA de agenda
Empty FichasTitular + 1 línea
Empty historialTitular + CTA Nueva fórmula
ErrorBanner + Reintentar; foco al banner; no pierde form
Loading Hoy3 tiles + 1 barra; no spinner centrado de marca
Loading Fichas5 filas lino
Loading editor (GET ficha)2 líneas skeleton

11. Cuenta (UI-11)

GestoResultado
Sin tokenRedirect login
Tap SalirLimpia storage → /login
Error 401 mid-sessionRedirect login

No hay edición de perfil Must. No hay «cambiar password».


12. Motion

SuperficieSpec
Cambio de tab / rutaFade 160ms (off si reduced-motion)
Tiles HoyFade 160ms, stagger ≤30ms (off si reduced-motion)
Badge SAVEDCrossfade 120ms
TabsSin hide-on-scroll v1
SkeletonsEstáticos o pulso 1.2s opacity 0.6–1; off si reduced-motion

Prohibido: scroll-jacking, page transitions de 400ms, parallax de vidriera, like animation, gotas de tinte, mechas que crecen.


13. Criterios de aceptación de interacción

  1. Una COLORISTA en 390px llega de Hoy a guardar una fórmula en ≤ 4 taps (silla → nueva → … → Guardar).
  2. Ningún submit permite doble POST.
  3. Los tabs no cubren el H1 ni el CTA Guardar (spacer).
  4. Reduced-motion elimina stagger y slide.
  5. La home no es hero 2-col (AP-12).
  6. En 390 las 3 sillas son visibles sin scroll de «marca» (AP-21).
  7. / y /fichas/:slug no presentan ningún control de cita ni cobro.
  8. El loading de Hoy es skeleton bento, no un logo.
  9. El editor muestra «fórmula, no receta» y «guardar no es aplicar» sin scroll en desktop.
  10. Manos húmedas: cero gestos que exijan hover o swipe horizontal como único camino.

17-prototype-map.md

17 — Mapa de prototipo — MATIZ

Paper no es clicable vía MCP. El prototipo vivo es la app Angular.
Los artboards de Paper se construyen en paralelo; esta tabla es el contrato de paridad.

RutaPaperRol
/loginUI-01COLORISTA
/UI-02, UI-08, UI-09, UI-10COLORISTA
/fichasUI-03 (+ empty/error)COLORISTA
/fichas/:slugUI-04COLORISTA
/fichas/:slug/formulaUI-05 (390) · UI-06 (1440)COLORISTA
/ok/:idUI-07COLORISTA
/cuentaUI-11COLORISTA

Proceso: UX-00…UX-11. Tokens: UI-00.

Empty, error y loading se prototipan como estados / variantes de UI-02 / UI-03 además de sus boards propios. Ver doc 16.

Recorrido demo (guión)

  1. Abrir / sin token — redirect /login. Leer eslogan La fórmula, no la cita.
  2. Entrar nora@matiz.salon / password123.
  3. / — Hoy bento: Silla 1 Marisol honey · Silla 2 Amina copper · Silla 3 Pilar silver. Última fórmula Diego 6.1+7.1 20 vol. Aviso: Amina trae 30 vol.
  4. Tocar Marisol Vega — ficha, historial de junio, «La ficha no es una cita.»
  5. Nueva fórmula — líneas Dia Light 9.3 20 g + 8.3 20 g, 20 vol, 25 min. Leer «Esto es una fórmula, no la receta de la caja.» y «Guardar no es aplicar.»
  6. Guardar fórmula/ok/:id — «Guardado no es aplicado.»
  7. Volver a la ficha: la nueva está SAVED.
  8. Tab Fichas — 5 filas. Abrir Diego (no está hoy).
  9. Tab Cuenta — Salón Amparo, Calle Amparo 22, COLORISTA. Leer «MATIZ no agenda. MATIZ no cobra.»
  10. Comprobar que no hay botón Agendar, Cobrar ni Aplicar.
  11. Salir → /login.

Estados en el mismo prototipo

EstadoCómo provocarlo
Empty Hoy0 filas en sittings del día (entorno de test)
Empty Fichaslista vacía autenticada
Empty historialficha nueva (L+1) o mock
ErrorAPI caída / 5xx
Loadingthrottle de red en GET /hoy
404 ficha/fichas/no-existe con token
401/ sin token → login
Editor 390390 × 844 en /fichas/marisol/formula
Editor 14401440 × 900 misma ruta (UI-06)
Aviso 30 volseed Amina silla 2

Fuera del prototipo clicable

  • Paper artboards son estáticos.
  • WhatsApp de Inma (hora) / caja física (ticket).
  • El gesto de aplicar el color en el lavacabezas.
  • Reserva de cita (no existe).
  • TPV / cobro (no existe).
  • Cuenta de clienta.
  • Recetario oficial de marca.

18-completeness-audit.md

18 — Auditoría de completitud — MATIZ (2026-08-19)

1. Alcance auditado

Vertical slice L2: PWA auth-first de cuaderno de fórmulas + fichas + editor + JWT COLORISTA, con docs, referencia Paper, contratos Angular + Nest + Neon.

Este encargo cubre la suite documental. Paper hi-fi y el repo matiz-app pueden existir en paralelo; la auditoría marca lo que esta suite cierra. El day brief y el paper-reference no se han sobrescrito.

2. Checklist CRON / ALS-2

RequisitoEstadoEvidencia
Diversidad sector/tipo/nivelOKBelleza oficio L2; no inmobiliaria; no galería; no tabla; no parte de obra
Terna S/H/F 0 códigos = N−1 · ≥2 vs N−2OKS-BOTTOM · H-BENTO · F-EDIT vs ZAGUÁN S-WIZ · H-WIZARD · F-CMP
Day brief + anti-patronesOKdocs/00-day-brief.md (no sobrescrito)
Paper ≥12 UX + ≥10 UIOKUX-count 12 · UI-count 12 · file 01M0CDJZPXVWCVYZC5Z5V9ZRQE
Docs 00–20OKsuite en docs/ + executive + README
JWT COLORISTAOKRole COLORISTA · D-P1-05 · auth-first
API + seed + NeonOKcontratos mute-wave-74707210, port 3019
Web tokensOKLibre Baskerville + Nunito Sans · paleta chapel
Bento-first (no AP-12 / AP-21)OKUI-02 / docs 03, 07, 08
Hipótesis no fake fieldOKetiquetas en doc 02
Copy ×5 irrenunciableOKdocs 01, 09, 16
Ground G-CHAPELOK#E6E4ED · no G-CONCRETE / G-GALLERY / G-MOSS

3. Cobertura funcional

Feature briefSpecUI PaperAPIDocs
Login COLORISTAUI-01POST login04, 06
Hoy bento 3 sillas + última + avisoUI-02GET hoy03, 04, 16
FichasUI-03GET clients04, 05
Ficha + historialUI-04GET :slug05, 09
Editor móvilUI-05POST formulas04, 16
Editor desktopUI-06misma API04, 16
SuccessUI-07GET formula04, 09
EmptyUI-08200 []16
ErrorUI-095xx/red16
LoadingUI-10pending16, 17
CuentaUI-11GET me04
Design systemUI-0008
Seed 1 + 5 + 8 + 3seed05

4. Cobertura Paper (literales)

IDNombreEn 00-paper-reference
UX-00Cover
UX-01Stakeholders
UX-02Personas
UX-03JTBD
UX-04Stories
UX-05Journey
UX-06Blueprint
UX-07Site map
UX-08Flujos
UX-09Datos+permisos
UX-10Métricas
UX-11Research
UI-00Tokens
UI-01Login
UI-02Hoy bento
UI-03Fichas
UI-04Ficha
UI-05Editor móvil
UI-06Editor desktop
UI-07Success
UI-08Empty
UI-09Error
UI-10Loading
UI-11Cuenta

[COMPROBADO] UX-count: 12 y UI-count: 12 literales para el gate check-paper-reference.mjs. Paper hi-fi en construcción paralela; inventario planificado escrito.

5. Rúbrica de calidad (auto SCORE orientativo · docs)

EjeScore 1–5Comentario
Diversidad5PWA oficio vs wizard ZAGUÁN, galería MARGEN y tabla CESTA; L2 no L1
Craft visual (spec)4–5Chapel + Libre/Nunito + fotos de ficha documentadas
Densidad UX docs5Suite L2: tablas, AC, etiquetas S/H/D
Completitud código L2App fuera o en paralelo; leftover scaffold anotado
Authz5Contrato: guard en todo salvo login; 401 público
Verdad investigación5Sin entrevistas ni stats de campo falsas

6. Huecos aceptados (no regresiones de cierre documental)

HuecoClasificación
Agenda / TPV / cuenta clienta / recetarioFuera L2
e2e automatizadoL+1
Alta de ficha nuevaL+1
Gesto APPLIEDFuera o L+1 con copy; no Must
Leftover listings / compare / mesa en repoBuild; contrato de esta suite gana
Paper/app no necesariamente tocados aquíEncargo = documentación

7. Veredicto

COMPLETO para entrega documental del caso 2026-08-19.
La suite define producto L2 usable (tablas, AC, riesgos, contratos) sin presentar investigación de campo como hecho.
Cualquier ampliación agenda / TPV / clienta-login / recetario / lookbook requiere nuevo brief de diversidad, no parche silencioso.

19-backlog-completo.md

19 — Backlog completo — MATIZ

Inventario de ítems. Los del alcance L2 documental del día están Done.
El resto es opcional / siguiente nivel, no deuda oculta del cierre.

1. Done — L2 case documental (2026-08-19)

IDÍtemCapa
D01Definición producto MATIZ + esloganDocs
D02Day brief diversidad belleza L2Docs (preexistente, no pisado)
D03Terna R-HY · S-BOTTOM · H-BENTO · F-EDITDocs
D04Personas Nora Beltrán / Inma Soto / Marisol VegaDocs + Paper ref
D05JTBD + stories MustDocs
D06IA auth-first + chrome S-BOTTOMDocs
D07Flujos F0–F9 + ACDocs
D08Modelo users / clients / formulas / sittingsDocs
D09Contratos API :3019Docs
D10Auth JWT COLORISTADocs
D11Status SAVED + 401 públicoDocs
D12Editor líneas + oxidante 10/20/30 + minutosDocs
D13Microcopy ×5 (receta / cita / aplicado / agenda / cobra)Docs
D14North star sillas con fórmula el mismo díaDocs
D15Tokens chapel + Libre Baskerville / Nunito SansDocs
D16Paper UX-00…11 + UI-00…11Docs
D17Suite docs 00–20 + README + executiveDocs
D18Seed 1 COLORISTA + 5 fichas + 8 fórmulas + 3 sillasDocs
D19Assets listados (hero + 5 clientas)README
D20QA smoke + S1–S25Docs
D21Neon mute-wave-74707210Docs
D22Hoy bento + editor 390/1440 especificadosDocs
D23Ground G-CHAPEL #E6E4EDDocs
D24Rutas reales solo las 7 del encargoDocs

Los must-have del brief (login, Hoy, fichas, ficha, editor, success, empty/error/loading, cuenta, ≥12 UX + ≥10 UI) están dentro de D01–D24, no como leftover.

2. Backlog L2 implementación (repo matiz-app)

Alineación de código al contrato ya cerrado. No es alcance documental pendiente.

IDÍtemPrioridadNotas
I01Alinear apps/api + apps/web al contrato MATIZP0quitar leftover listings/compare/mesa
I02Schema users / clients / formulas / sittingsP0
I03Seed Nora + 5 clients + 8 formulas + 3 sittingsP0
I04GET hoy / clients / clients/:slug / formulas/:idP0401 sin token
I05POST clients/:slug/formulas + PATCH formulas/:idP0status SAVED
I06POST login JWT + GET meP0nora@matiz.salon
I07GET healthP0
I08Auth guard Angular en todo salvo loginP0
I09Hoy / fichas / ficha / editor / ok / cuenta / loginP0
I10Tokens Tailwind + fotos + bottom tabsP0
I11Smoke curl + buildP0D-P1-06
I12Puerto API 3019P0

3. Backlog L+1 (polish)

IDÍtemPrioridadNotas
B01Alta de ficha nuevaP1sin cuenta clienta
B02PATCH desde historialP2status SAVED
B03Rate limit loginP1anti stuffing
B04Playwright smokeP1CI
B05Skeleton UI-10 en códigoP2
B06PWA manifest / installP2S2
B07Borrador local del editorP3no es SAVED
B08API URL por environmentP1deploy
B09Página 404 amigableP3
B10Sessions invalidablesP2
B11Gesto «puesta» con copyP2no fingir resultado
B12Rol DUEÑA solo lecturaP3brief corto

4. Backlog L3 (requiere brief nuevo)

IDÍtemDependencia
C01Multi-colorista / turnosusers + membership
C02Cuenta CLIENTA + «mi cita»authz · rompe H3
C03Calendario de sillasH2 se rompe · brief nuevo
C04TPV / ticketmicrocopy se rompe
C05Recetario oficial de marcafórmula ≠ receta se rompe
C06Cadena de salonesS1 se rompe
C07Historial clínico / alergiasotro producto

5. Backlog explícitamente fuera

IDÍtem
E01Reserva / slot / confirmación de cita
E02Lookbook editorial / agenda de looks
E03Tabla de necesidades / compromiso de caja
E04Parte de obra / wizard de oficio
E05Wizard + comparador de fincas
E06App nativa
E07Hero 2-col + 3 cards de marca

6. Explicitamente no-backlog

IdeaRazón
Hero 2-col + 3 cardsAP-12 / rompe H-BENTO
Masthead de periódicoS-TOP de MARGEN
Chrome de pasos 1—2—3S-WIZ de ZAGUÁN
Sidebar CRMS-SIDE
Rail de iconosS-RAIL de CESTA
Form «pide cita»H2
«Pagar ahora»MATIZ no cobra
Status APPLIED como hechoGuardado ≠ aplicado

7. Orden de ataque recomendado (continuidad de código)

  1. I01–I12 (vertical slice runnable, sin leftover)
  2. B03 + B04 + B08
  3. B01 + B02
  4. B06 + B11 si hay campo
  5. Evaluar brief L3 — no parche silencioso

8. Trazabilidad

OrigenÍtems
Day brief must-haveD01–D24, I01–I12
Doc 15 L+1B01–B12
Doc 15 L3 / fueraC01–C07, E01–E07

20-implementation.md

20 — Implementación — MATIZ

1. Resumen ejecutivo técnico

CampoValor
App path/Users/cristian/orca/matiz-app
APINestJS · puerto 3019 · prefijo /api
WebAngular standalone · puerto 4200
Packageapps independientes (npm --prefix o pnpm --filter)
DBNeon PostgreSQL · project mute-wave-74707210
Tablasusers, clients, formulas, sittings (+ sessions Should)
AuthJWT Bearer · role COLORISTA
DominioUser, Client, Formula, Sitting
Fecha2026-08-19
GitHubhttps://github.com/Criscode2022/matiz-app

Este documento es la especificación de build alineada al case. No sustituye al código: si el repo diverge, gana el contrato de docs 05 + este archivo tras actualizar ambos.

[COMPROBADO] Path, puerto, Neon, Paper y GitHub salen del encargo.
[COMPROBADO] El repo existe; contiene leftover de ZAGUÁN (listings, compare, mesa, páginas wizard / finca). El contrato de esta suite es MATIZ.

2. Cómo arrancar

cd /Users/cristian/orca/matiz-app
# instalar dependencias por app (no npm workspaces)

# apps/api/.env
#   DATABASE_URL=   # Neon mute-wave-74707210
#   JWT_SECRET=
#   PORT=3019

# schema + seed
# api → http://localhost:3019
# web → http://localhost:4200

Credenciales

RolEmailPassword
COLORISTAnora@matiz.salonpassword123

3. Schema

Al boot o vía migrate:

  1. CREATE TABLE / migrate de users, clients, formulas, sittings (doc 05). Opcional sessions.
  2. Índices: clients(slug), formulas(client_id, created_at DESC), sittings(day, chair), users(email). Unique (day, chair) y (day, client_id).
  3. Seed idempotente:
    • upsert COLORISTA nora@matiz.salon (bcrypt de password123)
    • upsert clients por slug
    • upsert formulas por id
    • upsert sittings del 2026-08-19 (o del CURRENT_DATE Europe/Madrid si se quiere que «hoy» sea siempre el día de demo)

[DECISIÓN] Para la demo diaria, el seed de sittings puede usar CURRENT_DATE en Europe/Madrid en lugar de la fecha fija 2026-08-19, de modo que Hoy nunca aparezca vacío al reabrir el case. Documentar en README del repo. Las fórmulas conservan fechas históricas del seed.

No borrar fórmulas nuevas de Nora en cada boot si ya existen filas distintas al seed.

Tablas (recordatorio)

TablaContenido
usersCOLORISTA Nora
clients5 fichas
formulas8 SAVED
sittings3 sillas de hoy
sessionsShould: sid + token_hash + expires_at

4. Módulos API a implementar

Auth

  • POST /api/auth/login
  • GET /api/me
  • Valida email/password; compara bcrypt; emite JWT con sub, email, role.
  • Guard JWT + role === COLORISTA protege todo salvo login y health.

Oficio

MétodoRutaAuthNotas
GET/api/healthNo{ ok: true }
GET/api/hoyJWTsittings del día + lastFormula + oxidantWarning
GET/api/clientsJWTcards + lastFormulaAt
GET/api/clients/:slugJWTficha + formulas[]
POST/api/clients/:slug/formulasJWTcrea SAVED
GET/api/formulas/:idJWT
PATCH/api/formulas/:idJWTlines / oxidant / minutes / notes · no status

Hoy (referencia de implementación)

day = DATE Europe/Madrid
sittings = WHERE day = today ORDER BY chair ASC
  + join client
  + lastFormula = latest formula of that client
lastFormula = latest formula globally
oxidantWarning.active = any sitting.lastFormula.oxidant === 30

[DECISIÓN] Timezone Europe/Madrid para day de sittings y para labels de UI. JSON transporta ISO UTC. Gramos y minutos son números.

Guardar no toca sittings ni genera cobro.

5. Frontend a implementar

PáginaRutaResponsabilidad
LoginPage/loginform → login → Hoy
HoyPage/GET hoy, bento, empty/loading/error
FichasPage/fichasGET clients
FichaPage/fichas/:slugGET slug, historial, CTA
FormulaPage/fichas/:slug/formulaeditor 390 + 1440, POST
OkPage/ok/:idGET formula, «guardado ≠ aplicado»
CuentaPage/cuentaGET me, salir

Shell autenticada: BottomTabsComponent. Login: sin tabs.
ApiService centraliza base URL http://localhost:3019/api, token storage (matiz_token, matiz_user), métodos tipados (Client, Formula, HoyResponse, User).

AuthGuard en todas las rutas salvo /login. Interceptor Authorization: Bearer.

6. Decisiones de implementación

DecisiónRazón
Puerto API 3019Evitar colisión con ZAGUÁN 3018
Tablas clients / formulas / sittingsDominio de oficio, no leftover listings
Role COLORISTAEncargo; no AGENT
Soft auth en páginasSimple; API es autoridad
Auth-first webEncargo; cero cara pública
JSONB lines1–4 líneas; no tabla hija
Status solo SAVEDGuardado ≠ aplicado
Home no usa hero 2-colAP-12 / H-BENTO
Sittings seed = hoy civilDemo usable cualquier día
JSON camelCaseConvenio web serie daily
PATCH :id sin statusContrato del encargo
Success /ok/:idBoard UI-07
Cover allowlistEvitar URL arbitraria
Apps independientesAP-06

7. Variables de entorno

VariableServicioDescripción
DATABASE_URLAPINeon mute-wave-74707210
JWT_SECRETAPIFirma tokens
PORTAPIopcional, 3019
JWT_EXPIRESAPIopcional, default 12h

Web: URL de API en ApiService (default localhost:3019).

8. Smoke de implementación (mínimo)

  1. Schema OK (tablas existen).
  2. GET /api/health 200.
  3. POST /api/auth/login 200 + accessToken + role=COLORISTA.
  4. GET /api/hoy Bearer: 3 sittings, chairs 1–2–3 = marisol / amina / pilar.
  5. GET /api/hoy sin token 401.
  6. GET /api/clients incluye 5 slugs.
  7. GET /api/clients/marisol historial ≥ 2 fórmulas.
  8. POST /api/clients/marisol/formulas 201 status=SAVED.
  9. Web: bento dominante, tabs, disclaimers ×5, fotos 1:1.

9. Estructura de ficheros clave (objetivo)

matiz-app/
├── package.json
├── apps/api/
│   ├── src/
│   │   ├── main.ts
│   │   ├── app.module.ts
│   │   ├── auth/
│   │   ├── clients/
│   │   ├── formulas/
│   │   ├── hoy/
│   │   └── health.controller.ts
│   └── .env.example
├── apps/web/
│   ├── tailwind.config.js
│   └── src/app/
│       ├── app.routes.ts
│       ├── core/api.service.ts
│       ├── shell/bottom-tabs.component.ts
│       └── pages/
│           ├── login/
│           ├── hoy/
│           ├── fichas/
│           ├── ficha/
│           ├── formula/
│           ├── ok/
│           └── cuenta/
└── README.md

Nombres de fichero orientativos; el contrato es de rutas y tablas, no de filenames exactos.

Quitar módulos leftover listings/, compare/, mesa/ y páginas wizard/, comparar/, finca/.

10. Seed — checklist de implementación

#Check
1User Nora Beltrán / nora@matiz.salon / password bcrypt de password123
25 clients con slugs y covers de doc 05
38 formulas SAVED con ids frm_*
43 sittings hoy: chair 1 marisol, 2 amina, 3 pilar
5Amina última fórmula oxidant 30 (aviso Hoy)
6lastFormula global = diego 2026-08-01
7Idempotencia: upsert por email / slug / formula id

11. Alineación case ↔ app

DocEvidencia esperada en código
05 data modelcolumnas y enums
03–04 IA/flowsroutes + controllers
08 DStailwind colors + fontFamily Libre/Nunito
09 contentstrings en templates (microcopy ×5)
00 paperURLs en README case
11 securityGuard en todo salvo login; 401 público

12. Notas de cierre técnico

  • El case documental L2 está especificado (docs + Paper ref + contratos).
  • Must-have de producto del brief están escritos con AC.
  • Mejoras (alta de ficha, e2e, rate limit, PWA install) viven en backlog L+1, no como deuda silenciosa.
  • Si el repo aún muestra listings / compare / mesa / wizard de zona, no está alineado: I01 es el primer ítem de build.