Files
impactflow_discovery/app/static/style.css
T
Joel Salmon b4d8d17aed Phase 2: AI coach reflection loop
Add the mirror-not-compass reflection layer between profile generation and
affirmation. The coach reflects the person's profile back, and only when they
explicitly correct or add something does it propose revisions in their own
direction — never prescribing goals.

- ReflectionCoach service (app/services/reflector.py): Anthropic-backed,
  returns {message, revisions, revision_note}; revisions filtered to the seven
  editable prose fields (never triad/type); one-retry JSON handling.
- Endpoints (owner-scoped, 409 when locked): POST /discovery/profile/me/reflect
  (opener + turns, applies revisions), GET .../reflection (dialogue),
  GET .../revisions (iteration history). complete records an 'extraction'
  revision; PATCH records 'manual_edit'.
- Models + migration 004: reflection_message (coach/person turns) and
  profile_revision (snapshots: extraction | reflection | manual_edit) —
  captures edits and iterations rather than overwriting.
- Frontend: reflect.html chat (coach/person bubbles, live profile summary that
  refreshes on revision, affirm); linked from profile.html.
- Affirmation remains the existing confirm/lock.

Also refresh README for Phase 2 and for the HTTPS deployment
(https://impactflow.teamci.org:8011, OAUTH_REDIRECT_URI + COOKIE_SECURE notes).

Tests: 50 passing (added reflector unit tests and reflection endpoint tests;
run in-container).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 20:58:45 -05:00

401 lines
6.6 KiB
CSS

/* ImpactFlow Self-Discovery — shared styles
Palette: navy #0d1b2a, gold #c9a84c, cream #f8f5ef */
:root {
--navy: #0d1b2a;
--gold: #c9a84c;
--cream: #f8f5ef;
--navy-soft: #1c3049;
--shadow: rgba(13, 27, 42, 0.12);
--green: #2e7d32;
--yellow: #c9a84c;
--red: #c0392b;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
background: var(--cream);
color: var(--navy);
font-family: "DM Sans", system-ui, sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
.wrap {
max-width: 760px;
margin: 0 auto;
padding: 48px 24px 80px;
}
/* ---------- Discovery flow ---------- */
.brand {
font-family: "Playfair Display", "DM Sans", serif;
font-size: 1.1rem;
letter-spacing: 0.04em;
color: var(--gold);
text-transform: uppercase;
margin-bottom: 40px;
}
.progress {
color: var(--gold);
font-weight: 600;
letter-spacing: 0.08em;
font-size: 0.85rem;
text-transform: uppercase;
margin-bottom: 14px;
}
.prompt-title {
font-family: "Playfair Display", "DM Sans", serif;
font-size: 1.35rem;
color: var(--gold);
margin: 0 0 10px;
}
.prompt-text {
font-family: "Playfair Display", "DM Sans", serif;
font-size: 1.7rem;
font-weight: 500;
color: var(--navy);
margin: 0 0 28px;
line-height: 1.35;
}
textarea {
width: 100%;
min-height: 240px;
padding: 20px;
border: 1px solid rgba(13, 27, 42, 0.18);
border-radius: 12px;
background: #fff;
color: var(--navy);
font-family: "DM Sans", system-ui, sans-serif;
font-size: 1.05rem;
line-height: 1.6;
resize: vertical;
}
textarea:focus {
outline: none;
border-color: var(--gold);
box-shadow: 0 0 0 3px rgba(201, 168, 76, 0.25);
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 28px;
gap: 16px;
}
button {
font-family: "DM Sans", system-ui, sans-serif;
font-size: 1rem;
font-weight: 600;
padding: 13px 30px;
border-radius: 10px;
border: none;
cursor: pointer;
transition: transform 0.06s ease, opacity 0.2s ease, background 0.2s ease;
}
button:active {
transform: translateY(1px);
}
button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.btn-primary {
background: var(--gold);
color: var(--navy);
}
.btn-primary:hover:not(:disabled) {
background: #d8b95f;
}
.btn-ghost {
background: transparent;
color: var(--navy);
border: 1px solid rgba(13, 27, 42, 0.25);
}
.btn-ghost:hover:not(:disabled) {
background: rgba(13, 27, 42, 0.05);
}
/* ---------- Loading state ---------- */
.loading {
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
min-height: 60vh;
background: var(--cream);
color: var(--navy);
}
.loading.active {
display: flex;
}
.loading p {
font-family: "Playfair Display", "DM Sans", serif;
font-size: 1.6rem;
color: var(--navy);
}
.spinner {
width: 46px;
height: 46px;
border: 4px solid rgba(201, 168, 76, 0.3);
border-top-color: var(--gold);
border-radius: 50%;
animation: spin 0.9s linear infinite;
margin-bottom: 22px;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* ---------- Profile page ---------- */
.profile-narrative {
background: var(--navy);
color: var(--cream);
padding: 36px 34px;
border-radius: 16px;
font-size: 1.2rem;
line-height: 1.7;
box-shadow: 0 10px 30px var(--shadow);
margin-bottom: 36px;
}
.section-label {
font-family: "Playfair Display", "DM Sans", serif;
font-size: 0.85rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--gold);
margin: 0 0 18px;
}
.ikigai-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 18px;
margin-bottom: 40px;
}
.card {
background: #fff;
border: 1px solid rgba(13, 27, 42, 0.1);
border-radius: 14px;
padding: 22px 24px;
box-shadow: 0 4px 14px rgba(13, 27, 42, 0.05);
}
.card h3 {
display: flex;
align-items: center;
gap: 9px;
margin: 0 0 10px;
font-size: 1.05rem;
color: var(--navy);
}
.card p {
margin: 0;
color: var(--navy-soft);
font-size: 0.98rem;
}
.dot {
display: inline-block;
width: 11px;
height: 11px;
border-radius: 50%;
flex: 0 0 auto;
}
.dot.high {
background: var(--green);
}
.dot.medium {
background: var(--yellow);
}
.dot.low {
background: var(--red);
}
.triad-block {
background: #fff;
border: 1px solid rgba(13, 27, 42, 0.1);
border-left: 5px solid var(--gold);
border-radius: 14px;
padding: 26px 28px;
margin-bottom: 40px;
}
.triad-block h2 {
display: flex;
align-items: center;
gap: 10px;
margin: 0 0 12px;
font-family: "Playfair Display", "DM Sans", serif;
font-size: 1.3rem;
color: var(--navy);
}
.triad-block p {
margin: 0;
color: var(--navy-soft);
}
.confirm-row {
text-align: center;
}
.confirmed {
background: var(--navy) !important;
color: var(--cream) !important;
cursor: default;
}
/* ---------- Profile edit mode ---------- */
.edit-help {
color: var(--navy-soft);
margin: 0 0 24px;
}
.edit-field {
margin-bottom: 18px;
}
.edit-field label {
display: block;
font-weight: 600;
font-size: 0.95rem;
color: var(--navy);
margin-bottom: 8px;
}
textarea.edit {
min-height: 120px;
padding: 14px 16px;
font-size: 0.98rem;
}
/* ---------- Reflection chat ---------- */
.thread {
display: flex;
flex-direction: column;
gap: 14px;
max-height: 55vh;
overflow-y: auto;
padding: 4px 2px 8px;
margin-bottom: 22px;
}
.bubble {
max-width: 80%;
padding: 14px 18px;
border-radius: 16px;
font-size: 1rem;
line-height: 1.55;
white-space: pre-wrap;
}
.bubble .who {
display: block;
font-size: 0.72rem;
letter-spacing: 0.08em;
text-transform: uppercase;
font-weight: 600;
margin-bottom: 5px;
opacity: 0.7;
}
.bubble.coach {
align-self: flex-start;
background: #fff;
border: 1px solid rgba(13, 27, 42, 0.1);
color: var(--navy-soft);
border-bottom-left-radius: 5px;
}
.bubble.person {
align-self: flex-end;
background: var(--navy);
color: var(--cream);
border-bottom-right-radius: 5px;
}
.bubble.person .who {
color: var(--gold);
opacity: 1;
}
.note {
align-self: center;
font-size: 0.85rem;
color: var(--gold);
font-style: italic;
}
.note.err {
color: var(--red);
}
.reflect-input textarea.edit {
min-height: 90px;
}
.back-link {
margin-top: 8px;
}
.back-link a {
color: var(--navy-soft);
text-decoration: none;
font-size: 0.92rem;
}
.back-link a:hover {
color: var(--gold);
}
.error-box {
background: #fdecea;
border: 1px solid var(--red);
color: #7a231b;
padding: 18px 22px;
border-radius: 12px;
}
@media (max-width: 560px) {
.ikigai-grid {
grid-template-columns: 1fr;
}
.prompt-text {
font-size: 1.4rem;
}
}