DocDoeAI / src /app /student-os.css
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
f3056f5
Raw History Blame Contribute Delete
45.3 kB
/* DocDoe Student OS β€” shared product surface polish */
.docdoe-student-os {
--student-canvas: #ffffff;
--student-panel: #ffffff;
--student-ink: #171717;
--student-ink-2: #404040;
--student-muted: #666666;
--student-muted-2: #707070;
--student-line: #ebebeb;
--student-line-strong: #c9c9c9;
--student-accent: var(--student-ink);
--student-accent-hover: #292929;
--student-accent-soft: #f5f5f5;
--student-rail: #fafafa;
--student-scroll-thumb: #c9c9c9;
--student-scroll-hover: #a3a3a3;
--student-purple: var(--student-accent);
--student-purple-hover: var(--student-accent-hover);
--student-purple-soft: var(--student-accent-soft);
--student-radius-sm: 8px;
--student-radius-md: 12px;
--student-radius-lg: 14px;
--student-radius-xl: 18px;
--student-shadow-sm: 0 1px 2px rgba(15, 17, 23, 0.03);
--student-shadow-card:
0 1px 2px rgba(15, 17, 23, 0.03),
0 8px 24px rgba(15, 17, 23, 0.04);
--student-shadow-lift:
0 2px 4px rgba(15, 17, 23, 0.04),
0 14px 32px rgba(15, 17, 23, 0.08);
--student-ease: cubic-bezier(0.23, 1, 0.32, 1);
--student-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--student-motion-instant: 90ms;
--student-motion-fast: 150ms;
--student-motion-base: 200ms;
--student-motion-slow: 260ms;
--student-motion-settle: 320ms;
--student-stagger-step: 40ms;
--student-stagger-cap: 5;
color-scheme: light;
color: var(--student-ink);
background: var(--student-canvas);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
--student-sidebar-w: 248px;
--student-header-h: 56px;
--student-bottom-nav-h: 64px;
--student-safe-bottom: env(safe-area-inset-bottom, 0px);
--student-safe-top: env(safe-area-inset-top, 0px);
--student-shell-bottom: calc(
var(--student-bottom-nav-h) + var(--student-safe-bottom)
);
overflow-x: clip;
max-width: 100%;
}
@media (min-width: 1024px) {
.docdoe-student-os {
--student-header-h: 68px;
--student-shell-bottom: 0px;
}
}
.docdoe-student-os ::selection {
background: rgba(23, 23, 22, 0.14);
}
.docdoe-student-os :is(h1, h2) {
text-wrap: balance;
}
.docdoe-student-os :is(p, li, figcaption) {
text-wrap: pretty;
}
.docdoe-student-os,
.docdoe-student-os * {
scrollbar-color: var(--student-scroll-thumb) transparent;
scrollbar-width: thin;
}
.docdoe-student-os ::-webkit-scrollbar {
width: 7px;
height: 7px;
}
.docdoe-student-os ::-webkit-scrollbar-thumb {
border: 2px solid transparent;
border-radius: 999px;
background: var(--student-scroll-thumb);
background-clip: padding-box;
}
.docdoe-student-os ::-webkit-scrollbar-thumb:hover {
background: var(--student-scroll-hover);
background-clip: padding-box;
}
@media (forced-colors: active) {
.docdoe-student-os,
.docdoe-student-os * {
scrollbar-color: auto;
scrollbar-width: auto;
}
.docdoe-student-os ::-webkit-scrollbar-thumb {
background: ButtonText;
}
}
.docdoe-student-os button,
.docdoe-student-os a {
-webkit-tap-highlight-color: transparent;
}
.docdoe-student-os button:not(:disabled),
.docdoe-student-os a[href] {
cursor: pointer;
}
.docdoe-student-os :is(h1, h2, h3, [tabindex="-1"]) {
scroll-margin-top: calc(var(--student-header-h) + 16px);
}
/* Migrate repeated legacy utility colors through the shared product tokens so
muted labels remain readable without changing every page-local component. */
.docdoe-student-os [class~="text-[#73757c]"] {
color: var(--student-muted) !important;
}
.docdoe-student-os [class~="text-[#92949a]"] {
color: var(--student-muted-2) !important;
}
/* A few older Student OS surfaces still carry pale metadata utilities. Route
them through the same readable muted token instead of maintaining a second
contrast scale in individual pages. */
.docdoe-student-os [class~="text-[#6f7484]"],
.docdoe-student-os [class~="text-[#747783]"],
.docdoe-student-os [class~="text-[#7a8190]"],
.docdoe-student-os [class~="text-[#83848d]"],
.docdoe-student-os [class~="text-[#858690]"],
.docdoe-student-os [class~="text-[#858894]"],
.docdoe-student-os [class~="text-[#888993]"],
.docdoe-student-os [class~="text-[#898a94]"],
.docdoe-student-os [class~="text-[#898b95]"],
.docdoe-student-os [class~="text-[#8a8b94]"],
.docdoe-student-os [class~="text-[#8b8c95]"],
.docdoe-student-os [class~="text-[#a8aaaf]"] {
color: var(--student-muted) !important;
}
.docdoe-student-os [class~="text-white/30"],
.docdoe-student-os [class~="text-white/40"],
.docdoe-student-os [class~="text-white/45"] {
color: rgb(255 255 255 / 0.68) !important;
}
/* Shared card elevation */
.docdoe-student-os .student-panel {
border: 1px solid var(--student-line);
border-radius: var(--student-radius-lg);
background: var(--student-panel);
box-shadow: var(--student-shadow-card);
}
.docdoe-student-os .student-card-lift {
transition:
transform 170ms var(--student-ease),
border-color 170ms var(--student-ease),
box-shadow 170ms var(--student-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .student-card-lift:hover {
transform: translateY(-2px);
border-color: var(--student-line-strong);
box-shadow: var(--student-shadow-lift);
}
}
.docdoe-student-os .student-card-lift:active {
transform: translateY(0) scale(0.995);
}
/* Pressable controls */
.docdoe-student-os .student-press {
transition:
transform 140ms var(--student-ease),
background-color 160ms var(--student-ease),
border-color 160ms var(--student-ease),
box-shadow 160ms var(--student-ease),
color 160ms var(--student-ease);
}
.docdoe-student-os .student-press:active:not(:disabled) {
transform: scale(0.97);
}
.docdoe-student-os .student-press:focus-visible {
outline: 2px solid var(--student-accent);
outline-offset: 2px;
}
/* Quiet eyebrow labels β€” product, not marketing shout */
.docdoe-student-os .student-eyebrow {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--student-muted);
}
.docdoe-student-os .student-eyebrow-accent {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--student-purple);
}
/* Input focus ring */
.docdoe-student-os .student-input-ring {
transition:
border-color 160ms var(--student-ease),
box-shadow 160ms var(--student-ease);
}
.docdoe-student-os .student-input-ring:focus-within {
border-color: rgba(23, 24, 27, 0.45);
box-shadow: 0 0 0 3px rgba(23, 24, 27, 0.1);
}
/* Page-local classes predate the Student OS tokens. Keep those routes
visually coherent while they are migrated: purple is not a product accent. */
.docdoe-student-os [class~="text-[#6c43f3]"],
.docdoe-student-os [class~="text-[#6d5ce7]"],
.docdoe-student-os [class~="text-[#5b35e0]"],
.docdoe-student-os [class~="text-[#5a49d6]"],
.docdoe-student-os [class~="text-[#7b5bf0]"],
.docdoe-student-os [class~="text-[#7865e7]"],
.docdoe-student-os [class*="text-violet-"] {
color: var(--student-accent) !important;
}
.docdoe-student-os [class~="bg-[#6c43f3]"],
.docdoe-student-os [class~="bg-[#6d5ce7]"],
.docdoe-student-os [class*="bg-violet-"] {
background-color: var(--student-accent) !important;
background-image: none !important;
}
.docdoe-student-os [class~="bg-[#f1edff]"],
.docdoe-student-os [class~="bg-[#f1ebff]"],
.docdoe-student-os [class~="bg-[#f0edff]"],
.docdoe-student-os [class~="bg-[#f2efff]"],
.docdoe-student-os [class~="bg-[#f7f5ff]"],
.docdoe-student-os [class*="bg-violet-50"] {
background-color: var(--student-accent-soft) !important;
background-image: none !important;
}
.docdoe-student-os [class*="border-violet-"] {
border-color: var(--student-line-strong) !important;
}
.docdoe-student-os [class*="from-[#f1edff]"],
.docdoe-student-os [class*="from-[#f4edff]"],
.docdoe-student-os [class*="to-[#fbfaff]"] {
--tw-gradient-from: var(--student-accent-soft) !important;
--tw-gradient-to: var(--student-panel) !important;
}
.docdoe-student-os :is(a, button, input, select, textarea):focus-visible {
outline: 2px solid var(--student-accent);
outline-offset: 2px;
}
/* The light rail and drawer need the same graphite keyboard cue as content. */
.docdoe-student-os aside :is(a, button):focus-visible {
outline-color: var(--student-ink);
outline-width: 3px;
}
.docdoe-student-os .student-os-sheet :is(a, button),
.docdoe-student-os [data-testid="docdoe-page-guide"] :is(a, button),
.docdoe-student-os [role="dialog"] :is(a, button) {
min-height: 44px;
}
@media (max-width: 639px) {
.docdoe-student-os main.student-os-main {
padding-inline: 0;
}
}
/* Shared mobile chrome helpers */
.docdoe-student-os .student-os-main {
min-height: calc(100dvh - var(--student-header-h));
padding-bottom: calc(var(--student-shell-bottom) + 12px);
outline: none;
}
@media (min-width: 1024px) {
.docdoe-student-os .student-os-main {
padding-bottom: 5.5rem;
}
}
.docdoe-student-os .student-route-surface {
min-height: inherit;
}
.docdoe-student-os .student-os-header {
min-height: calc(var(--student-header-h) + var(--student-safe-top));
padding-top: var(--student-safe-top);
}
.docdoe-student-os .student-os-bottom-nav {
height: calc(var(--student-bottom-nav-h) + var(--student-safe-bottom));
padding-bottom: var(--student-safe-bottom);
}
@keyframes docdoe-mascot-bob {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-6px);
}
}
.docdoe-mascot-bob {
animation: docdoe-mascot-bob 2.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.docdoe-mascot-bob {
animation: none;
}
}
/* Prevent iOS input zoom without fighting desktop scale */
@media (max-width: 767px) {
.docdoe-student-os input,
.docdoe-student-os select,
.docdoe-student-os textarea {
font-size: 16px !important;
}
}
/* Full-bleed fixed sheets above bottom nav + helper FAB */
.docdoe-student-os .student-os-sheet {
z-index: 80;
}
/* Horizontal safety for long content rows */
.docdoe-student-os .student-os-main,
.docdoe-student-os .student-os-main > * {
max-width: 100%;
}
.docdoe-student-os .student-loading-block {
overflow: hidden;
border-radius: 6px;
background: #ebeae5;
background-image: linear-gradient(
100deg,
transparent 20%,
rgba(255, 255, 255, 0.82) 40%,
transparent 60%
);
background-position: 120% 0;
background-size: 220% 100%;
animation: student-loading-sheen 1.6s ease-in-out infinite;
}
.docdoe-student-os .student-loading-line {
animation: student-loading-line 1.35s var(--student-ease-out) infinite;
transform-origin: left center;
}
@keyframes student-loading-sheen {
0% { background-position: 120% 0; }
100% { background-position: -120% 0; }
}
@keyframes student-loading-line {
0% { transform: translateX(-110%) scaleX(0.45); }
55% { transform: translateX(130%) scaleX(0.9); }
100% { transform: translateX(320%) scaleX(0.45); }
}
@media (prefers-reduced-motion: reduce) {
.docdoe-student-os *,
.docdoe-student-os *::before,
.docdoe-student-os *::after {
scroll-behavior: auto !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
.docdoe-student-os .student-card-lift:hover,
.docdoe-student-os .student-card-lift:active,
.docdoe-student-os .student-press:active:not(:disabled) {
transform: none;
}
.docdoe-student-os .student-loading-block {
animation: none !important;
opacity: 0.7;
}
.docdoe-student-os .student-loading-line {
width: 40%;
animation: none !important;
opacity: 0.7;
transform: none;
}
}
@keyframes docdoe-thinking-pulse {
0%,
70%,
100% {
opacity: 0.28;
transform: translateY(0);
}
35% {
opacity: 1;
transform: translateY(-1.5px);
}
}
@keyframes tuition-load-sweep {
0% {
transform: translateX(-120%);
}
100% {
transform: translateX(360%);
}
}
@keyframes la-plan-load-sweep {
0% {
transform: translateX(-120%);
}
100% {
transform: translateX(360%);
}
}
@keyframes la-plan-card-rise {
0% {
opacity: 0.35;
transform: translateY(6px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes la-plan-pulse {
0%,
100% {
opacity: 0.45;
}
50% {
opacity: 1;
}
}
.la-plan-load-bar {
animation: la-plan-load-sweep 1.15s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform;
}
.la-plan-card-rise {
animation: la-plan-card-rise 0.55s cubic-bezier(0.16, 1, 0.3, 1) both;
}
.la-plan-pulse {
animation: la-plan-pulse 1.1s ease-in-out infinite;
}
/* Quiet indeterminate bar for tuition class prep */
.tuition-load-bar {
animation: tuition-load-sweep 1.15s cubic-bezier(0.4, 0, 0.2, 1) infinite;
will-change: transform;
}
.docdoe-thinking-dots {
display: inline-flex;
gap: 3px;
}
.docdoe-thinking-dots i {
width: 4px;
height: 4px;
border-radius: 999px;
background: currentColor;
animation: docdoe-thinking-pulse 1.05s ease-in-out infinite;
}
.docdoe-thinking-dots i:nth-child(2) {
animation-delay: 0.13s;
}
.docdoe-thinking-dots i:nth-child(3) {
animation-delay: 0.26s;
}
/* Shared muted labels must remain readable on the app and auth surfaces. */
[class~="text-[#777873]"],
[class~="text-[#73757c]"],
[class~="text-[#92949a]"],
[class~="text-[#898a85]"] {
color: #5c6170 !important;
}
/* Learn Anything uses lighter editorial metadata tokens on a white surface. */
[class~="text-[#9CA3AF]"],
[class~="text-[#C4C4CC]"] {
color: #62635e !important;
}
/* ------------------------------------------------------------------ *
* Entrance choreography
*
* Deliberately CSS-only: the stagger, card rise and progress fill run on the
* compositor with no JavaScript per node, which matters on low-end Android
* where a 40-item grid animated through JS springs drops frames. Only
* transform and opacity are touched.
* ------------------------------------------------------------------ */
@keyframes student-rise {
from {
opacity: 0;
transform: translate3d(0, 10px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
@keyframes student-fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Staggered children, capped at 6: delay = min(i, 5) * 40ms, so the whole group
settles within 200ms. The 7th sibling onward renders static β€” a 40-row grid
must never animate 40 nodes, they arrive with the container instead. */
.docdoe-student-os .student-stagger {
animation: student-fade var(--student-motion-slow) var(--student-ease-out) both;
}
.docdoe-student-os .student-stagger > * {
animation: student-rise 220ms var(--student-ease-out) both;
}
.docdoe-student-os .student-stagger > *:nth-child(1) { animation-delay: 0ms; }
.docdoe-student-os .student-stagger > *:nth-child(2) { animation-delay: 40ms; }
.docdoe-student-os .student-stagger > *:nth-child(3) { animation-delay: 80ms; }
.docdoe-student-os .student-stagger > *:nth-child(4) { animation-delay: 120ms; }
.docdoe-student-os .student-stagger > *:nth-child(5) { animation-delay: 160ms; }
.docdoe-student-os .student-stagger > *:nth-child(6) { animation-delay: 200ms; }
/* Beyond the cap the children stop animating individually and ride the
container's single fade. This is what keeps long lists cheap. */
.docdoe-student-os .student-stagger > *:nth-child(n + 7) {
animation: none;
}
/* Slower variant for page-level sections, which read as larger blocks. */
.docdoe-student-os .student-stagger-slow > * {
animation-duration: var(--student-motion-slow);
}
.docdoe-student-os .student-stagger-fade > * {
animation-name: student-fade;
}
/* Standalone rising element. */
.docdoe-student-os .student-rise {
animation: student-rise var(--student-motion-slow) var(--student-ease-out) both;
}
.docdoe-student-os .student-fade-in {
animation: student-fade var(--student-motion-base) var(--student-ease-out) both;
}
/* Progress fills animate with scaleX (compositor) instead of width (layout),
which is the difference between smooth and janky on a weak GPU. */
.docdoe-student-os .student-progress-track {
position: relative;
overflow: hidden;
border-radius: 999px;
background: #e6e7e2;
}
.docdoe-student-os .student-progress-fill {
display: block;
height: 100%;
width: 100%;
border-radius: inherit;
background: var(--student-accent);
transform-origin: left center;
transform: scaleX(var(--student-progress, 0));
transition: transform var(--student-motion-settle) var(--student-ease-out);
}
/* ------------------------------------------------------------------ *
* Tuition focus mode
*
* The tuition surfaces are deliberately dark β€” it is an intentional
* concentration mode, not an oversight, so it must NOT be "fixed" to match the
* light canvas. It was, however, ~345 hardcoded hex literals spread across 12
* files with no single source of truth. These tokens are that source: new work
* should reference them, and the legacy literals migrate over time.
* ------------------------------------------------------------------ */
.docdoe-tuition-focus {
--tuition-canvas: #0d0d0d;
--tuition-surface: #111111;
--tuition-surface-raised: #1a1a1a;
--tuition-border: #2a2a2a;
--tuition-border-strong: #333333;
--tuition-ink: #ffffff;
--tuition-muted: #8b8b8b;
--tuition-success: #10b981;
--tuition-critical: #ef4444;
--tuition-track: rgb(255 255 255 / 0.12);
}
/* Tuition runs on the dark focus surface, so the track tint differs but the
fill still animates with scaleX for the same compositor reason. */
.tuition-progress-fill {
transform-origin: left center;
transform: scaleX(var(--tuition-progress, 0));
transition: transform var(--student-motion-settle) var(--student-ease-out);
}
/* Subtle sheen that runs once when a panel mounts β€” signals "fresh content"
without a spinner. */
@keyframes student-panel-settle {
from {
opacity: 0;
transform: translate3d(0, 6px, 0) scale(0.995);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0) scale(1);
}
}
.docdoe-student-os .student-panel-settle {
animation: student-panel-settle var(--student-motion-settle) var(--student-ease-out) both;
}
/* Count-up values should not reflow their neighbours as digits change. */
.docdoe-student-os .student-tabular {
font-variant-numeric: tabular-nums;
font-feature-settings: "tnum";
}
@media (prefers-reduced-motion: reduce) {
.tuition-load-bar,
.la-plan-load-bar,
.la-plan-card-rise,
.la-plan-pulse,
.docdoe-thinking-dots i {
animation: none;
}
.tuition-load-bar,
.la-plan-load-bar {
width: 40%;
opacity: 0.75;
}
.docdoe-student-os .student-stagger > *,
.docdoe-student-os .student-rise,
.docdoe-student-os .student-fade-in,
.docdoe-student-os .student-panel-settle {
animation: none !important;
opacity: 1 !important;
transform: none !important;
}
.docdoe-student-os .student-progress-fill {
transition: none;
}
}
/* ------------------------------------------------------------------ *
* DocDoe craft layer β€” shared language for /home, /tuition, /study-chat
*
* Three routes were rewritten against one vocabulary instead of three local
* dialects. The rules that keep it coherent:
*
* - White content and a quiet gray rail. Group through space and typography;
* reserve subtle surfaces for related work, not stacks of promotional cards.
* - One neutral scale, owned by --student-* and exposed through --dd-*.
* No independent warm or blue-gray palette in individual student routes.
* - Hue is status. Green/amber/red carry evidence, revision and failure.
* They are never decoration, so at most one appears per reading band.
* - Motion is 150-250ms ease-out and only ever answers a user action:
* hover, focus, press, and value changes. Nothing animates on its own.
* ------------------------------------------------------------------ */
.docdoe-student-os {
--dd-paper: var(--student-canvas);
--dd-canvas: var(--student-rail);
--dd-sunk: var(--student-accent-soft);
--dd-panel: var(--student-panel);
--dd-ink: var(--student-ink);
--dd-ink-2: var(--student-ink-2);
--dd-muted: var(--student-muted);
--dd-faint: var(--student-muted-2);
--dd-line: var(--student-line);
--dd-line-strong: var(--student-line-strong);
--dd-positive: #147a5a;
--dd-positive-soft: #eaf4ef;
--dd-positive-line: #cfe4d9;
--dd-attention: #a66b1f;
--dd-attention-soft: #fbf3e7;
--dd-attention-line: #eddcbe;
--dd-critical: #b33a3a;
--dd-critical-soft: #fbedec;
--dd-critical-line: #eed3d1;
/* Board world. Only class/board surfaces may use these, and only the chalk
hairline below may carry a hue inside them. */
--dd-board: #0b0f0e;
--dd-board-panel: #0e1413;
--dd-board-line: #232a28;
--dd-board-ink: #f2f4f1;
--dd-board-muted: #8f9a92;
--dd-chalk: #ead08c;
--dd-r-sm: 8px;
--dd-r-md: 10px;
--dd-r-lg: 14px;
--dd-r-xl: 18px;
--dd-ease: cubic-bezier(0.16, 1, 0.3, 1);
--dd-ease-soft: cubic-bezier(0.23, 1, 0.32, 1);
--dd-t-fast: 150ms;
--dd-t-base: 200ms;
--dd-t-slow: 250ms;
--dd-t-settle: 320ms;
}
/* ── Typography atoms ─────────────────────────────────────────────── */
/* 11px tracked caps. The product voice for structure; never a headline. */
.docdoe-student-os .dd-eyebrow {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.09em;
line-height: 1;
text-transform: uppercase;
color: var(--dd-faint);
}
.docdoe-student-os .dd-eyebrow-ink {
color: var(--dd-ink-2);
}
/* Docket numerals: index numbers, counts, and any figure that must not
reflow while its neighbours change. */
.docdoe-student-os .dd-mono {
font-family: var(--font-mono), ui-monospace, monospace;
font-variant-numeric: tabular-nums;
font-feature-settings: "tnum";
letter-spacing: 0.02em;
}
/* Display figures β€” the hero moment's numbers only. */
.docdoe-student-os .dd-figure {
font-family: var(--font-display), var(--font-sans), sans-serif;
font-weight: 600;
font-variant-numeric: tabular-nums;
letter-spacing: -0.03em;
line-height: 0.95;
}
/* ── Structure: rules and tear-lines ──────────────────────────────── */
.docdoe-student-os .dd-rule {
height: 1px;
background: var(--dd-line);
border: 0;
}
.docdoe-student-os .dd-tear {
height: 0;
border: 0;
border-top: 1px dashed var(--dd-line-strong);
}
.docdoe-student-os .dd-rule-vertical {
width: 1px;
align-self: stretch;
background: var(--dd-line);
}
/* ── Press and focus ──────────────────────────────────────────────── */
.docdoe-student-os .dd-press {
transition:
transform var(--dd-t-fast) var(--dd-ease),
background-color var(--dd-t-base) var(--dd-ease),
border-color var(--dd-t-base) var(--dd-ease),
color var(--dd-t-base) var(--dd-ease),
box-shadow var(--dd-t-base) var(--dd-ease);
}
.docdoe-student-os .dd-press:active:not(:disabled) {
transform: scale(0.975);
}
.docdoe-student-os .dd-press:focus-visible,
.docdoe-student-os .dd-focusable:focus-visible {
outline: 2px solid var(--dd-ink);
outline-offset: 2px;
}
/* ── Marks ────────────────────────────────────────────────────────── */
/* The product mark. A hairline inset gives the flat ink tile a physical
edge without a glow or gradient. */
.docdoe-student-os .dd-mark {
display: grid;
place-items: center;
border-radius: var(--dd-r-md);
background: var(--dd-ink);
color: #fff;
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.14);
transition: transform var(--dd-t-base) var(--dd-ease);
}
.docdoe-student-os .dd-mark-quiet {
background: var(--dd-sunk);
color: var(--dd-ink);
box-shadow: inset 0 0 0 1px rgb(23 23 22 / 0.05);
}
/* ── Buttons ──────────────────────────────────────────────────────── */
.docdoe-student-os .dd-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
border-radius: var(--dd-r-md);
font-size: 14px;
font-weight: 600;
letter-spacing: -0.005em;
white-space: nowrap;
}
.docdoe-student-os .dd-btn-ink {
background: var(--dd-ink);
color: #fff;
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.1);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-btn-ink:hover:not(:disabled) {
background: #2b2b29;
}
}
.docdoe-student-os .dd-btn-ink:disabled {
background: #cfcfca;
color: #fbfbfa;
box-shadow: none;
}
.docdoe-student-os .dd-btn-paper {
background: var(--dd-panel);
border: 1px solid var(--dd-line-strong);
color: var(--dd-ink);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-btn-paper:hover:not(:disabled) {
border-color: #b9b8b1;
background: var(--dd-paper);
}
}
.docdoe-student-os .dd-btn-ghost {
background: transparent;
color: var(--dd-ink-2);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-btn-ghost:hover:not(:disabled) {
background: var(--dd-sunk);
color: var(--dd-ink);
}
}
/* The inverse button belongs to the board world: white fill, ink text. */
.docdoe-student-os .dd-btn-board {
background: var(--dd-board-ink);
color: var(--dd-board);
box-shadow: inset 0 0 0 1px rgb(11 15 14 / 0.12);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-btn-board:hover:not(:disabled) {
background: #fff;
}
}
/* Arrow that answers a hover without moving the layout. */
.docdoe-student-os .dd-arrow {
transition: transform var(--dd-t-base) var(--dd-ease);
}
.docdoe-student-os .group:hover .dd-arrow,
.docdoe-student-os .group:focus-visible .dd-arrow {
transform: translateX(3px);
}
/* Quiet link: the underline draws from the left on hover instead of a
colour flash, so it reads as navigation rather than a state change. */
.docdoe-student-os .dd-link {
position: relative;
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 600;
color: var(--dd-ink-2);
}
.docdoe-student-os .dd-link::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: -2px;
height: 1px;
background: currentColor;
transform: scaleX(0);
transform-origin: left center;
transition: transform var(--dd-t-base) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-link:hover::after {
transform: scaleX(1);
}
}
.docdoe-student-os .dd-link:focus-visible::after {
transform: scaleX(1);
}
/* ── Rows ─────────────────────────────────────────────────────────── */
/* Rows replace cards for anything that repeats. The hover is a surface
tint plus an arrow nudge β€” no lift, no shadow, no nested border. */
.docdoe-student-os .dd-row {
position: relative;
display: flex;
align-items: center;
gap: 12px;
transition: background-color var(--dd-t-fast) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-row-interactive:hover {
background: var(--dd-paper);
}
}
/* ── Meter ────────────────────────────────────────────────────────── */
/* Fills animate with scaleX so the value change never triggers layout. */
.docdoe-student-os .dd-meter {
position: relative;
height: 3px;
width: 100%;
overflow: hidden;
border-radius: 999px;
background: var(--dd-line);
}
.docdoe-student-os .dd-meter > i {
position: absolute;
inset: 0;
display: block;
border-radius: inherit;
background: var(--dd-ink);
transform-origin: left center;
transform: scaleX(var(--dd-value, 0));
transition: transform var(--dd-t-settle) var(--dd-ease);
}
/* ── Chips ────────────────────────────────────────────────────────── */
.docdoe-student-os .dd-chip {
display: inline-flex;
align-items: center;
gap: 6px;
border-radius: 999px;
padding: 4px 9px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
line-height: 1;
white-space: nowrap;
}
.docdoe-student-os .dd-chip-ink {
background: var(--dd-ink);
color: #fff;
}
.docdoe-student-os .dd-chip-quiet {
background: var(--dd-sunk);
color: var(--dd-ink-2);
}
.docdoe-student-os .dd-chip-outline {
border: 1px solid var(--dd-line-strong);
color: var(--dd-ink-2);
}
/* ── Completion tick ──────────────────────────────────────────────── */
/* The ring draws itself when a task is completed. It is the only place a
stroke animation appears, because it is the one state change a student
performs deliberately and wants confirmed. */
.docdoe-student-os .dd-tick {
display: grid;
place-items: center;
border-radius: 999px;
border: 1.5px solid var(--dd-line-strong);
color: transparent;
transition:
border-color var(--dd-t-base) var(--dd-ease),
background-color var(--dd-t-base) var(--dd-ease),
color var(--dd-t-base) var(--dd-ease),
transform var(--dd-t-fast) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os button:hover > .dd-tick,
.docdoe-student-os .dd-tick-host:hover .dd-tick {
border-color: var(--dd-positive);
transform: scale(1.06);
}
}
.docdoe-student-os .dd-tick-done {
border-color: var(--dd-positive);
background: var(--dd-positive);
color: #fff;
}
@keyframes dd-tick-pop {
0% { transform: scale(0.82); }
60% { transform: scale(1.08); }
100% { transform: scale(1); }
}
.docdoe-student-os .dd-tick-just-done {
animation: dd-tick-pop 240ms var(--dd-ease);
}
/* ── Fields ───────────────────────────────────────────────────────── */
.docdoe-student-os .dd-field {
border-radius: var(--dd-r-lg);
border: 1px solid var(--dd-line-strong);
background: var(--dd-panel);
transition:
border-color var(--dd-t-base) var(--dd-ease),
box-shadow var(--dd-t-base) var(--dd-ease);
}
.docdoe-student-os .dd-field:focus-within {
border-color: rgb(23 23 22 / 0.42);
box-shadow: 0 0 0 3px rgb(23 23 22 / 0.07);
}
/* ── Board surface ────────────────────────────────────────────────── */
/* The one dark surface in the product. It is a blackboard, so it gets a
chalk hairline and a vignette rather than a card shadow, and the chalk
hue is confined to rules inside this element. */
.docdoe-student-os .dd-board {
position: relative;
overflow: hidden;
background: var(--dd-board);
color: var(--dd-board-ink);
border-radius: var(--dd-r-lg);
}
.docdoe-student-os .dd-board::after {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(
120% 90% at 88% 6%,
rgb(234 208 140 / 0.07),
transparent 62%
);
}
.docdoe-student-os .dd-chalk-rule {
height: 1px;
border: 0;
background: linear-gradient(
90deg,
rgb(234 208 140 / 0.5),
rgb(234 208 140 / 0.1) 55%,
transparent
);
}
/* ── Reduced motion ───────────────────────────────────────────────── */
/* ── Route reveal ────────────────────────────────────────────────── */
/* One entrance per route, never per card and never scroll-triggered: the
page arrives as a single block so a student re-entering a tab is not
waiting on a cascade. Everything below it is state-transition motion. */
@keyframes dd-page-in {
from {
opacity: 0;
transform: translate3d(0, 7px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.docdoe-student-os .dd-page-in {
animation: dd-page-in var(--dd-t-slow) var(--dd-ease) both;
}
/* A conversation entry is the one repeat element allowed to animate in:
each message is a discrete arrival, not decoration. */
@keyframes dd-entry-in {
from {
opacity: 0;
transform: translate3d(0, 9px, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
.docdoe-student-os .dd-entry {
animation: dd-entry-in 240ms var(--dd-ease) both;
}
/* ── Status blocks ───────────────────────────────────────────────── */
/* Hue is evidence, never decoration. Each block states one fact about the
student's work, so at most one appears per reading band. */
.docdoe-student-os .dd-status {
display: flex;
align-items: flex-start;
gap: 10px;
border-radius: var(--dd-r-md);
border: 1px solid;
padding: 11px 13px;
font-size: 13px;
line-height: 20px;
}
.docdoe-student-os .dd-status > svg {
margin-top: 3px;
flex-shrink: 0;
}
.docdoe-student-os .dd-status-positive {
border-color: var(--dd-positive-line);
background: var(--dd-positive-soft);
color: var(--dd-positive);
}
.docdoe-student-os .dd-status-attention {
border-color: var(--dd-attention-line);
background: var(--dd-attention-soft);
color: var(--dd-attention);
}
.docdoe-student-os .dd-status-critical {
border-color: var(--dd-critical-line);
background: var(--dd-critical-soft);
color: var(--dd-critical);
}
/* ── Quiet icon button ───────────────────────────────────────────── */
/* Chrome controls sit at the edge of the reading band. They gain a surface
on hover rather than a colour change, so they stay furniture. */
.docdoe-student-os .dd-icon-btn {
display: grid;
place-items: center;
border-radius: var(--dd-r-md);
color: var(--dd-muted);
transition:
background-color var(--dd-t-fast) var(--dd-ease),
color var(--dd-t-fast) var(--dd-ease),
transform var(--dd-t-fast) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-icon-btn:hover:not(:disabled) {
background: var(--dd-sunk);
color: var(--dd-ink);
}
}
.docdoe-student-os .dd-icon-btn:active:not(:disabled) {
transform: scale(0.94);
}
.docdoe-student-os .dd-icon-btn:disabled {
opacity: 0.4;
}
/* ── Rails ───────────────────────────────────────────────────────── */
/* Side rails are ruled lists on the sunken canvas β€” never a stack of
cards, because a rail is navigation and cards imply content. */
.docdoe-student-os .dd-rail {
display: flex;
min-height: 0;
flex-direction: column;
}
.docdoe-student-os .dd-rail-head {
padding: 16px 16px 12px;
}
.docdoe-student-os .dd-rail-scroll {
min-height: 0;
flex: 1;
overflow-y: auto;
padding: 4px 8px;
}
.docdoe-student-os .dd-rail-item {
display: flex;
width: 100%;
align-items: flex-start;
gap: 10px;
border-radius: var(--dd-r-md);
padding: 9px 10px;
text-align: left;
transition:
background-color var(--dd-t-fast) var(--dd-ease),
color var(--dd-t-fast) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-rail-item:hover:not([data-active="true"]) {
background: var(--dd-panel);
}
}
.docdoe-student-os .dd-rail-item[data-active="true"] {
background: var(--dd-ink);
color: #fff;
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.1);
}
/* ── Conversation ────────────────────────────────────────────────── */
/* The student's own message is the only inverted block in the thread. The
asymmetric corner is the tail: it points at the sender without an avatar
bubble competing with the assistant's mark. */
.docdoe-student-os .dd-bubble-user {
border-radius: 16px 16px 4px 16px;
background: var(--dd-ink);
color: #fff;
padding: 11px 15px;
font-size: 14px;
line-height: 24px;
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.08);
}
/* Assistant sections are ruled, not boxed: a multi-part answer reads as one
continuous document with an index in the margin. */
.docdoe-student-os .dd-answer-section {
border-top: 1px solid var(--dd-line);
padding: 16px 0;
}
.docdoe-student-os .dd-answer-section:last-of-type {
border-bottom: 1px solid var(--dd-line);
}
/* ── Prose ───────────────────────────────────────────────────────── */
/* Tutor output is markdown, so the reading measure and rhythm are set here
rather than per call site. Without this every answer re-invents spacing. */
.docdoe-student-os .dd-prose {
font-size: 14.5px;
line-height: 26px;
color: var(--dd-ink-2);
}
.docdoe-student-os .dd-prose > :first-child {
margin-top: 0;
}
.docdoe-student-os .dd-prose > :last-child {
margin-bottom: 0;
}
.docdoe-student-os .dd-prose p {
margin-block: 0 12px;
}
.docdoe-student-os .dd-prose strong {
font-weight: 600;
color: var(--dd-ink);
}
.docdoe-student-os .dd-prose h1,
.docdoe-student-os .dd-prose h2,
.docdoe-student-os .dd-prose h3,
.docdoe-student-os .dd-prose h4 {
margin-block: 18px 8px;
font-weight: 600;
letter-spacing: -0.022em;
color: var(--dd-ink);
}
.docdoe-student-os .dd-prose h3 {
font-size: 15px;
}
.docdoe-student-os .dd-prose ul,
.docdoe-student-os .dd-prose ol {
margin-block: 12px;
padding-left: 20px;
}
.docdoe-student-os .dd-prose ul {
list-style: disc;
}
.docdoe-student-os .dd-prose ol {
list-style: decimal;
}
.docdoe-student-os .dd-prose li {
margin-block: 5px;
}
.docdoe-student-os .dd-prose li::marker {
color: var(--dd-faint);
}
.docdoe-student-os .dd-prose code {
border-radius: 4px;
background: var(--dd-sunk);
padding: 1px 5px;
font-family: var(--font-mono), ui-monospace, monospace;
font-size: 0.9em;
}
.docdoe-student-os .dd-prose pre {
margin-block: 14px;
overflow-x: auto;
border-radius: var(--dd-r-md);
background: var(--dd-sunk);
padding: 12px 14px;
}
.docdoe-student-os .dd-prose pre code {
background: none;
padding: 0;
}
.docdoe-student-os .dd-prose blockquote {
margin-block: 14px;
border-left: 2px solid var(--dd-line-strong);
padding-left: 14px;
color: var(--dd-muted);
}
.docdoe-student-os .dd-prose a {
color: var(--dd-ink);
text-decoration: underline;
text-decoration-color: var(--dd-line-strong);
text-underline-offset: 3px;
transition: text-decoration-color var(--dd-t-fast) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-prose a:hover {
text-decoration-color: var(--dd-ink);
}
}
.docdoe-student-os .dd-prose table {
margin-block: 14px;
width: 100%;
border-collapse: collapse;
font-size: 13.5px;
}
.docdoe-student-os .dd-prose th,
.docdoe-student-os .dd-prose td {
border-bottom: 1px solid var(--dd-line);
padding: 7px 10px;
text-align: left;
}
.docdoe-student-os .dd-prose th {
font-weight: 600;
color: var(--dd-ink);
}
.docdoe-student-os .dd-prose hr {
margin-block: 18px;
border: 0;
border-top: 1px solid var(--dd-line);
}
/* ── Small actions ───────────────────────────────────────────────── */
/* Secondary actions under an answer. Outlined and quiet so the primary
reading path stays the answer itself. */
.docdoe-student-os .dd-action {
display: inline-flex;
height: 32px;
align-items: center;
gap: 6px;
border-radius: var(--dd-r-sm);
border: 1px solid var(--dd-line);
background: transparent;
padding-inline: 10px;
font-size: 12px;
font-weight: 500;
color: var(--dd-muted);
white-space: nowrap;
transition:
background-color var(--dd-t-fast) var(--dd-ease),
border-color var(--dd-t-fast) var(--dd-ease),
color var(--dd-t-fast) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-action:hover:not(:disabled) {
background: var(--dd-sunk);
border-color: var(--dd-line-strong);
color: var(--dd-ink);
}
}
.docdoe-student-os .dd-action:disabled {
opacity: 0.45;
cursor: not-allowed;
}
/* ── Removable chip ──────────────────────────────────────────────── */
.docdoe-student-os .dd-chip-remove {
display: inline-flex;
max-width: 220px;
align-items: center;
gap: 6px;
border-radius: var(--dd-r-sm);
background: var(--dd-sunk);
padding: 5px 8px;
font-size: 12px;
font-weight: 500;
color: var(--dd-ink-2);
transition:
background-color var(--dd-t-fast) var(--dd-ease),
color var(--dd-t-fast) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-chip-remove:hover {
background: var(--dd-ink);
color: #fff;
}
}
.docdoe-student-os .dd-chip-remove > svg {
flex-shrink: 0;
opacity: 0.55;
transition: opacity var(--dd-t-fast) var(--dd-ease);
}
.docdoe-student-os .dd-chip-remove:hover > svg {
opacity: 1;
}
/* ── Citation ────────────────────────────────────────────────────── */
/* Evidence rows. They must look like a source, not a button: a hairline
border that only strengthens on intent. */
.docdoe-student-os .dd-cite {
display: block;
border-radius: var(--dd-r-md);
border: 1px solid var(--dd-line);
background: var(--dd-panel);
padding: 10px 12px;
transition:
border-color var(--dd-t-fast) var(--dd-ease),
background-color var(--dd-t-fast) var(--dd-ease);
}
@media (hover: hover) and (pointer: fine) {
.docdoe-student-os .dd-cite:hover {
border-color: var(--dd-line-strong);
background: var(--dd-paper);
}
}
/* ── Route stepper ───────────────────────────────────────────────── */
/* A class is a sequence, so the steps get a real spine: one hairline
connecting the nodes rather than 5 identical boxed rows. */
.docdoe-student-os .dd-stepper {
position: relative;
}
.docdoe-student-os .dd-step {
position: relative;
display: flex;
align-items: flex-start;
gap: 12px;
padding-block: 13px;
}
.docdoe-student-os .dd-step + .dd-step {
border-top: 1px solid var(--dd-line);
}
.docdoe-student-os .dd-step-node {
display: grid;
place-items: center;
flex-shrink: 0;
border-radius: 999px;
border: 1px solid var(--dd-line-strong);
background: var(--dd-panel);
color: var(--dd-faint);
transition:
background-color var(--dd-t-base) var(--dd-ease),
border-color var(--dd-t-base) var(--dd-ease),
color var(--dd-t-base) var(--dd-ease);
}
.docdoe-student-os .dd-step[data-current="true"] .dd-step-node {
border-color: var(--dd-ink);
background: var(--dd-ink);
color: #fff;
}
/* ── Hero spine ──────────────────────────────────────────────────── */
/* The day's hero earns one piece of structure the rest of the page does
not get: an ink edge that marks it as the first thing to read. */
.docdoe-student-os .dd-spine {
position: relative;
}
.docdoe-student-os .dd-spine::before {
content: "";
position: absolute;
inset-block: 0;
left: 0;
width: 3px;
background: var(--dd-ink);
}
/* ── Board-detail strip ──────────────────────────────────────────── */
/* Hairline metadata strip on the inverted hero. Uses paper translucency,
never a chalk hue β€” chalk is reserved for actual board surfaces. */
.docdoe-student-os .dd-ink-rule {
height: 1px;
border: 0;
background: linear-gradient(
90deg,
rgb(255 255 255 / 0.22),
rgb(255 255 255 / 0.05) 60%,
transparent
);
}
@media (prefers-reduced-motion: reduce) {
.docdoe-student-os .dd-tick-just-done,
.docdoe-student-os .dd-arrow,
.docdoe-student-os .dd-mark,
.docdoe-student-os .dd-page-in,
.docdoe-student-os .dd-entry {
animation: none;
transform: none;
}
.docdoe-student-os .dd-meter > i {
transition: none;
}
.docdoe-student-os .dd-page-in,
.docdoe-student-os .dd-entry {
opacity: 1;
}
}