/* --------------------------------------------------------------------------- Shared component primitives. Page-level layout lives in each page's scoped styles; anything used by more than one surface lives here. --------------------------------------------------------------------------- */ /* ---- Shared keyframes (referenced by scoped styles too) ---- */ @keyframes fade-up { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes spin { to { transform: rotate(360deg); } } @keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } } @keyframes pop { 0% { transform: scale(0.6); opacity: 0; } 100% { transform: scale(1); opacity: 1; } } /* --------------------------------------------------------------------------- Buttons. Primary = ink, secondary = outlined surface, ghost = quiet, danger = destructive only. Hover lifts, press settles — both subtle. --------------------------------------------------------------------------- */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.45rem; border: 1px solid transparent; border-radius: var(--r-md); padding: 0.6rem 1.05rem; font-weight: 600; font-size: var(--text-base); line-height: 1.25; cursor: pointer; text-decoration: none; background: transparent; color: var(--ink); transition: background-color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out); } .btn:active:not(:disabled) { transform: translateY(1px); } .btn:disabled { opacity: 0.5; cursor: not-allowed; } .btn-primary { background: var(--btn-primary-bg); color: var(--btn-primary-ink); box-shadow: var(--shadow-1); } .btn-primary:hover:not(:disabled) { background: var(--btn-primary-bg-hover); box-shadow: var(--shadow-2); } .btn-secondary { background: var(--surface); border-color: var(--line-strong); color: var(--ink); box-shadow: var(--shadow-1); } .btn-secondary:hover:not(:disabled) { border-color: var(--ink-3); background: var(--surface-2); } .btn-ghost { color: var(--ink-2); } .btn-ghost:hover:not(:disabled) { background: var(--surface-3); color: var(--ink); } .btn-danger { background: var(--btn-danger-bg); color: var(--btn-danger-ink); } .btn-danger:hover:not(:disabled) { background: var(--btn-danger-bg-hover); } .btn-link { padding: 0.25rem 0.4rem; border-radius: var(--r-sm); color: var(--accent); font-weight: 600; font-size: var(--text-sm); } .btn-link:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent-on-soft); } .btn-sm { padding: 0.38rem 0.72rem; font-size: var(--text-sm); border-radius: var(--r-sm); } .btn-block { width: 100%; } /* Icon-only buttons stay square and ≥ 36px so they remain comfortable taps. */ .btn-icon { padding: 0.5rem; min-width: 2.25rem; min-height: 2.25rem; } /* --------------------------------------------------------------------------- Cards & panels --------------------------------------------------------------------------- */ .card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-1); padding: var(--s-5); } .card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-bottom: var(--s-4); } .card-head h2, .card-head h3 { display: flex; align-items: center; gap: var(--s-2); } .well { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s-4); } /* --------------------------------------------------------------------------- Status badges — a tinted chip with a dot, so state never relies on color alone. Tones: neutral, ok, warn, danger, info. --------------------------------------------------------------------------- */ .badge { display: inline-flex; align-items: center; gap: 0.34rem; padding: 0.18rem 0.58rem; border-radius: var(--r-pill); font-size: var(--text-xs); font-weight: 600; letter-spacing: 0.01em; background: var(--surface-2); color: var(--ink-2); border: 1px solid var(--line); white-space: nowrap; } .badge .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; } .badge-ok { background: var(--ok-soft); color: var(--ok-on-soft); border-color: color-mix(in srgb, var(--ok) 30%, transparent); } .badge-warn { background: var(--warn-soft); color: var(--warn-on-soft); border-color: color-mix(in srgb, var(--warn) 30%, transparent); } .badge-danger { background: var(--danger-soft); color: var(--danger-on-soft); border-color: color-mix(in srgb, var(--danger) 30%, transparent); } .badge-info { background: var(--accent-soft); color: var(--accent-on-soft); border-color: color-mix(in srgb, var(--accent) 30%, transparent); } /* --------------------------------------------------------------------------- Notes & inline messages --------------------------------------------------------------------------- */ .note { display: flex; gap: var(--s-3); align-items: flex-start; padding: var(--s-3) var(--s-4); border-radius: var(--r-md); border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-2); font-size: var(--text-sm); line-height: 1.5; } .note > svg { flex: none; margin-top: 0.1rem; } .note-warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 28%, transparent); color: var(--warn-on-soft); } .note-danger { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 28%, transparent); color: var(--danger-on-soft); } .note-ok { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 28%, transparent); color: var(--ok-on-soft); } .note-info { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 28%, transparent); color: var(--accent-on-soft); } /* Inline, per-field validation message. */ .field-error { display: flex; align-items: center; gap: 0.35rem; margin-top: var(--s-1); color: var(--danger); font-size: var(--text-sm); animation: fade-up var(--dur-2) var(--ease-out); } /* --------------------------------------------------------------------------- Chat bubbles (public claim + staff transcript) --------------------------------------------------------------------------- */ .bubble { max-width: 84%; padding: 0.62rem 0.85rem; border-radius: var(--r-lg); line-height: 1.5; word-break: break-word; animation: fade-up var(--dur-3) var(--ease-out); font-size: var(--text-base); } .bubble .who { display: block; font-size: var(--text-xs); font-weight: 700; margin-bottom: 0.12rem; } .bubble.assistant, .bubble.system { align-self: flex-start; background: var(--surface-2); border: 1px solid var(--line); border-bottom-left-radius: var(--r-sm); color: var(--ink); } .bubble.assistant .who { color: var(--ink-3); } .bubble.user { align-self: flex-end; background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent); border-bottom-right-radius: var(--r-sm); color: var(--ink); } .bubble.user .who { color: var(--accent-on-soft); } .bubble.staff { align-self: flex-start; background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent); border-bottom-left-radius: var(--r-sm); color: var(--ink); } .bubble.staff .who { color: var(--warn-on-soft); } .bubble.photo-bubble { padding: 0.3rem; } .bubble.photo-bubble img { display: block; max-width: 200px; border-radius: calc(var(--r-lg) - 4px); } .typing-dots { display: inline-flex; gap: 4px; align-items: center; padding: 0.2rem 0; } .typing-dots i { width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); animation: typing-bounce 1.2s infinite var(--ease-out); } .typing-dots i:nth-child(2) { animation-delay: 0.15s; } .typing-dots i:nth-child(3) { animation-delay: 0.3s; } @keyframes typing-bounce { 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } 30% { transform: translateY(-4px); opacity: 1; } } /* --------------------------------------------------------------------------- Lists & micro-layout utilities --------------------------------------------------------------------------- */ .grid { display: grid; gap: var(--s-4); } .grid-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } .row { display: flex; align-items: center; gap: var(--s-2); min-width: 0; } /* A truncating child inside a .row must be allowed to shrink below its content width, or it inflates the whole layout (flex min-width:auto). */ .row > .trunc { flex: 1 1 auto; min-width: 0; } .wrap { flex-wrap: wrap; } .actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2); } .stack { display: grid; gap: var(--s-3); } .muted { color: var(--ink-3); } .optional { font-weight: 400; color: var(--ink-3); } .brand-name { font-weight: 700; letter-spacing: -0.01em; } /* Top bar shared by Home, the public claim page, and the staff gate. */ .page-bar { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); padding: var(--s-2) 0 var(--s-5); } .back-link { display: flex; align-items: center; gap: var(--s-3); text-decoration: none; color: inherit; border-radius: var(--r-md); min-width: 0; } .section-title { display: flex; align-items: center; gap: var(--s-2); font-size: var(--text-base); font-weight: 650; margin-bottom: var(--s-2); } .section-title svg { color: var(--ink-3); } .hint { color: var(--ink-3); font-size: var(--text-sm); line-height: 1.5; margin: var(--s-1) 0 0; } .req { color: var(--danger); margin-left: 0.12rem; } .trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; } .mono { font-family: var(--font-mono); } .clamp-2, .clamp-3 { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; } .clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; } .clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; } .meta { color: var(--ink-3); font-size: var(--text-sm); line-height: 1.5; } /* Hide visually, keep for screen readers. */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; } /* File-picker disguised as a button; input stays focusable for keyboards. */ .file-btn { display: inline-flex; align-items: center; gap: 0.45rem; cursor: pointer; padding: 0.45rem 0.85rem; border-radius: var(--r-md); border: 1px dashed var(--line-strong); background: var(--surface); color: var(--ink-2); font-size: var(--text-sm); font-weight: 600; margin: 0; transition: border-color var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out); } .file-btn:hover { border-color: var(--ink-3); background: var(--surface-2); color: var(--ink); } .file-btn:focus-within { outline: 2px solid var(--focus); outline-offset: 2px; } .file-btn input[type="file"] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; } /* Inline confirmation for destructive actions — appears in place, cancellable, and Esc-dismissable (wired in markup). */ .inline-confirm { margin-top: var(--s-3); padding: var(--s-3) var(--s-4); border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent); border-radius: var(--r-md); background: var(--danger-soft); animation: fade-up var(--dur-2) var(--ease-out); } .inline-confirm p { margin: 0 0 var(--s-2); color: var(--danger-on-soft); font-size: var(--text-sm); } /* Selectable list cards (claims, events). */ .list-card { display: grid; gap: 0.45rem; width: 100%; text-align: left; padding: var(--s-3) var(--s-4); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); color: inherit; cursor: pointer; font: inherit; transition: border-color var(--dur-1) var(--ease-out), background-color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out); } .list-card:hover { border-color: var(--line-strong); box-shadow: var(--shadow-1); } .list-card:active { transform: translateY(1px); } .list-card.active { border-color: var(--accent); background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); } [data-theme="dark"] .list-card.active { background: var(--accent-soft); } /* On touch devices, grow compact controls to a comfortable tap size without changing their visual weight (padding/min-height only, same font). */ @media (pointer: coarse) { .btn-sm, .btn-link { min-height: 2.5rem; } .btn-icon { min-width: 2.75rem; min-height: 2.75rem; } .filter-chip { min-height: 2.25rem; } .toast-close { padding: 0.55rem; } .file-btn { min-height: 2.5rem; } }