initial commit

This commit is contained in:
2026-08-26 15:01:15 +05:30
parent aa42d9ee81
commit cf4f4ecd9c
87 changed files with 12814 additions and 2328 deletions

View File

@@ -45,24 +45,32 @@
}
.dai-root {
--dai-accent: #C8102E;
/* Brand accent — resolves to the app's accent token, which is black
(root CLAUDE.md §6.2: "the brand is black", one accent only). Send
button, active states and the trigger all read from this single
variable, so switching the assistant to Doormile red is a one-line
change here rather than a hunt through the components. */
--dai-accent: var(--color-accent, #0f172a);
--dai-accent-contrast: #ffffff;
--dai-accent-ring: rgba(200, 16, 46, 0.16);
/* Focus ring, tinted with the accent rather than a flat grey wash. */
--dai-accent-ring: color-mix(in srgb, var(--dai-accent) 14%, transparent);
--dai-ai-from: #C8102E;
--dai-ai-to: #E11D48;
--dai-ai-glow: rgba(200, 16, 46, 0.15);
/* AI identity accent — used ONLY on the spark/orb marks so the assistant
reads as an AI surface without turning the panel into a purple product. */
--dai-ai-from: #6366f1;
--dai-ai-to: #8b5cf6;
--dai-ai-glow: rgba(99, 102, 241, 0.18);
--dai-text: #0f172a;
--dai-text-secondary: #475569;
--dai-text-secondary: #64748b;
--dai-text-muted: #94a3b8;
--dai-surface: #ffffff;
--dai-surface-alt: #f8fafc;
--dai-surface-hover: #f1f5f9;
--dai-border: #e2e8f0;
--dai-border-strong: #cbd5e1;
--dai-border: rgba(15, 23, 42, 0.08);
--dai-border-strong: rgba(15, 23, 42, 0.14);
--dai-scrim: rgba(15, 23, 42, 0.08);
--dai-shadow: -6px 0 24px rgba(15, 23, 42, 0.06);
--dai-shadow: 0 8px 30px rgba(15, 23, 42, 0.1);
--dai-live: #10b981;
--dai-panel-width: 428px;
@@ -109,22 +117,16 @@
No radius and no drop shadow: both are what make a surface read as floating
ABOVE the page. A soft shadow is kept only as a left-edge falloff so the
seam has depth without the panel detaching. */
/* Two classes, not one: the element is a shadcn SheetContent, which carries
`inset-y-0 h-full` from Tailwind. Those utilities are emitted after this
stylesheet, so a single `.dai-panel` ties on specificity and loses — the
dock then starts at y=0 and covers the top nav it is supposed to sit under.
`.dai-root.dai-panel` out-specifies them; `height: auto` hands the box back
to top/bottom so it cannot run past the bottom of the window. */
.dai-root.dai-panel {
.dai-panel {
position: fixed;
/* Measured from the app header by AIPanel — see the [data-app-header] hook. */
/* Set from JS by AIPanel — Astryx does not publish a header-height token,
despite --appshell-header-height looking like one. */
top: var(--dai-dock-top, 57px);
right: 0;
bottom: 0;
height: auto;
z-index: 1200;
width: var(--dai-dock-width);
display: none;
display: flex;
flex-direction: column;
min-height: 0;
overflow: hidden;
@@ -138,20 +140,13 @@
transform var(--dai-duration) var(--dai-ease),
visibility 0s linear var(--dai-duration);
}
.dai-panel[data-state='open'],
.dai-panel[data-open='true'] {
display: flex !important;
transform: translateX(0);
visibility: visible;
transition:
transform var(--dai-duration) var(--dai-ease),
visibility 0s;
}
.dai-panel[data-state='closed'],
.dai-panel[data-open='false'] {
display: none !important;
}
/* ---- The page makes room -------------------------------------------------
`.dai-docked` is set on <body> while the panel is open. Padding rather than
@@ -162,10 +157,10 @@
The transition sits on the container unconditionally so the page slides back
when the panel closes too — a rule that only exists while `.dai-docked` is
applied cannot animate its own removal. */
.dai-page {
.astryx-layout-content {
transition: padding-right var(--dai-duration) var(--dai-ease);
}
body.dai-docked .dai-page {
body.dai-docked .astryx-layout-content {
padding-right: var(--dai-dock-width);
}
@@ -174,7 +169,7 @@ body.dai-docked .dai-page {
used to be everywhere — a full-width overlay with a scrim — and the page
stops reserving a strip it cannot afford. */
@media (max-width: 900px) {
.dai-root.dai-panel {
.dai-panel {
top: 0;
width: 100%;
z-index: 1301;
@@ -187,7 +182,7 @@ body.dai-docked .dai-page {
.dai-scrim[data-open='true'] {
opacity: 1;
}
body.dai-docked .dai-page {
body.dai-docked .astryx-layout-content {
padding-right: 0;
}
}
@@ -199,59 +194,67 @@ body.dai-docked .dai-page {
-------------------------------------------------------------------------- */
.dai-root .dai-header {
flex: 0 0 auto;
padding: 14px 16px 12px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(12px);
padding: 14px 12px 12px 14px;
border-bottom: 1px solid var(--dai-border);
}
.dai-root .dai-title {
font-size: 15px;
font-weight: 700;
font-weight: 650;
line-height: 1.2;
letter-spacing: -0.01em;
color: var(--dai-text);
}
.dai-root .dai-subtitle {
font-size: 12px;
font-weight: 500;
line-height: 1.3;
color: var(--dai-text-muted);
}
/* The AI mark — sleek rounded tinted container */
/* The AI mark. A soft gradient orb — not a robot face. */
.dai-root .dai-spark {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
border-radius: 9px;
background: #fff1f2;
border: 1px solid #fecdd3;
border-radius: 999px;
background: var(--dai-surface);
overflow: hidden;
}
/* The mark's own canvas is only 66.8% content — a third of every edge is
transparent padding, measured off the PNG's alpha bounding box (x and y both
170..853 of 1024, i.e. perfectly centred). Drawing it at 100% therefore
rendered a D two-thirds the size the box implied, which is exactly why it
read as too small. 150% cancels that padding so the D fills its box edge to
edge; the overflow:hidden above clips only transparent pixels. */
.dai-root .dai-spark img {
width: 150%;
height: 150%;
max-width: none;
object-fit: contain;
display: block;
}
.dai-root .dai-spark[data-size='sm'] {
width: 28px;
height: 28px;
width: 30px;
height: 30px;
}
.dai-root .dai-spark[data-size='md'] {
width: 36px;
height: 36px;
width: 40px;
height: 40px;
}
.dai-root .dai-spark[data-size='lg'] {
width: 48px;
height: 48px;
width: 56px;
height: 56px;
}
/* Live indicator — crisp pill */
/* Live indicator — subtle, not a large pill. */
.dai-root .dai-live {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 3px 9px;
padding: 3px 8px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
font-weight: 550;
color: #047857;
background: #ecfdf5;
border: 1px solid #a7f3d0;
background: rgba(16, 185, 129, 0.08);
white-space: nowrap;
}
.dai-root .dai-live-dot {
@@ -275,10 +278,9 @@ body.dai-docked .dai-page {
/* Page-context strip — "Orders · Today · All locations" */
.dai-root .dai-context {
flex: 0 0 auto;
padding: 8px 16px;
padding: 7px 14px;
font-size: 11.5px;
font-weight: 500;
color: var(--dai-text-secondary);
color: var(--dai-text-muted);
background: var(--dai-surface-alt);
border-bottom: 1px solid var(--dai-border);
white-space: nowrap;
@@ -367,31 +369,34 @@ body.dai-docked .dai-page {
align-items: center;
gap: 6px;
max-width: 100%;
padding: 6px 14px;
padding: 5px 10px;
text-align: left;
font: inherit;
font-size: 12px;
line-height: 1.35;
font-weight: 550;
font-weight: 500;
color: var(--dai-text);
background: var(--dai-surface);
border: 1px solid var(--dai-border);
border-radius: 999px;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
/* 14px, pinned — NOT 999px.
On a one-line chip a fully round radius already resolves to about 14px, so
these look identical. The difference shows on a chip whose question wraps
to two lines: 999px would resolve to half of ~46px and the chip stops
reading as a chip and starts reading as a card, so one long suggestion
would look like a different component from the nineteen beside it. */
border-radius: 14px;
cursor: pointer;
transition:
background-color 140ms ease,
border-color 140ms ease,
color 140ms ease,
transform 140ms ease,
box-shadow 140ms ease;
transform 140ms ease;
}
.dai-suggestion:hover {
background: #fff1f2;
border-color: #fecdd3;
color: #C8102E;
background: var(--dai-surface-alt);
border-color: var(--dai-border-strong);
transform: translateY(-1px);
box-shadow: 0 2px 6px rgba(200, 16, 46, 0.1);
}
.dai-suggestion:active {
transform: translateY(0);
@@ -406,7 +411,7 @@ body.dai-docked .dai-page {
transition: color 140ms ease;
}
.dai-suggestion:hover .dai-suggestion-icon {
color: #C8102E;
color: var(--dai-text-secondary);
}
.dai-root .dai-suggestion-text {
min-width: 0;
@@ -497,32 +502,22 @@ body.dai-docked .dai-page {
}
.dai-root .dai-msg-user {
max-width: 84%;
max-width: 82%;
margin-left: auto;
padding: 10px 15px;
border-radius: 18px 18px 4px 18px;
padding: 8px 12px;
border-radius: 5px;
border-bottom-right-radius: 5px;
font-size: 13.5px;
font-weight: 500;
line-height: 1.45;
color: #ffffff;
background: linear-gradient(135deg, #C8102E 0%, #A00C24 100%);
box-shadow: 0 2px 8px rgba(200, 16, 46, 0.16);
color: var(--dai-accent-contrast);
background: var(--dai-accent);
white-space: pre-wrap;
overflow-wrap: anywhere;
}
/* Assistant — sleek rounded response card */
.dai-root .dai-msg-row {
background: #f8fafc;
border: 1px solid var(--dai-border);
border-radius: 18px 18px 18px 4px;
padding: 12px 14px;
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.03);
max-width: 95%;
width: fit-content;
}
/* Assistant — no bubble. Text sits on the panel surface. */
.dai-root .dai-msg-ai {
font-size: 13.5px;
line-height: 1.55;
line-height: 1.5;
color: var(--dai-text);
overflow-wrap: anywhere;
}
@@ -534,8 +529,8 @@ body.dai-docked .dai-page {
}
.dai-root .dai-msg-name {
font-size: 11.5px;
font-weight: 700;
color: #334155;
font-weight: 600;
color: var(--dai-text-secondary);
}
.dai-root .dai-msg-footer {
font-size: 11px;
@@ -700,23 +695,24 @@ body.dai-docked .dai-page {
-------------------------------------------------------------------------- */
.dai-root .dai-composer-wrap {
flex: 0 0 auto;
padding: 12px 14px calc(12px + env(safe-area-inset-bottom, 0px));
padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
border-top: 1px solid var(--dai-border);
background: var(--dai-surface);
}
.dai-root .dai-composer {
border: 1.5px solid var(--dai-border);
border-radius: 16px;
background: var(--dai-surface-alt);
border: 1px solid var(--dai-border-strong);
/* 14px, the same corner the suggestion chips use. At 5px the input was the
one sharp-cornered thing in a panel of rounded surfaces, and it sat
directly beneath the chips where the mismatch was most visible. */
border-radius: 14px;
background: var(--dai-surface);
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
padding: 10px 12px 8px 14px;
padding: 10px 10px 8px 14px;
transition:
border-color 140ms ease,
background-color 140ms ease,
box-shadow 140ms ease;
}
.dai-root .dai-composer[data-focused='true'] {
background: var(--dai-surface);
border-color: var(--dai-accent);
box-shadow: 0 0 0 3px var(--dai-accent-ring);
}
@@ -815,27 +811,24 @@ body.dai-docked .dai-page {
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 32px;
height: 32px;
width: 30px;
height: 30px;
padding: 0;
border: none;
border-radius: 50%;
background: var(--dai-accent);
color: var(--dai-accent-contrast);
cursor: pointer;
box-shadow: 0 2px 6px rgba(200, 16, 46, 0.25);
transition:
background-color 140ms ease,
opacity 140ms ease,
transform 140ms ease,
box-shadow 140ms ease;
transform 140ms ease;
}
.dai-root .dai-send:hover:not(:disabled) {
opacity: 0.92;
transform: scale(1.05);
opacity: 0.86;
}
.dai-root .dai-send:active:not(:disabled) {
transform: scale(0.95);
transform: scale(0.94);
}
.dai-root .dai-send:focus-visible {
outline: 2px solid var(--dai-accent);
@@ -846,7 +839,6 @@ body.dai-docked .dai-page {
.dai-root .dai-send:disabled {
background: var(--dai-surface-hover);
color: var(--dai-text-muted);
box-shadow: none;
cursor: default;
}
.dai-root .dai-composer textarea {