﻿/* SMDK Solutions — brand tokens + Fluent-style surface system.
Brand identity: deep teal + warm gold + ivory ("medical-tourism luxury
dental clinic"). Matches the strategic-plan mockups at /mockups/.
Earlier iteration used navy + Fluent blue + neutral grey — kept the
variable NAMES (--brand-navy, --brand-blue) on purpose so every
downstream consumer still resolves without rename; just the VALUES
shifted to the teal/gold palette.
Shell (sidebar / top bar / cards) stays light + neutral with ivory bg.
The teal + gold are reserved for brand moments: login, primary CTAs,
active nav state, accent rails, gradient highlights. */
:root {
/* —— Brand — teal/gold (palette per mockups/01-brand-moodboard) —— */
--brand-navy:        #0C3B3A;   /* teal-800: primary brand, CTAs */
--brand-navy-deep:   #062524;   /* teal-900: gradient deeps, footers */
--brand-blue:        #0F4E4C;   /* teal-700: secondary brand wash, links */
--brand-cyan:        #C9A977;   /* gold-500: warm accent (was electric cyan) */
--brand-cyan-soft:   #E6D5B0;   /* gold-300: focus-ring tint, soft pills */
/* Optional new aliases so callers can opt into "gold" directly. */
--brand-gold:        #C9A977;
--brand-gold-soft:   #E6D5B0;
--brand-gold-mist:   #F4ECD8;
--brand-teal-mist:   #E8F1F0;   /* nav-link active wash */
/* Legacy gradient aliases — now solid colors to keep Fluent flat aesthetic. */
--brand-gradient:      var(--brand-navy);
--brand-gradient-soft: var(--surface-active);
/* —— Surfaces — ivory page bg + warm-neutral cards —— */
--surface-page:    #FAFAF7;   /* page canvas — ivory, was #FAF9F8 */
--surface-card:    #FFFFFF;   /* elevated card */
--surface-sidebar: #FAFAF7;   /* sidebar rail — was light grey, now ivory */
--surface-topbar:  #FFFFFF;   /* top bar */
--surface-hover:   #F0EEE6;   /* nav-link hover — warm-neutral, was cool grey */
--surface-active:  #D5E5E3;   /* nav-link active wash — teal-100, bumped from
#E8F1F0 so the active sidebar item reads
clearly against the ivory page bg. */
--surface-elevated:#FFFFFF;   /* card variant with shadow-sm baked in */
--surface-muted:   #F0EEE6;   /* section backgrounds, list-row hover —
warm-neutral to match the rest of the
palette; was #F7F8FA cool-grey before
the H1.4 swap. */
/* —— Text scale —— */
--text-primary:    #0F1614;   /* near-black with green undertone, was #1B1B1F */
--text-secondary:  #5C6B68;   /* secondary text, labels */
--text-muted:      #9AA5A3;   /* hints, timestamps */
--text-on-brand:   #FFFFFF;   /* text on filled brand color */
/* —— Border scale — warmer neutrals —— */
--border-subtle:   #F0EEE6;   /* was #EDEBE9 (cool) */
--border-default:  #E5E2D9;   /* was #D2D0CE (cool) */
--border-strong:   #9AA5A3;   /* was #8A8886 */
/* —— Semantic aliases (legacy CSS consumes these) —— */
--color-primary:           var(--brand-navy);
--color-primary-accent:    var(--brand-gold);    /* gold for accents */
--color-btn-primary:       var(--brand-navy);    /* teal-800 — was Fluent blue */
--color-btn-primary-hover: var(--brand-blue);    /* teal-700 — was darker Fluent blue */
--color-top-bar:           var(--surface-topbar);
--background-color:        var(--surface-page);
--background-nav:          var(--surface-sidebar);
--background-nav-dark:     var(--surface-sidebar);
--color-primary-focus:     var(--brand-gold-soft);
--color-secondary:         var(--text-secondary);
--color-border:            var(--border-subtle);
--color-text:              var(--text-primary);
--color-soft-white:        #FFFFFF;
}
/* —— Spacing scale (kept — already solid) —— */
:root {
--space-xs: 0.25rem;   /* 4px */
--space-sm: 0.5rem;    /* 8px */
--space-md: 0.875rem;  /* 14px — tighter than 1rem */
--space-lg: 1.5rem;    /* 24px */
--space-lg-big: 2.5rem;
/* —— Radius scale — bumped to modern dental-SaaS standard
(Dentally/Curve/Praktika all sit in 8–12px range). The old 4px
button radius read as "Fluent 2018"; 10px reads as 2024. —— */
--radius-xs: 4px;      /* badges, chips */
--radius-sm: 6px;      /* inputs, small surfaces */
--radius-md: 10px;     /* buttons, cards (default) */
--radius-lg: 14px;     /* modals, large surfaces */
--radius-pill: 9999px; /* filter pills, status badges */
/* —— Shadow tiers — tuned with a teal tint (rgba 12,59,58) so
shadows feel cohesive with the deep-teal brand instead of the
cool-navy of the previous iteration. Same intensity steps. —— */
--shadow-none: 0 0 0 0 transparent;
--shadow-sm:   0 1px 2px rgba(12, 59, 58, 0.06);
--shadow-md:   0 4px 12px rgba(12, 59, 58, 0.10);
--shadow-lg:   0 12px 32px rgba(12, 59, 58, 0.16);
/* Focus ring — warm gold halo on the new palette. Replaces the
cyan halo from the navy iteration; same opacity so the visual
weight is unchanged. */
--shadow-focus: 0 0 0 3px rgba(201, 169, 119, 0.40);
/* —— Button height scale — new. The old codebase pinned every
button at 32px. Modern apps differentiate toolbar (28px),
default (36px), and primary-CTA (44px) — improves visual
hierarchy on dense screens. —— */
--btn-h-sm: 28px;
--btn-h-md: 36px;
--btn-h-lg: 44px;
}
/* —— Typography — system font stack, no external load needed —— */
:root {
--font-base:
-apple-system,
BlinkMacSystemFont,
'Segoe UI Variable',
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji';
--font-size: 0.95rem;          /* 15.2px — slightly tighter than 1rem */
--font-size-lg: 1.45rem;
--font-size-sm: 0.82rem;
--font-weight-normal: 400;
--font-weight-semibold: 600;
--font-weight-bold: 600;
--font-size-page-title: 1.5rem;  /* 24px — Fluent H1 size */
/* New weight aliases — modern stacks differentiate 500 (medium) from
600 (semibold). The old codebase only used 400/600; introducing 500
gives body text a slightly richer look without going full bold. */
--fw-regular:  400;
--fw-medium:   500;
--fw-semibold: 600;
--fw-bold:     700;
}
/* —— Card surface aliases —— */
:root {
--color-card-header: var(--surface-card);
--color-card:        var(--surface-card);
--color-card-hover:  #F8F8F8;
}
/* —— Tables —— */
:root {
--color-table-header:  #F3F2F1;
--color-table-row-alt: #FAF9F8;
}
/* App shell — Fluent-style light surface, mobile-first drawer sidebar. */
body {
margin: 0;
padding: 0;
background-color: var(--surface-page);
font-family: var(--font-base) !important;
color: var(--text-primary);
font-size: var(--font-size);
line-height: 1.45;
-webkit-font-smoothing: antialiased;
}
/* —— Page grid —— */
.page {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.page > main {
flex: 1 1 auto;
min-width: 0;
display: flex;
flex-direction: column;
}
/* —— Sidebar — white surface with hairline border (modern dental-SaaS).
The old grey panel read as "Outlook 2003"; switching to white +
subtle right border + soft shadow gives the app a cleaner shell. —— */
.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 260px;
background-color: var(--surface-card) !important;
background-image: none !important;
border-right: 1px solid var(--border-subtle);
z-index: 50;
transform: translateX(-100%);
transition: transform 0.22s ease, box-shadow 0.22s ease, width 0.18s ease;
overflow-x: hidden;
overflow-y: hidden;
display: flex;
flex-direction: column;
box-shadow: var(--shadow-sm);
box-sizing: border-box;
}
.sidebar * { box-sizing: border-box; }
.page.sidebar-open .sidebar {
transform: translateX(0);
box-shadow: 0 8px 32px rgba(15, 39, 69, 0.18);
}
.sidebar-backdrop {
position: fixed;
inset: 0;
background: rgba(15, 39, 69, 0.32);
z-index: 40;
opacity: 0;
pointer-events: none;
transition: opacity 0.18s ease;
}
.page.sidebar-open .sidebar-backdrop {
opacity: 1;
pointer-events: auto;
}
/* —— Top bar — taller (56px), soft shadow on scroll instead of hard
border. Modern dental-SaaS shells use a thicker top strip so global
search and the user menu have room to breathe. —— */
.top-row.navbar { padding: 0 1.25rem; }
.top-row {
height: 56px;
display: flex;
align-items: center;
gap: 0.85rem;
padding: 0 1.25rem;
background-color: var(--surface-topbar) !important;
color: var(--text-primary);
border-bottom: 1px solid var(--border-subtle);
position: sticky;
top: 0;
z-index: 20;
box-shadow: var(--shadow-sm);
}
.top-row .top-row-mark {
height: 26px;
width: 26px;
object-fit: contain;
}
.top-row .top-row-brand {
font-weight: 600;
font-size: 0.95rem;
color: var(--text-primary);
letter-spacing: 0;
}
.top-row .top-row-brand em {
font-style: normal;
font-weight: 400;
letter-spacing: 1.4px;
text-transform: uppercase;
font-size: 0.6rem;
color: var(--text-secondary);
margin-left: 4px;
}
/* —— Brand chip — the top-left brand area is now a single clickable
affordance routing to /dashboard. We wrap the mark + name in an
anchor so users get a clear hover state (subtle background, soft
radius) — the old static text gave no cue it was clickable. —— */
.top-row .top-row-brand-chip {
display: inline-flex;
align-items: center;
gap: 0.55rem;
padding: 4px 10px;
margin: 0 -4px;
border-radius: var(--radius-md);
text-decoration: none;
color: inherit;
transition:
background-color 0.15s ease,
box-shadow 0.15s ease;
}
.top-row .top-row-brand-chip:hover {
background: var(--surface-muted);
}
.top-row .top-row-brand-chip:focus-visible {
outline: none;
background: var(--surface-muted);
box-shadow: var(--shadow-focus);
}
.top-row .top-row-brand-chip:active {
background: var(--surface-hover);
}
.top-row .top-row-spacer { flex: 1 1 auto; }
.top-row .btn-hamburger {
background: transparent;
color: var(--text-primary);
border: 1px solid transparent;
border-radius: var(--radius-md);
width: 36px;
height: 36px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.15s ease;
}
.top-row .btn-hamburger:hover {
background: var(--surface-muted);
}
.top-row .btn-logout {
color: var(--text-primary);
border: 1px solid var(--border-default);
background: transparent;
margin: 0;
height: 32px;
font-size: 0.85rem;
text-align: center;
padding: 0 0.85rem;
border-radius: var(--radius-sm);
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.top-row .btn-logout:hover {
background: var(--surface-hover);
border-color: var(--border-strong);
}
/* —— Article content —— */
article {
padding: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
/* —— Desktop overrides —— */
@media (min-width: 768px) {
.page {
flex-direction: row;
}
.sidebar {
position: sticky;
height: 100vh;
transform: none !important;
box-shadow: none !important;
width: 240px;
flex-shrink: 0;
}
.sidebar-backdrop { display: none; }
.top-row .btn-hamburger { display: none; }
/* Company logo + brand live in the top bar on every breakpoint now
(used to be sidebar-only on desktop). Sidebar shows only the
collapse button + nav icons. */
article {
padding: 1.5rem 2rem;
}
}
/* —— Sidebar header (brand + collapse toggle) ——
Notion / Claude / Linear pattern: workspace identity at the top,
subtle collapse chevron right-aligned.  On collapse the header
stacks vertically so the logo mark + expand icon fit in 64 px. */
.sidebar-header {
display: flex;
align-items: center;
min-height: 56px;                          /* match top-row exactly */
height: 56px;
flex-shrink: 0;                            /* never compress — flex siblings must yield */
padding: 0 1rem;
border-bottom: 1px solid var(--border-subtle);
background: transparent;
gap: 0.5rem;
box-sizing: border-box;
}
/* Legacy selector kept for any residual Bootstrap navbar rules that
may target .navbar inside .sidebar — neutralise them. */
.sidebar .top-row,
.sidebar .navbar {
padding: 0.85rem 1rem;
background: transparent !important;
border-bottom: 1px solid var(--border-subtle);
position: static;
box-shadow: none;
height: auto;
}
/* —— Sidebar navbar (brand header) —— */
.navbar {
background-color: transparent !important;
z-index: 15 !important;
padding: 0.85rem 1rem;
border-bottom: 1px solid var(--border-subtle);
}
.navbar-brand {
display: flex;
align-items: center;
gap: 0.55rem;
color: var(--text-primary) !important;
text-decoration: none !important;
font-weight: 600;
letter-spacing: 0;
padding: 0;
}
.navbar-brand:hover { opacity: 0.85; }
.navbar-brand .brand-mark {
height: 32px;
width: 32px;
object-fit: contain;
flex-shrink: 0;
}
.navbar-brand .brand-text {
font-size: 0.95rem;
line-height: 1.1;
color: var(--text-primary);
}
.navbar-brand .brand-text em {
font-style: normal;
font-weight: 400;
letter-spacing: 1.6px;
text-transform: uppercase;
font-size: 0.58rem;
display: block;
margin-top: 2px;
color: var(--text-secondary);
}
/* —— Sidebar nav links —— */
.nav-scrollable {
padding: 0.4rem 0.5rem;
flex: 1 1 auto;
overflow-y: auto;
overflow-x: hidden;  /* prevent CSS-spec auto-promotion when overflow-y is set */
min-width: 0;
}
/* —— Nav links — pill highlight on active, soft hover wash on rest.
Dense, information-rich layout (Claude / Linear density). Active
state uses brand-blue text + soft bg wash — no accent rail. —— */
.nav-link {
color: var(--text-primary) !important;
border-radius: var(--radius-md);                /* 10px */
transition:
background-color 0.15s ease,
color 0.15s ease,
transform 0.08s ease;
margin: 1px 0;
padding: 0.45rem 0.75rem !important;
display: flex !important;
align-items: center;
gap: 0.65rem;
font-size: 0.875rem;
font-weight: 400;                               /* regular — active gets 500 */
}
.nav-link:hover {
background-color: var(--surface-muted);
color: var(--text-primary) !important;
}
.nav-link:active {
transform: translateY(0.5px);
}
/* Colour the icon slightly so the nav reads like Fluent. */
.nav-link > .fa-solid,
.nav-link > .fa-regular,
.nav-link > .fa-brands {
width: 18px;
text-align: center;
color: var(--text-secondary);
font-size: 1rem;
flex-shrink: 0;
transition: color 0.15s ease;
}
.nav-link:hover > .fa-solid,
.nav-link:hover > .fa-regular,
.nav-link:hover > .fa-brands {
color: var(--brand-blue);
}
.nav-link.active {
background-color: var(--surface-active) !important;
color: var(--brand-blue) !important;
font-weight: var(--fw-medium, 500);          /* one step above inactive 400 */
}
.nav-link.active > .fa-solid,
.nav-link.active > .fa-regular,
.nav-link.active > .fa-brands {
color: var(--brand-blue);
}
/* Left-edge accent rail removed — Claude / Linear / Notion use
background-only active state.  Pure bg wash reads cleaner. */
.nav-item { position: relative; }
/* —— Company brand variants (top-row legacy) —— */
.top-row .company-mark {
background: #fff;
border: 1.5px solid var(--border-default);
border-radius: var(--radius-md);
padding: 3px;
width: 36px;
height: 36px;
object-fit: contain;
box-shadow:
0 1px 3px rgba(12, 59, 58, 0.08),
0 0 0 0.5px rgba(12, 59, 58, 0.06);
}
.top-row .top-row-initial,
.brand-initial {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: var(--radius-md);
background: linear-gradient(135deg, var(--brand-blue) 0%, var(--brand-navy) 100%);
color: var(--text-on-brand);
font-weight: var(--fw-semibold, 600);
font-size: 0.95rem;
flex-shrink: 0;
box-shadow: var(--shadow-sm);
}
.top-row .company-brand {
font-weight: 600;
font-size: 0.95rem;
color: var(--text-primary);
max-width: 50vw;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Sidebar header (desktop): tenant logo */
.navbar.company-header,
.sidebar-header.company-header {
background-color: transparent !important;
}
/* —— Clinic workspace icon — self-contained avatar that reads as its
own element regardless of sidebar background. Notion / Slack / Linear
all frame the workspace mark in a clear, lifted container. ———————— */
.navbar-brand .company-mark {
background: #fff;
border: 1.5px solid var(--border-default);
padding: 4px;
border-radius: var(--radius-md);            /* 10px — rounded square */
width: 38px;
height: 38px;
object-fit: contain;
box-shadow:
0 1px 3px rgba(12, 59, 58, 0.08),      /* teal-tinted lift */
0 0 0 0.5px rgba(12, 59, 58, 0.06);    /* hairline ring */
flex-shrink: 0;
transition: box-shadow 0.15s ease, border-color 0.15s ease;
}
.navbar-brand:hover .company-mark {
border-color: var(--brand-blue);
box-shadow:
0 2px 6px rgba(12, 59, 58, 0.12),
0 0 0 0.5px rgba(12, 59, 58, 0.10);
}
.navbar-brand .brand-initial {
width: 38px;
height: 38px;
border-radius: var(--radius-md);            /* match company-mark */
font-size: 1rem;
}
.navbar-brand .company-brand-text {
font-size: 0.95rem;
line-height: 1.2;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
/* —— Sidebar footer (user menu + collapse toggle + Powered by) ———————— */
.sidebar .sidebar-footer {
margin-top: auto;
display: flex;
flex-direction: column;
}
/* User menu wrapper inside the sidebar footer. The component itself
was originally designed for the top bar (compact pill with dropdown
that opens downward). In the sidebar context we want a full-width
row that flows with the nav, plus a panel that pops UPWARD so it
doesn't get clipped by the viewport bottom. */
.sidebar .sidebar-user {
padding: 0.5rem 0.65rem 0.5rem;
border-top: 1px solid var(--border-subtle);
}
.sidebar .sidebar-user .user-menu {
display: flex;
width: 100%;
}
.sidebar .sidebar-user .user-menu-trigger {
width: 100%;
background: transparent;
border: 1px solid transparent;
padding: 0.5rem 0.65rem;
border-radius: var(--radius-md);
height: auto;
justify-content: flex-start;
}
.sidebar .sidebar-user .user-menu-trigger:hover {
background: var(--surface-muted);
border-color: transparent;
}
.sidebar .sidebar-user .user-trigger-label {
flex: 1 1 auto;
text-align: left;
max-width: none;
}
.sidebar .sidebar-user .user-trigger-caret {
margin-left: auto;
}
/* Panel pops UPWARD so the dropdown isn't clipped by the viewport
bottom; aligned to the left edge of the trigger so it stays inside
the sidebar column. */
.sidebar .sidebar-user .user-menu-panel {
top: auto;
bottom: calc(100% + 6px);
left: 0;
right: auto;
min-width: 240px;
}
/* Collapsed sidebar: shrink the user-menu trigger to icon-only
(avatar in the centre, hide label + caret). */
.page.app--nav-collapsed .sidebar-user .user-trigger-label,
.page.app--nav-collapsed .sidebar-user .user-trigger-caret { display: none; }
.page.app--nav-collapsed .sidebar-user .user-menu-trigger { justify-content: center; padding: 0.4rem; }
/* —— Powered by footer —— */
.sidebar .powered-by {
padding: 0.75rem 1rem 0.85rem;
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.7rem;
color: var(--text-muted);
border-top: 1px solid var(--border-subtle);
letter-spacing: 0;
}
.sidebar .powered-by .powered-by-label {
font-size: 0.62rem;
text-transform: uppercase;
letter-spacing: 1.2px;
color: var(--text-muted);
}
.sidebar .powered-by .powered-by-mark {
height: 14px;
width: 14px;
object-fit: contain;
opacity: 0.9;
}
.sidebar .powered-by .powered-by-brand {
font-weight: 600;
font-size: 0.72rem;
color: var(--text-primary);
}
.sidebar .powered-by .powered-by-brand em {
font-style: normal;
font-weight: 400;
letter-spacing: 1.2px;
text-transform: uppercase;
font-size: 0.55rem;
color: var(--text-secondary);
margin-left: 3px;
}
/* ===== Sidebar collapse (desktop only) — body class .app--nav-collapsed ===== */
/* The toggle button is now `.btn-sidebar-toggle` inside `.sidebar-header`.
Old `.btn-nav-collapse` (top-bar) is no longer used — removed. */
/* ===== Sidebar internals — separator + collapse button + polish ===== */
/* Group separator between nav sections.
Modernised away — the sidebar is now a single flat list (Claude /
Linear / Arc style). Keeps the data entries in NavMenu.razor so the
ordering can still encode IA, but the visual rule is suppressed.
To bring grouping back, swap display:none for the previous height/
background pair. */
.nav-separator { display: none; }
/* Nav icon: fixed width so the left edge of the labels aligns across rows */
.nav-link .nav-icon {
width: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.95rem;
margin-right: 0.6rem;
flex-shrink: 0;
color: var(--text-secondary);
}
.nav-link.active .nav-icon { color: var(--brand-blue); }
.nav-link:hover .nav-icon  { color: var(--brand-blue); }
/* —— Sidebar header collapse toggle (Notion «/» pattern) ——————————
Small icon button right-aligned in the header row. Hidden on mobile
(mobile uses the off-canvas hamburger in the top bar). */
.btn-sidebar-toggle {
appearance: none;
background: transparent;
border: 0;
color: var(--text-muted);
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
display: none;                 /* hidden on mobile */
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 0.72rem;
flex-shrink: 0;
transition: background-color 0.15s ease, color 0.15s ease;
margin-left: auto;
}
.btn-sidebar-toggle:hover {
background: var(--surface-muted);
color: var(--text-primary);
}
.btn-sidebar-toggle:focus-visible {
outline: none;
box-shadow: var(--shadow-focus);
color: var(--text-primary);
}
@media (min-width: 768px) {
.btn-sidebar-toggle { display: inline-flex; }
}
/* ====== Collapsed-state polish ====== */
@media (min-width: 768px) {
.page.app--nav-collapsed .sidebar { width: 64px; }
/* Header: row layout in 64px rail — logo left, toggle right,
locked to 56px so the hairline aligns with the top bar. */
.page.app--nav-collapsed .sidebar-header {
flex-direction: row;
height: 56px;
min-height: 56px;
gap: 0;
padding: 0 4px;
align-items: center;
justify-content: space-between;
}
.page.app--nav-collapsed .sidebar-header .btn-sidebar-toggle {
margin-left: 0;
}
/* Centred, larger-target icons; left active-rail */
.page.app--nav-collapsed .nav-link {
justify-content: center;
padding: 0.55rem 0 !important;
position: relative;
}
.page.app--nav-collapsed .nav-link .nav-icon {
width: 28px;
height: 28px;
margin: 0;
font-size: 1.05rem;
border-radius: var(--radius-sm);
}
.page.app--nav-collapsed .nav-link:hover .nav-icon {
background: var(--surface-hover);
}
/* Accent rail removed — bg-only active state in both expanded + collapsed */
.page.app--nav-collapsed .nav-link.active .nav-icon {
background: var(--surface-active);
color: var(--brand-blue);
}
.page.app--nav-collapsed .nav-link-text { display: none; }
/* Hide brand text but keep mark */
.page.app--nav-collapsed .navbar-brand .brand-text,
.page.app--nav-collapsed .navbar-brand .company-brand-text { display: none; }
.page.app--nav-collapsed .navbar-brand { justify-content: center; }
/* Footer: simplified (no collapse button) — powered-by hides text */
.page.app--nav-collapsed .powered-by .powered-by-label,
.page.app--nav-collapsed .powered-by .powered-by-brand { display: none; }
.page.app--nav-collapsed .powered-by { justify-content: center; padding: 0.55rem 0; }
/* Tighter separators when collapsed */
.page.app--nav-collapsed .nav-separator { margin: 0.4rem 0.75rem; }
/* Hover tooltip surfaces the page name when collapsed */
.page.app--nav-collapsed .nav-link[title]:hover::after {
content: attr(title);
position: absolute;
left: calc(100% + 8px);
top: 50%;
transform: translateY(-50%);
background: var(--brand-navy);
color: #fff;
padding: 4px 10px;
border-radius: var(--radius-sm);
font-size: 0.8rem;
font-weight: 500;
white-space: nowrap;
pointer-events: none;
z-index: 100;
box-shadow: 0 6px 16px rgba(0,0,0,0.15);
}
}
/* ===== Global search / command palette (top bar) — pill input ===== */
.global-search {
position: relative;
display: flex;
align-items: center;
height: 38px;
background: var(--surface-muted);
border: 1px solid transparent;
border-radius: var(--radius-pill, 9999px);
padding: 0 0.85rem;
margin: 0 0.4rem;
flex: 1 1 auto;
max-width: 480px;
transition:
border-color 0.15s ease,
box-shadow 0.15s ease,
background-color 0.15s ease;
}
.global-search:hover {
background: var(--surface-hover);
}
.global-search:focus-within,
.global-search.is-open {
border-color: var(--brand-blue);
background: var(--surface-card);
box-shadow: var(--shadow-focus);
}
.global-search-icon {
color: var(--text-secondary);
font-size: 0.85rem;
margin-right: 0.5rem;
flex-shrink: 0;
}
.global-search-input {
border: 0;
outline: 0;
background: transparent;
color: var(--text-primary);
font-size: 0.88rem;
flex: 1 1 auto;
min-width: 0;
padding: 0;
height: 100%;
}
.global-search-input::placeholder {
color: var(--text-muted);
font-size: 0.85rem;
}
/* Hide the browser's built-in clear button for type=search */
.global-search-input::-webkit-search-cancel-button { display: none; }
.global-search-clear {
background: transparent;
border: 0;
color: var(--text-muted);
cursor: pointer;
padding: 0 0.25rem;
font-size: 0.85rem;
}
.global-search-clear:hover { color: var(--text-primary); }
.global-search-panel {
position: absolute;
top: calc(100% + 6px);
left: 0;
right: 0;
background: var(--surface-card);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
box-shadow:
0 1px 2px rgba(15, 39, 69, 0.06),
0 20px 40px -12px rgba(15, 39, 69, 0.22);
z-index: 60;
max-height: 70vh;
overflow-y: auto;
padding: 0.35rem 0;
}
.global-search-section {
padding: 0.55rem 0.8rem 0.25rem;
font-size: 0.68rem;
font-weight: 700;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
}
.global-search-item {
width: 100%;
background: transparent;
border: 0;
text-align: left;
color: var(--text-primary);
cursor: pointer;
padding: 0.5rem 0.8rem;
display: grid;
grid-template-columns: 22px 1fr auto;
align-items: center;
gap: 0.6rem;
font-size: 0.9rem;
transition: background-color 0.08s ease;
}
.global-search-item.is-active,
.global-search-item:hover {
background: var(--surface-active);
}
.global-search-item-icon {
color: var(--text-secondary);
font-size: 0.9rem;
text-align: center;
}
.global-search-item.is-active .global-search-item-icon,
.global-search-item:hover .global-search-item-icon { color: var(--brand-blue); }
.global-search-item-label { color: var(--text-primary); }
.global-search-item-hint {
font-size: 0.78rem;
color: var(--text-muted);
}
.global-search-empty {
padding: 0.9rem 1rem;
color: var(--text-secondary);
font-size: 0.88rem;
}
.global-search-foot {
display: flex;
gap: 1rem;
padding: 0.45rem 0.8rem;
border-top: 1px solid var(--border-subtle);
background: var(--surface-page);
font-size: 0.72rem;
color: var(--text-secondary);
}
.global-search-foot kbd {
background: var(--surface-card);
border: 1px solid var(--border-default);
border-radius: 4px;
padding: 0 5px;
margin-right: 3px;
font-size: 0.7rem;
color: var(--text-primary);
font-family: var(--font-base);
}
/* Hide the palette on mobile to keep the top bar compact; users can use
the hamburger nav. Show from 768px up. */
@media (max-width: 767px) {
.global-search { display: none; }
}
/* Fix: collapsed logo sizing.  Keep the self-contained card frame so
the workspace icon reads clearly even in the narrow 64px rail. */
@media (min-width: 768px) {
/* Generic brand-mark (SMDK global logo) — strip container */
.page.app--nav-collapsed .navbar-brand .brand-mark {
width: 36px;
height: 36px;
max-width: 36px;
max-height: 36px;
padding: 0;
background: transparent;
border: 0;
box-shadow: none;
object-fit: contain;
display: block;
margin: 0 auto;
}
/* Company logo — KEEP the self-contained card frame. Must come
AFTER .brand-mark because the <img> has both classes. */
.page.app--nav-collapsed .navbar-brand .company-mark {
width: 28px;
height: 28px;
max-width: 28px;
max-height: 28px;
padding: 2px;
background: #fff;
border: 1.5px solid var(--border-default);
border-radius: var(--radius-sm);
box-shadow:
0 1px 3px rgba(12, 59, 58, 0.08),
0 0 0 0.5px rgba(12, 59, 58, 0.06);
object-fit: contain;
display: block;
margin: 0;
}
.page.app--nav-collapsed .navbar-brand .brand-initial { width: 28px; height: 28px; font-size: 0.8rem; }
.page.app--nav-collapsed .powered-by .powered-by-mark { width: 18px; height: 18px; max-width: 18px; max-height: 18px; }
.page.app--nav-collapsed .sidebar .navbar { padding: 0.6rem 0; }
/* sidebar-header collapsed padding set above in the collapsed-state block */
.page.app--nav-collapsed .powered-by { padding: 0.45rem 0; }
}
/* ===== User menu (top bar) ===== */
.user-menu {
position: relative;
display: inline-flex;
align-items: center;
}
.user-menu-trigger {
appearance: none;
background: var(--surface-muted);
border: 1px solid transparent;
border-radius: var(--radius-pill, 9999px);
padding: 3px 14px 3px 3px;
height: 40px;
display: inline-flex;
align-items: center;
gap: 0.6rem;
cursor: pointer;
color: var(--text-primary);
transition:
background-color 0.15s ease,
border-color 0.15s ease,
box-shadow 0.15s ease;
}
.user-menu-trigger:hover {
background: var(--surface-hover);
border-color: var(--border-default);
}
.user-menu-trigger:focus-visible {
outline: none;
box-shadow: var(--shadow-focus);
border-color: var(--brand-blue);
}
.user-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: linear-gradient(135deg, var(--brand-blue) 0%, var(--brand-navy) 100%);
color: #fff;
font-weight: var(--fw-bold, 700);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.85rem;
box-shadow: var(--shadow-sm);
}
.user-trigger-label {
font-size: 0.85rem;
font-weight: 500;
max-width: 140px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.user-trigger-caret {
font-size: 0.7rem;
color: var(--text-secondary);
}
@media (max-width: 640px) {
.user-trigger-label,
.user-trigger-caret { display: none; }
.user-menu-trigger { padding: 3px; }
}
.user-menu-backdrop {
position: fixed;
inset: 0;
background: transparent;
z-index: 29;
}
.user-menu-panel {
position: absolute;
top: calc(100% + 8px);
right: 0;
min-width: 240px;
background: var(--surface-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
z-index: 30;
padding: 0.5rem 0;
}
.user-menu-head {
display: flex;
align-items: center;
gap: 0.7rem;
padding: 0.7rem 0.9rem;
}
.user-menu-avatar-lg {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--brand-blue);
color: #fff;
font-weight: 700;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1rem;
flex-shrink: 0;
}
.user-menu-name {
font-weight: 600;
color: var(--text-primary);
font-size: 0.9rem;
}
.user-menu-email {
font-size: 0.78rem;
color: var(--text-secondary);
word-break: break-all;
}
.user-menu-role {
font-size: 0.7rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.06em;
margin-top: 0.15rem;
}
/* —— Role pill — replaces the tiny uppercase role label inside the
user menu. Each role gets a distinct colour + icon so authority
reads instantly:
GlobalAdmin  = gold/amber, shield icon  (top-tier authority)
CompanyAdmin = brand blue, user-tie icon (tenant lead)
CompanyUser  = neutral grey, user icon  (default member)
*/
.role-pill {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.15rem 0.55rem;
margin-top: 0.2rem;
border-radius: var(--radius-pill, 9999px);
font-size: 0.72rem;
font-weight: 600;
line-height: 1.4;
white-space: nowrap;
border: 1px solid transparent;
}
.role-pill > .fa-solid,
.role-pill > .fa-regular {
font-size: 0.7rem;
line-height: 1;
}
.role-pill.role-pill-global {
background: #FFF6E2;
color: #8A6500;
border-color: rgba(138, 101, 0, 0.18);
}
.role-pill.role-pill-admin {
background: var(--surface-active);
color: var(--brand-blue);
border-color: rgba(0, 120, 212, 0.22);
}
.role-pill.role-pill-user {
background: var(--surface-muted);
color: var(--text-secondary);
border-color: var(--border-subtle);
}
.user-menu-divider {
height: 1px;
background: var(--border-subtle);
margin: 0.35rem 0;
}
.user-menu-item {
background: transparent;
border: 0;
width: 100%;
padding: 0.55rem 0.9rem;
display: flex;
align-items: center;
gap: 0.6rem;
color: var(--text-primary);
font-size: 0.88rem;
cursor: pointer;
text-align: left;
}
.user-menu-item:hover { background: var(--surface-hover); }
.user-menu-item-danger { color: #C42B1C; }
.user-menu-item-danger:hover { background: #FDE7E9; }
/* reset.css */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* typography.css */
body {
font-family: var(--font-base);
font-size: var(--font-size);
line-height: 1.5;
color: #333;
}
.page-title {
font-size: var(--font-size-lg, 1.5rem);
font-weight: 600;
}
.error-msg {
font-size: 1rem;
font-weight: var(--font-weight--bold);
color: red;
}
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.text-align-center {
text-align: center;
}
.text-align-end {
text-align: end;
}
/* FONT SIZES */
.fs-1 {
font-size: 1rem !important;
font-weight: 200;
}
.fs-2 {
font-size: 1.5rem !important;
font-weight: 400;
}
.fs-3 {
font-size: 2rem !important;
}
.fs-4 {
font-size: 2.5rem !important;
font-weight: 600;
}
/* TEXT COLORS */
.text-gray {
color: grey;
}
/* Buttons — modern dental-SaaS style.
10px radius, subtle elevation on primary, ghost/outline variants for
the secondary hierarchy. Inspired by Dentally / Curve Dental — every
button reads "clinical calm, brand-accent only where it matters". */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;                          /* tighter icon+label gap */
font-weight: var(--fw-semibold, 600);
font-size: 0.88rem;
text-align: center;
vertical-align: middle;
user-select: none;
padding: 0 1rem;
line-height: 1.4;
border-radius: var(--radius-md);      /* 10px — was 4px */
border: 1px solid transparent;
cursor: pointer;
background: transparent;
color: var(--text-primary);
min-height: var(--btn-h-md, 36px);    /* 36px default — was 32px */
transition:
background-color 0.15s ease,
color 0.15s ease,
border-color 0.15s ease,
box-shadow 0.15s ease,
transform 0.08s ease;
}
.btn .fa-solid, .btn .fa-regular, .btn .fa-brands {
font-size: 0.95em;
line-height: 1;
}
.btn:active { transform: translateY(1px); }
/* —— Size variants —— */
.btn-sm { min-height: var(--btn-h-sm, 28px); padding: 0 0.75rem; font-size: 0.82rem; border-radius: var(--radius-sm); }
.btn-lg { min-height: var(--btn-h-lg, 44px); padding: 0 1.4rem;  font-size: 0.95rem; }
/* —— Icon-only (compact toolbars, table-row actions) —— */
.btn-icon {
min-height: var(--btn-h-md, 36px);
width: var(--btn-h-md, 36px);
padding: 0;
border-radius: var(--radius-md);
}
.btn-icon.btn-sm { min-height: var(--btn-h-sm, 28px); width: var(--btn-h-sm, 28px); }
.btn-icon.btn-lg { min-height: var(--btn-h-lg, 44px); width: var(--btn-h-lg, 44px); }
/* —— Primary — solid Fluent blue with shadow lift on hover.
Used for the single most important action on a screen.
New visual: shadow-sm at rest, shadow-md on hover, subtle press. —— */
.btn-primary {
color: var(--text-on-brand);
background-color: var(--color-btn-primary);
border: 1px solid transparent;
box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
color: var(--text-on-brand);
background-color: var(--color-btn-primary-hover);
border-color: transparent;
box-shadow: var(--shadow-md);
}
.btn-primary:disabled,
.btn-primary[disabled] {
background: var(--border-default);
color: var(--text-muted);
cursor: not-allowed;
box-shadow: none;
}
/* —— Secondary — outlined neutral, slight tint on hover.
New variant. Maps to "Back / Cancel / passive secondary action".
Visually distinct from ghost (which has no border). —— */
.btn-secondary {
color: var(--text-primary);
background: var(--surface-card);
border: 1px solid var(--border-default);
box-shadow: var(--shadow-sm);
}
.btn-secondary:hover {
background: var(--surface-muted);
border-color: var(--border-strong);
color: var(--text-primary);
box-shadow: var(--shadow-md);
}
.btn-secondary:disabled,
.btn-secondary[disabled] {
background: var(--surface-muted);
color: var(--text-muted);
border-color: var(--border-subtle);
cursor: not-allowed;
box-shadow: none;
}
/* —— Ghost — transparent, no border, hover surface tint.
For tertiary actions in toolbars where the visual weight of
an outline button would be too much. —— */
.btn-ghost,
.btn-logout {
color: var(--text-primary);
border: 1px solid transparent;
background: transparent;
}
.btn-ghost:hover,
.btn-logout:hover {
background: var(--surface-muted);
border-color: transparent;
color: var(--text-primary);
}
/* —— Disabled — ghost buttons used to keep their full-strength
text colour even when disabled, so the Result page action bar's
Copy / WhatsApp buttons looked clickable while the underlying
_viewerUrl was still loading. Match the secondary/primary
disabled treatment: muted text, not-allowed cursor. —— */
.btn-ghost:disabled,
.btn-ghost[disabled],
.btn-logout:disabled,
.btn-logout[disabled] {
color: var(--text-muted);
cursor: not-allowed;
background: transparent;
}
.btn-ghost:disabled:hover,
.btn-ghost[disabled]:hover,
.btn-logout:disabled:hover,
.btn-logout[disabled]:hover {
background: transparent;
color: var(--text-muted);
}
/* —— Danger — for destructive confirms (delete, discard, sign out).
Subdued red, only used when the action is irreversible. —— */
.btn-danger {
color: var(--text-on-brand);
background: #C42B1C;
border: 1px solid transparent;
box-shadow: var(--shadow-sm);
}
.btn-danger:hover {
background: #9B1F13;
color: var(--text-on-brand);
box-shadow: var(--shadow-md);
}
/* —— Focus ring — replaces the 2px solid cyan with a softer 3px halo
(matches Tailwind/Radix/Fluent 2). Works on all variants. —— */
.btn:focus-visible,
.btn:focus:not(:active) {
outline: none;
box-shadow: var(--shadow-focus), var(--shadow-sm);
}
.btn-primary:focus-visible {
box-shadow: var(--shadow-focus), var(--shadow-md);
}
/* —— Action-bar grouping — when buttons sit in a row at the top of a
page (the strip in your screenshot), they live inside a flex
container with gap-sm. Use .action-bar on the parent. —— */
.action-bar {
display: flex;
align-items: center;
gap: var(--space-sm, 0.5rem);
flex-wrap: wrap;
}
.action-bar .spacer { flex: 1; }   /* push subsequent buttons to the right */
/* —— Mobile: the Result page action bar has 7+ buttons (Home, Back,
New, View, Copy, WhatsApp, Send). On a narrow viewport flex-wrap
pushes them to multiple rows, but the .spacer between left-nav
and right-actions then eats a full row of vertical space and
leaves the buttons visually disconnected. Collapse the spacer
below 640px so the buttons flow as a continuous wrapped group. —— */
@media (max-width: 640px) {
.action-bar .spacer { display: none; }
}
/* —— Side-button group (template-v2 toolbox) — keep existing sizing
but inherit new radius + focus ring. —— */
.side-button-group {
padding: 0.5rem;
padding-bottom: 0;
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 0.4rem;
}
.side-button {
width: 2rem;
height: 2rem;
border-radius: var(--radius-sm);
}
.unselected-side-button {
background-color: transparent;
color: var(--text-primary);
border: 1px solid var(--border-default);
}
/* Cards — modern dental-SaaS surface. 10px radius matches buttons,
shadow-sm at rest, shadow-md on hover for interactive variants.
Replaces the older 6px-radius / 1px-shadow Fluent flat treatment. */
.card {
position: relative;
display: flex;
flex-direction: column;
flex-grow: 1;
background-color: var(--surface-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
overflow: hidden;
min-width: 0;
height: fit-content;
padding: 1rem;                          /* was 0.875rem — bumped */
transition:
border-color 0.15s ease,
box-shadow 0.15s ease,
transform 0.08s ease;
}
/* Hover lift only on opted-in cards (list items, dashboard tiles). */
.card.card-interactive {
cursor: pointer;
}
.card.card-interactive:hover {
border-color: var(--border-default);
box-shadow: var(--shadow-md);
transform: translateY(-1px);
}
.card.card-interactive:active {
transform: translateY(0);
box-shadow: var(--shadow-sm);
}
/* Elevated variant — used when a card needs to feel above the page
(modals, sticky toolbars, floating panels). */
.card-elevated {
box-shadow: var(--shadow-md);
border-color: var(--border-subtle);
}
/* Section header inside a card. */
.card-header {
background-color: transparent;
padding: 0 0 0.65rem 0;
margin: 0 0 0.65rem 0;
border-bottom: 1px solid var(--border-subtle);
font-weight: var(--fw-semibold, 600);
text-align: left;
color: var(--text-primary);
font-size: 0.95rem;
display: flex;
align-items: center;
gap: var(--space-sm, 0.5rem);
}
.card-header .fa-solid,
.card-header .fa-regular {
color: var(--brand-blue);
font-size: 1rem;
}
.card-title {
margin: 0 0 0.4rem 0;
font-size: 1.05rem;
line-height: 1.3;
font-weight: var(--fw-semibold, 600);
color: var(--text-primary);
}
.card-text {
margin: 0 0 var(--space-md, 1rem) 0;
line-height: 1.5;
color: var(--text-secondary);
}
.card-body {
display: flex;
flex-direction: column;
padding: 0;
}
.card-link {
color: var(--brand-blue);
text-decoration: none;
font-weight: var(--fw-medium, 500);
}
.card-link:hover,
.card-link:focus {
text-decoration: underline;
outline: none;
}
.card-footer {
padding: 0.65rem 0;
margin: 0.65rem 0 0 0;
background-color: transparent;
border-top: 1px solid var(--border-subtle);
text-align: right;
font-size: 0.82rem;
color: var(--text-secondary);
}
.card--shadow-none {
box-shadow: none !important;
}
.card--no-radius {
border-radius: 0 !important;
}
.filter-options {
display: flex;
flex-wrap: wrap;
gap: 1rem;
width: 100%;
height: min-content;
color: grey;
margin-bottom: var(--space-sm);
}
.filter-options > * {
font-size: 0.8rem;
align-self: center;
height: 2rem;
width: min-content;
}
.filter-options select,
.filter-options input,
.filter-options button {
height: 1.8rem;
width: fit-content;
font-size: 0.8rem;
}
.filter-options .search-input {
max-width: 10rem;
padding: 0 1.4rem 0 2rem;
}
.filter-options button {
padding: 0.2rem 0.5rem;
min-width: max-content;
text-align: center;
}
.filter-options-clear {
font-size: 1rem;
height: 90%;
aspect-ratio: 1/1;
text-align: center;
align-content: center;
}
/* Form controls — Fluent flat inputs.
Tighter padding, 4px radius, subtle borders, focused with brand cyan. */
.form-row {
display: grid;
grid-template-columns: 1fr;
gap: 0.85rem;
margin-bottom: 0.85rem;
width: 100%;
}
@media (min-width: 640px) {
.form-row {
grid-template-columns: repeat(2, 1fr);
gap: 1.1rem;
}
}
.form-row.form-row:not(:last-child) {
margin-bottom: 0.85rem;
}
.form-group {
display: flex;
flex-direction: column;
width: 100%;
margin-bottom: 0.85rem;
}
.form-group label {
margin-bottom: 0.35rem;
font-weight: 600;
font-size: 0.82rem;
color: var(--text-primary);
}
/* Spans both grid columns in a two-column form-row */
.form-group-full {
grid-column: 1 / -1;
}
/* Visual label for optional form sections */
.form-section-label {
font-size: 0.78rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-secondary);
margin: 0 0 0.5rem;
}
.form-group-row {
display: flex;
flex-direction: row;
width: 100%;
}
.form-group-row label {
min-width: fit-content;
padding-right: 0.5rem;
font-weight: 600;
align-self: center;
color: var(--text-primary);
}
/* Input fields — modern dental-SaaS look. Slightly taller (38px),
6px radius, halo focus ring instead of solid 1px brand-blue. */
.form-control {
display: block;
width: 100%;
padding: 0.5rem 0.85rem;
font-size: 0.92rem;
line-height: 1.5;
color: var(--text-primary);
background-color: var(--surface-card);
background-clip: padding-box;
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
transition:
border-color 0.15s ease,
box-shadow 0.15s ease,
background-color 0.15s ease;
min-height: 38px;
}
.form-control:hover {
border-color: var(--border-strong);
}
.form-control:focus {
outline: none;
border-color: var(--brand-blue);
box-shadow: var(--shadow-focus);
}
.form-control::placeholder {
color: var(--text-muted);
}
.form-control:disabled,
.form-control[readonly] {
background-color: var(--surface-muted);
color: var(--text-secondary);
cursor: not-allowed;
}
.form-control.invalid,
.form-control.is-invalid {
border-color: #C42B1C;
}
.form-control.invalid:focus,
.form-control.is-invalid:focus {
box-shadow: 0 0 0 3px rgba(196, 43, 28, 0.18);
}
/* Plain-text form control (read-only display) */
.form-control-plaintext {
font-size: 0.9rem;
line-height: 1.5;
color: var(--text-primary);
background-color: transparent;
border: solid transparent;
margin: 0;
padding: 0;
pointer-events: none;
}
.form-control-plaintext.modified.valid { outline: none; }
.form-control-plaintext.invalid       { outline: none; }
/* —— intl-tel-input (Phone Number field) ————————————————————
The lib renders a flag + dial code "+90" + an arrow inside the
input's leading edge. The default arrow is a thin grey triangle
that reads as static text; the rules below give it a clearer
chevron-style affordance plus a hover surface so users see the
country picker is clickable. */
.iti__selected-flag {
cursor: pointer;
transition: background-color 0.15s ease;
border-radius: var(--radius-sm) 0 0 var(--radius-sm);
padding-right: 0.5rem !important;
}
.iti__selected-flag:hover,
.iti__selected-flag:focus-visible {
background: var(--surface-muted) !important;
outline: none;
}
.iti__arrow {
border-top-color: var(--text-secondary) !important;
border-top-width: 5px !important;
margin-left: 0.35rem !important;
transition: transform 0.15s ease;
}
.iti--allow-dropdown:hover .iti__arrow,
.iti__selected-flag:hover .iti__arrow {
border-top-color: var(--brand-blue) !important;
}
.iti__selected-dial-code {
font-weight: var(--fw-semibold, 600);
color: var(--text-primary);
}
.i-lg {
font-size: 1.5rem;
}
.icon-left {
padding-left: 40px;
}
i {
align-items: center;
justify-content: center;
vertical-align: middle;
}
.i-whatsapp {
color: #28D146;
}
.fa-facebook {
background: linear-gradient(180deg, #17A9FD, #0165E1);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
color: transparent;
}
.fa-instagram {
background: radial-gradient( circle at 30% 107%, /* approximate Instagram origin (bottom-leftish) */
#fdf497 0%, /* yellow */
#fdf497 5%, /* yellow edge */
#fd5949 45%, /* orange-pink */
#d6249f 60%, /* purple */
#285AEB 90% /* blue */
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
color: transparent;
}
.preserve-text {
white-space: pre-wrap;
}
input[type="color"] {
width: 2rem !important;
height: 2rem !important;
border: 1px solid #ccc;
border-radius: 4px;
padding: 0;
}
.loading-card {
font-size: 1.2rem;
}
.loading-card .loading-text::after {
content: '.';
animation: dots 1.5s steps(4, end) infinite;
}
@keyframes dots {
0% {
content: '';
}
25% {
content: '.';
}
50% {
content: '..';
}
75% {
content: '...';
}
100% {
content: '';
}
}
.show-loading {
position: relative;
pointer-events: none;
overflow: visible;
}
.show-loading::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 3px solid transparent;
border-radius: 3rem;
pointer-events: none;
animation: border-spin 2.5s steps(20, start) infinite;
}
@keyframes border-spin {
/* ——— TOP border (0–20%) ——— */
0% {
border-image: linear-gradient(90deg, var(--color-primary), var(--color-primary), transparent, transparent, transparent ) 1;
border: 3px 0 0 0;
}
5% {
border-image: linear-gradient(90deg, transparent, var(--color-primary), var(--color-primary), transparent, transparent ) 1;
}
10% {
border-image: linear-gradient(90deg, transparent, transparent, var(--color-primary), var(--color-primary), transparent ) 1;
}
15% {
border-image: linear-gradient(90deg, transparent, transparent, transparent, var(--color-primary), var(--color-primary) ) 1;
}
20% {
border-image: linear-gradient(90deg, transparent, transparent, transparent, transparent, var(--color-primary) ) 1;
}
/* ——— BOTTOM border (50–70%) ——— */
45% {
border-image: linear-gradient(270deg, var(--color-primary), transparent, transparent, transparent, transparent ) 1;
}
50% {
border-image: linear-gradient(270deg, var(--color-primary), var(--color-primary), transparent, transparent, transparent ) 1;
}
55% {
border-image: linear-gradient(270deg, transparent, var(--color-primary), var(--color-primary), transparent, transparent ) 1;
}
60% {
border-image: linear-gradient(270deg, transparent, transparent, var(--color-primary), var(--color-primary), transparent ) 1;
}
65% {
border-image: linear-gradient(270deg, transparent, transparent, transparent, var(--color-primary), var(--color-primary) ) 1;
}
70% {
border-image: linear-gradient(270deg, transparent, transparent, transparent, transparent, var(--color-primary) ) 1;
}
95% {
border-image: linear-gradient(90deg, var(--color-primary), transparent, transparent, transparent, transparent ) 1;
}
/* ——— wrap back to the top ——— */
100% {
border-image: linear-gradient(90deg, var(--color-primary), var(--color-primary), transparent, transparent, transparent ) 1;
}
}
/* Skeleton — shimmer placeholders for loading content.
The shimmer is a linear-gradient sweeping left-to-right across the
block. Two-tone soft-grey reads as "calm clinical surface", not
"loud loading spinner". Honours prefers-reduced-motion: the shimmer
becomes a static tinted block for users who opted out of motion.
Composes with anything — wrap a Skeleton in your existing card to
skeleton-out a card; stack SkeletonText to skeleton-out a paragraph.
No JS interop, no async work — purely declarative markup. */
.skeleton {
display: block;
background-color: var(--surface-muted, #F0F2F4);
background-image: linear-gradient(
90deg,
var(--surface-muted, #F0F2F4) 0%,
var(--border-subtle, #E5E8EC) 50%,
var(--surface-muted, #F0F2F4) 100%
);
background-size: 200% 100%;
background-repeat: no-repeat;
background-position: -150% 0;
border-radius: var(--radius-sm, 6px);
animation: skeleton-shimmer 1.4s ease-in-out infinite;
}
@keyframes skeleton-shimmer {
0%   { background-position: -150% 0; }
100% { background-position:  150% 0; }
}
/* Shape variants — compose with .skeleton to vary the silhouette. */
.skeleton-circle  { border-radius: 50%; }
.skeleton-pill    { border-radius: 999px; }
.skeleton-card    { border-radius: var(--radius-md, 10px); }
/* SkeletonText — stacked lines that mimic a paragraph. The component
sets per-line width inline; we just provide consistent spacing. */
.skeleton-text {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.skeleton-text > .skeleton {
border-radius: var(--radius-sm, 6px);
}
/* Accessibility — users who opted into reduced motion get a static
tinted block instead of the shimmer sweep. Same calm-surface tone,
no animation. */
@media (prefers-reduced-motion: reduce) {
.skeleton {
animation: none;
background-image: none;
background-color: var(--border-subtle, #E5E8EC);
}
}
/* EmptyState — the "nothing here yet" pattern.
Three sizes (sm / md / lg) so the same component fits inline under
a table row AND can hero a freshly-onboarded dashboard. The sub
sizing rules only change spacing, icon scale, and max-width — the
structural columns (illustration / title / sub / cta) stay
consistent.
The existing .empty-state class already exists in
reports.css / dashboard.css for the legacy Reports page. We define
it once here at component level so future migrations can drop the
page-scoped duplicates. The size modifiers (-sm / -lg) are new. */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.6rem;
padding: 2rem 1rem;
text-align: center;
color: var(--text-muted, #5C6B68);
}
/* Illustration column — central icon by default, expandable to a
custom SVG slot. The default Font Awesome icon sits on a soft
tinted disc so it doesn't feel naked on top of the page. */
.empty-state .empty-illustration {
display: inline-flex;
align-items: center;
justify-content: center;
width: 4.5rem;
height: 4.5rem;
border-radius: 50%;
background: var(--surface-muted, #F0F2F4);
color: var(--brand-primary, #0C3B3A);
margin-bottom: 0.25rem;
}
.empty-state .empty-icon {
font-size: 1.85rem;
line-height: 1;
}
.empty-state .empty-title {
font-size: 1.05rem;
font-weight: var(--fw-semibold, 600);
color: var(--text-primary, #0F1614);
}
.empty-state .empty-sub {
font-size: 0.92rem;
max-width: 36ch;
line-height: 1.45;
}
.empty-state .empty-cta {
margin-top: 0.65rem;
display: flex;
gap: 0.5rem;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
/* Size modifiers — adjust spacing + illustration scale without
touching the column structure. */
.empty-state-sm {
padding: 1.25rem 0.75rem;
gap: 0.45rem;
}
.empty-state-sm .empty-illustration {
width: 3rem;
height: 3rem;
}
.empty-state-sm .empty-icon { font-size: 1.25rem; }
.empty-state-sm .empty-title { font-size: 0.95rem; }
.empty-state-sm .empty-sub   { font-size: 0.85rem; max-width: 32ch; }
.empty-state-lg {
padding: 3.5rem 1.25rem;
gap: 0.85rem;
}
.empty-state-lg .empty-illustration {
width: 6.5rem;
height: 6.5rem;
}
.empty-state-lg .empty-icon { font-size: 2.6rem; }
.empty-state-lg .empty-title { font-size: 1.25rem; }
.empty-state-lg .empty-sub   { font-size: 1rem; max-width: 44ch; }
/* Welcome card — first-login orientation on the dashboard.
Subdued brand-gradient surface so the card reads as a "you're new
here" cue without competing with the actual dashboard tiles below
it. Dismissable; vanishes once the clinician confirms they've got
it. */
.welcome-card {
position: relative;
display: flex;
flex-direction: column;
gap: 1.25rem;
padding: 1.5rem 1.75rem;
margin-bottom: 1.25rem;
background: linear-gradient(135deg, var(--brand-navy, #0C3B3A) 0%, var(--brand-blue, #0F4E4C) 100%);
color: var(--text-on-brand, #FFFFFF);
border-radius: var(--radius-lg, 16px);
box-shadow: var(--shadow-md, 0 4px 14px rgba(12, 59, 58, 0.18));
overflow: hidden;
}
/* Subtle dot-grid texture behind the gradient — feels intentional
without dominating the content. */
.welcome-card::before {
content: "";
position: absolute;
inset: 0;
background-image: radial-gradient(circle, rgba(255, 255, 255, 0.06) 1px, transparent 1px);
background-size: 18px 18px;
pointer-events: none;
}
/* Dismiss "x" sits top-right, ghost on the gradient. */
.welcome-dismiss {
position: absolute;
top: 0.75rem;
right: 0.75rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
border: none;
background: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.85);
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.85rem;
transition: background-color 0.15s ease, color 0.15s ease;
z-index: 1;
}
.welcome-dismiss:hover {
background: rgba(255, 255, 255, 0.2);
color: #FFFFFF;
}
/* Head row — icon on the left, title + sub on the right. */
.welcome-head {
display: flex;
align-items: flex-start;
gap: 1rem;
position: relative;
z-index: 1;
}
.welcome-glow {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 3.25rem;
height: 3.25rem;
border-radius: 50%;
background: rgba(255, 255, 255, 0.14);
color: var(--brand-gold, #C9A977);
font-size: 1.4rem;
}
.welcome-title {
margin: 0;
font-size: 1.25rem;
font-weight: var(--fw-bold, 700);
color: var(--text-on-brand, #FFFFFF);
}
.welcome-sub {
margin: 0.2rem 0 0;
font-size: 0.92rem;
color: rgba(255, 255, 255, 0.85);
max-width: 64ch;
line-height: 1.5;
}
/* Tile grid — 2x2 on desktop, single column on mobile.
Each tile is a click-through to one of four orientation surfaces. */
.welcome-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.75rem;
position: relative;
z-index: 1;
}
@media (max-width: 720px) {
.welcome-grid {
grid-template-columns: 1fr;
}
.welcome-card {
padding: 1.25rem 1rem;
}
}
.welcome-tile {
display: flex;
align-items: center;
gap: 0.85rem;
padding: 0.85rem 1rem;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: var(--radius-md, 10px);
color: var(--text-on-brand, #FFFFFF);
text-decoration: none;
transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.1s ease;
}
.welcome-tile:hover {
background: rgba(255, 255, 255, 0.16);
border-color: rgba(255, 255, 255, 0.25);
color: var(--text-on-brand, #FFFFFF);
transform: translateY(-1px);
}
.welcome-tile:active {
transform: translateY(0);
}
.welcome-tile-icon {
flex-shrink: 0;
width: 2.25rem;
height: 2.25rem;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-sm, 8px);
background: rgba(255, 255, 255, 0.12);
color: var(--brand-gold, #C9A977);
font-size: 1rem;
}
.welcome-tile-body {
flex: 1;
min-width: 0;
}
.welcome-tile-title {
font-size: 0.95rem;
font-weight: var(--fw-semibold, 600);
margin-bottom: 0.15rem;
}
.welcome-tile-desc {
font-size: 0.82rem;
color: rgba(255, 255, 255, 0.78);
line-height: 1.4;
}
.welcome-tile-chev {
flex-shrink: 0;
color: rgba(255, 255, 255, 0.5);
font-size: 0.85rem;
}
.welcome-tile:hover .welcome-tile-chev {
color: var(--text-on-brand, #FFFFFF);
}
/* Modals — modern dental-SaaS dialog. Centered card with shadow-lg
elevation, 14px radius, soft inner padding. Replaces the old narrow
25em / 50vh constrained dialog with a more breathing-room layout. */
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: min(480px, calc(100vw - 2rem));
max-height: calc(100vh - 4rem);
display: flex;
flex-direction: column;
background-color: var(--surface-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
overflow: hidden;
padding: 0;
z-index: 1000;
}
.modal.hidden {
display: none;
}
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(15, 39, 69, 0.45);
z-index: 999;
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
}
.modal-header {
width: 100%;
background-color: transparent;
padding: 1.1rem 1.4rem 0.85rem;
border-bottom: 1px solid var(--border-subtle);
font-weight: var(--fw-semibold, 600);
font-size: 1.05rem;
color: var(--text-primary);
text-align: left;
display: flex;
align-items: center;
gap: 0.6rem;
}
.modal-header .fa-solid,
.modal-header .fa-regular {
color: var(--brand-blue);
font-size: 1.1rem;
}
.modal-body {
padding: 1.1rem 1.4rem;
color: var(--text-primary);
line-height: 1.5;
overflow-y: auto;
}
.modal-text {
color: var(--text-secondary);
line-height: 1.55;
}
.modal-buttons,
.modal-footer {
display: flex;
flex-direction: row;
justify-content: flex-end;
gap: var(--space-sm);
padding: 0.9rem 1.4rem;
border-top: 1px solid var(--border-subtle);
background: var(--surface-muted);
}
/* The legacy .emtpy class — kept as-is in case anything still references
it. New code should prefer the .card-elevated variant from cards.css. */
.emtpy {
display: flex;
flex-direction: column;
flex-grow: 1;
background-color: var(--surface-card);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
overflow: hidden;
min-width: 0;
}
/* Tables — modern dental-SaaS look. Bigger rows, soft hover wash,
sticky uppercase headers. The old 2.6rem dense row + 0.4px tracking
read as "Outlook 2010"; the new 3rem row + 0.6px tracking reads as
modern SaaS. */
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 0.85rem;
table-layout: auto;
background-color: transparent;
display: block;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
white-space: nowrap;
font-size: 0.92rem;
}
@media (min-width: 768px) {
table {
display: table;
overflow-x: visible;
white-space: normal;
}
}
th:first-child,
td:first-child {
padding-left: 1rem;
}
th:last-child,
td:last-child {
padding-right: 1rem;
}
th {
padding: 0.7rem 0.55rem 0.55rem;
border-bottom: 1px solid var(--border-subtle);
font-weight: var(--fw-semibold, 600);
font-size: 0.72rem;
color: var(--text-muted);
background-color: var(--surface-muted);
text-align: left;
text-transform: uppercase;
letter-spacing: 0.6px;
position: sticky;
top: 0;
z-index: 1;
}
tbody > tr {
border-bottom: 1px solid var(--border-subtle);
height: 3rem;
transition: background-color 0.15s ease;
}
tbody > tr:last-child {
border-bottom: 0;
}
tbody > tr:hover {
background-color: var(--surface-muted);
}
/* Reveal-on-hover action column — a common dental-SaaS pattern where
the row's action icons only appear when you hover the row, keeping
the list clean at rest. Opt-in via .table-actions on the cell. */
.table-actions {
opacity: 0;
transition: opacity 0.15s ease;
}
tbody > tr:hover .table-actions {
opacity: 1;
}
td {
padding: 0.65rem 0.55rem;
color: var(--text-primary);
vertical-align: middle;
}
/* Wrap any table in this class to get a card-like surface with the
modern shadow + radius treatment. */
.table-card {
background: var(--surface-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
box-shadow: var(--shadow-sm);
overflow: hidden;
}
.table-card table {
margin-bottom: 0;
}
.toolbox {
position: sticky;
top: 3.5rem;
z-index: 99;
margin-top: -2rem;
margin-right: -2.8rem;
margin-bottom: 1.5rem;
margin-left: -2.8rem;
padding: 0.5rem 1rem;
background-color: #e0eef6;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: row;
height: 8rem;
}
.toolbox > div {
width: 2rem;
padding: var(--space-sm);
}
/*Give any tool a background color and 100% width (rest of space)*/
.toolbox .tool {
margin-left: var(--space-sm);
background-color: #00000020;
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: var(--space-sm);
overflow-x: hidden;
overflow-y: auto;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none;
}
.toolbox .tool::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
.toolbox .tool button {
font-size: 80%;
width: fit-content;
height: fit-content;
}
.toolbox .tool > .form-group {
width: fit-content;
height: fit-content;
text-align: end;
}
.toolbox .tool > .form-group label {
width: max-content;
text-align: right;
padding-right: var(--space-sm);
}
.toolbox .tool > .form-group *:not(:first-child) {
min-width: 2rem;
width: fit-content;
max-width: 10rem;
height: fit-content;
min-height: 1.5rem;
font-size: 0.9rem;
}
.toolbox .tool-button-high {
width: 2rem;
height: 100%;
padding: 0;
margin: 0 var(--space-sm);
}
.toolbox .form-group {
margin: 0;
flex-direction: row;
}
/*All select smaller in the tool box*/
.toolbox select, .toolbox input {
padding: 0;
margin: 0;
width: max-content;
height: fit-content;
text-align: center;
}
.toolbox .form-group label {
width: 40%;
padding-right: 1.3rem;
text-align: right;
}
/*Special button to open selection menu*/
.toolbox .selection-button {
margin-left: -0.5rem;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.toolbox .selection-button {
width: 1.5rem; /* or whatever fixed width you want */
min-width: 1.5rem;
max-width: 1.5rem;
height: 100%;
box-sizing: border-box;
margin-left: -0.5rem;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
padding: 0; /* ensure no extra padding */
font-size: 1rem; /* ensure consistent font size */
transition: none; /* prevent size changes on hover/focus */
flex-shrink: 0; /* prevent shrinking in flexbox */
}
/*Main selection of company and language */
#toolbox-selection {
display: flex;
flex-direction: column;
justify-content: space-around;
width: 20rem;
height: 100%;
background-color: #00000020;
}
#toolbox-selection select {
width: 60%;
}
/*Tools for main selection tool button and save, disacrd and show full example */
#default-tools {
display: flex;
flex-direction: column;
justify-content: space-around;
width: 12rem;
box-sizing: border-box;
}
#default-tools #buttons {
display: flex;
flex-direction: row;
gap: 0.5rem;
}
#default-tools button {
width: 5rem; /* fixed width for all menu buttons */
min-width: 5rem;
max-width: 5rem;
font-size: 1rem;
box-sizing: border-box;
flex-shrink: 0;
}
#show-ful-example > * {
width: max-content;
}
#toolbox-menu button {
height: 3rem;
width: fit-content;
}
.upload-container {
display: flex;
flex-direction: row;
gap: var(--space-sm);
width: max-content;
}
.upload-container div {
display: flex;
max-width: 100%;
}
.upload-container div span:first-child {
max-width: 10rem;
}
.upload-container div span:not(:first-child) {
margin-right: var(--space-sm);
}
.color-picker input[type="color"] {
height: 1rem !important;
border: none;
cursor: pointer;
padding: 0;
}
/*
.upload-container > div {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
}
.upload-container span {
font-size: 0.95rem;
color: #333;
margin-right: 0.5rem;
word-break: break-all;
}
.upload-container .btn {
min-width: 5rem;
font-size: 1rem;
padding: 0.3rem 1rem;
margin-right: 0.25rem;
}
.upload-container input[type="file"] {
margin-top: 0.5rem;
font-size: 0.95rem;
background: transparent;
border: none;
}
.upload-container .btn + .btn {
margin-left: 0.5rem;
}*/
/*
#default-tools {
display: flex;
flex-direction: column;
width: 18rem;
gap: var(--space-sm);
padding-right: var(--space-sm);
border-right: solid var(--color-primary);
}
#default-tools label {
font-weight: bold;
padding-right: var(--space-sm);
}
#display-language {
max-width: fit-content;
}
#display-language select {
width: min-content;
max-width: 7rem;
padding: 0;
}
#template-buttons {
display: flex;
flex-direction: row;
gap: var(--space-sm);
}
#template-buttons .btn {
padding: 0.2rem 0.5rem;
}
.toolbox {
display: grid;
grid-template-columns: auto auto;
grid-template-rows: 2rem auto;
height: 100%;
}
.toolbox-menu-name {
grid-column: 2;
font-weight: bold;
}
.toolbox-back-button {
grid-row: 1 / span 2;
width: 2rem;
padding: 0;
margin: 0 var(--space-sm);
}
.toolbox-content {
grid-column: 2;
grid-row: 2;
display: flex;
flex-direction: row;
flex-wrap: wrap;
border: solid blue;
overflow: hidden;
}
*/
#field-translations .card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
gap: 2rem;
}
#select-language {
display: flex;
align-items: center;
gap: 0.75rem;
}
#select-language label {
font-weight: 600;
font-size: 115%;
white-space: nowrap;
}
#select-language .form-control {
min-width: 160px;
border-radius: 4px;
border: 1px solid #d1d5db;
background: #fff;
font-size: 1rem;
padding: 0.25rem 0.75rem;
}
.card-header .pos-right-middle {
display: flex;
gap: 1rem;
}
#field-translations table {
width: 90%;
margin: 2rem auto 1.5rem auto;
border-radius: 6px;
}
#field-translations th, #field-translations td {
padding: 0.6rem 1rem;
text-align: left;
}
#field-translations td {
border-bottom: 2px solid #f0f0f0;
}
#field-translations td:first-child {
text-align: left;
padding-left: auto;
font-weight: 600;
}
#field-translations input[type="text"], #field-translations input[type="text"].form-control, #field-translations input:not([type]), #field-translations .form-control:not(select) {
border: none;
background: transparent;
box-shadow: none;
outline: none;
width: 100%;
font-size: 1rem;
padding: 0.25rem 1rem;
}
#field-translations input[type="text"]:focus,
#field-translations input[type="text"].form-control:focus,
#field-translations input:not([type]):focus,
#field-translations .form-control:not(select):focus {
background: #dceaff;
}
/* Dashboard — mobile-first.
Single-column on phones; 2 cols ≥640px; 3-4 cols ≥1024px. */
.dashboard {
display: flex;
flex-direction: column;
gap: 1.5rem;
max-width: 1200px;
width: 100%;
margin: 0 auto;
}
.dashboard-header h1 {
font-size: 1.5rem;
font-weight: 600;
color: var(--brand-navy);
margin: 0 0 0.25rem;
}
.dashboard-sub {
font-size: 0.95rem;
color: var(--color-secondary);
margin: 0;
}
/* —— Stat cards —— */
.stats-grid {
display: grid;
grid-template-columns: 1fr;
gap: 0.875rem;
}
.stat-card {
position: relative;
display: flex;
align-items: center;
gap: 0.85rem;
padding: 1rem 1.1rem;
background: var(--color-soft-white);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
box-shadow:
0 1px 2px rgba(12, 31, 63, 0.04),
0 6px 18px -6px rgba(12, 31, 63, 0.08);
transition: transform 0.15s ease, box-shadow 0.15s ease;
overflow: hidden;
}
.stat-card:hover {
transform: translateY(-1px);
box-shadow:
0 1px 2px rgba(12, 31, 63, 0.04),
0 14px 30px -8px rgba(12, 31, 63, 0.14);
}
.stat-card .stat-icon {
flex-shrink: 0;
width: 44px;
height: 44px;
border-radius: 12px;
background: var(--brand-gradient-soft);
color: var(--brand-blue);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
}
.stat-card .stat-body {
flex: 1 1 auto;
min-width: 0;
}
.stat-card .stat-label {
font-size: 0.78rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 1.2px;
color: var(--color-secondary);
}
/* —— Mobile / narrow-tablet: relax the uppercase styling so the
longer labels ("MY CONVERSION (30 D)", "MY PATIENTS THIS MONTH")
stop wrapping to 3-4 lines inside a 180px tile column. Lower
letter-spacing + smaller body type buys two extra characters
per line; dropping all-caps on phones cuts another ~15% width
so the label fits a sensible 1-2 lines. —— */
@media (max-width: 720px) {
.stat-card .stat-label {
font-size: 0.72rem;
letter-spacing: 0.4px;
}
}
@media (max-width: 480px) {
.stat-card .stat-label {
text-transform: none;
letter-spacing: 0;
font-size: 0.78rem;
font-weight: 600;
}
}
.stat-card .stat-value {
font-size: 1.6rem;
font-weight: 600;
color: var(--brand-navy);
line-height: 1.2;
margin-top: 0.1rem;
}
.stat-card .stat-value-sm {
font-size: 0.95rem;
color: var(--brand-navy);
margin-top: 0.1rem;
}
.stat-card .stat-link {
flex-shrink: 0;
width: 36px;
height: 36px;
border-radius: 50%;
background: transparent;
color: var(--brand-blue);
display: inline-flex;
align-items: center;
justify-content: center;
text-decoration: none;
transition: background-color 0.15s ease, transform 0.15s ease;
}
.stat-card .stat-link:hover {
background: var(--brand-gradient-soft);
transform: translateX(2px);
}
.stat-card.stat-card-primary {
background: var(--brand-gradient);
border-color: transparent;
color: var(--color-soft-white);
}
.stat-card.stat-card-primary .stat-icon {
background: #FFFFFF26;
color: var(--color-soft-white);
}
.stat-card.stat-card-primary .stat-label,
.stat-card.stat-card-primary .stat-value,
.stat-card.stat-card-primary .stat-value-sm {
color: var(--color-soft-white);
}
.stat-card.stat-card-primary .stat-link {
color: var(--color-soft-white);
background: #FFFFFF26;
}
.stat-card.stat-card-primary .stat-link:hover {
background: #FFFFFF40;
}
/* —— Sections —— */
.section-title {
font-size: 1rem;
font-weight: 600;
color: var(--brand-navy);
margin: 0 0 0.75rem;
letter-spacing: 0.2px;
}
.section-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.75rem;
}
.section-head .section-title { margin: 0; }
.section-head .section-link {
font-size: 0.85rem;
color: var(--brand-blue);
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.35rem;
}
.section-head .section-link:hover {
color: var(--brand-navy);
}
/* —— Quick action grid —— */
.action-grid {
display: grid;
grid-template-columns: 1fr;
gap: 0.75rem;
}
.action-card {
display: flex;
align-items: center;
gap: 0.85rem;
padding: 0.95rem 1rem;
background: var(--color-soft-white);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
text-decoration: none;
color: inherit;
transition:
transform 0.12s ease,
box-shadow 0.15s ease,
border-color 0.15s ease;
}
.action-card:hover {
transform: translateY(-1px);
border-color: var(--brand-cyan);
box-shadow: 0 8px 22px -8px rgba(30, 95, 174, 0.18);
color: inherit;
}
.action-card .action-icon {
flex-shrink: 0;
width: 38px;
height: 38px;
border-radius: 10px;
background: var(--brand-gradient-soft);
color: var(--brand-blue);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1rem;
}
.action-card .action-body { min-width: 0; }
.action-card .action-title {
font-weight: 600;
color: var(--brand-navy);
font-size: 0.95rem;
}
.action-card .action-desc {
font-size: 0.82rem;
color: var(--color-secondary);
margin-top: 0.1rem;
}
/* —— Recent list — modern dental-SaaS card-stack rows. Each row reads
as its own micro-card: rounded corners, soft hover lift, brand-blue
icon chip on the left. Replaces the flat list-of-rows look. —— */
.list-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
background: transparent;
border: 0;
border-radius: 0;
overflow: visible;
}
.list-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.85rem 1rem;
text-decoration: none;
color: inherit;
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
background: var(--surface-card);
box-shadow: var(--shadow-sm);
transition:
background-color 0.15s ease,
border-color 0.15s ease,
box-shadow 0.15s ease,
transform 0.08s ease;
}
.list-item:last-child { border-bottom: 1px solid var(--border-subtle); }
.list-item:hover {
background: var(--surface-card);
border-color: var(--border-default);
box-shadow: var(--shadow-md);
color: inherit;
transform: translateY(-1px);
}
.list-item .list-item-icon {
width: 40px;
height: 40px;
border-radius: var(--radius-md);
background: linear-gradient(135deg, var(--surface-active) 0%, #DBE8FB 100%);
color: var(--brand-blue);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1rem;
flex-shrink: 0;
}
.list-item .list-item-title {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 500;
color: var(--brand-navy);
}
.list-item .list-item-meta {
flex-shrink: 0;
font-size: 0.88rem;
color: var(--color-secondary);
}
.list-item .list-item-chev {
flex-shrink: 0;
color: var(--color-secondary);
font-size: 0.8rem;
}
/* —— Responsive grid steps ——
Bumped the 2-column breakpoint from 640px to 720px so the
tablet-narrow band (640-720px) gets the single-column layout
instead of two cramped tiles whose labels wrap to 3-4 lines.
The action-grid stays at 640px because its labels are short and
the 2-column layout reads well even at narrow widths. */
@media (min-width: 640px) {
.action-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 720px) {
.stats-grid  { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
.stats-grid  { grid-template-columns: repeat(3, 1fr); }
.action-grid { grid-template-columns: repeat(3, 1fr); }
.dashboard-header h1 { font-size: 1.875rem; }
}
@media (min-width: 1280px) {
.stats-grid  { grid-template-columns: repeat(4, 1fr); }
.action-grid { grid-template-columns: repeat(4, 1fr); }
}
/* —— Consultations list header with right-aligned primary CTA —— */
.dashboard-header.list-header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-md);
flex-wrap: wrap;
}
.dashboard-header.list-header .list-header-text { min-width: 0; flex: 1 1 auto; }
.dashboard-header.list-header .new-consultation-btn {
flex-shrink: 0;
white-space: nowrap;
}
/* —— Consultation detail page —— */
.detail-header { display: flex; flex-direction: column; gap: 0.35rem; }
.detail-header .back-link {
color: var(--text-secondary);
font-size: 0.85rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.4rem;
margin-bottom: 0.25rem;
}
.detail-header .back-link:hover { color: var(--brand-blue); }
.detail-header .detail-title-row {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}
.detail-header .detail-title-row h1 { margin: 0; }
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: var(--space-md);
margin-top: var(--space-md);
}
.detail-card {
background: var(--surface-card);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
padding: var(--space-md);
}
.detail-card.detail-card-wide { grid-column: 1 / -1; }
.detail-card h2 {
margin: 0 0 0.75rem;
font-size: 1rem;
font-weight: 600;
color: var(--brand-navy);
}
.detail-list {
display: grid;
grid-template-columns: max-content 1fr;
gap: 0.4rem 1rem;
margin: 0;
}
.detail-list dt {
color: var(--text-secondary);
font-weight: 500;
font-size: 0.85rem;
}
.detail-list dd { margin: 0; color: var(--text-primary); }
.detail-list.totals-list dt,
.detail-list.totals-list dd { font-variant-numeric: tabular-nums; }
.detail-notes {
white-space: pre-wrap;
color: var(--text-primary);
margin: 0;
}
.visit-block {
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
padding: 0.75rem;
margin-top: 0.75rem;
}
.visit-block:first-of-type { margin-top: 0; }
.visit-block-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.5rem;
margin-bottom: 0.5rem;
flex-wrap: wrap;
}
.visit-block-meta { color: var(--text-secondary); font-size: 0.85rem; }
.visit-products { width: 100%; border-collapse: collapse; }
.visit-products th, .visit-products td {
padding: 0.4rem 0.5rem;
border-bottom: 1px solid var(--border-subtle);
font-size: 0.9rem;
}
.visit-products th {
text-align: left;
color: var(--text-secondary);
font-weight: 600;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.visit-products .num { text-align: right; font-variant-numeric: tabular-nums; }
.detail-actions {
display: flex;
gap: 0.5rem;
margin-top: var(--space-md);
flex-wrap: wrap;
}
/* ===== My Company page ===== */
.company-profile-page .card-head-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.company-profile-page .card-head-row h2 {
margin: 0;
font-size: 1rem;
font-weight: 600;
color: var(--brand-navy);
display: inline-flex;
align-items: baseline;
gap: 0.5rem;
}
.company-profile-page .card-count {
font-size: 0.78rem;
color: var(--text-secondary);
background: var(--surface-hover);
border-radius: 999px;
padding: 2px 8px;
font-weight: 500;
}
.icon-btn {
appearance: none;
background: transparent;
border: 1px solid transparent;
color: var(--text-secondary);
width: 32px;
height: 32px;
border-radius: var(--radius-sm);
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.icon-btn:hover {
background: var(--surface-hover);
color: var(--brand-blue);
border-color: var(--border-default);
}
.icon-btn.icon-btn-danger:hover { color: #C42B1C; }
.card-divider {
border: 0;
border-top: 1px solid var(--border-subtle);
margin: 1rem 0;
}
.card-hint {
margin-top: 0.6rem;
padding: 0.5rem 0.75rem;
background: var(--surface-active);
color: var(--brand-navy);
border-radius: var(--radius-sm);
font-size: 0.82rem;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.link-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
}
.link-row {
display: grid;
grid-template-columns: 28px 1fr auto;
align-items: center;
gap: 0.6rem;
padding: 0.6rem 0;
border-bottom: 1px solid var(--border-subtle);
}
.link-row:last-child { border-bottom: 0; }
.link-logo {
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
background: var(--surface-active);
color: var(--brand-blue);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.95rem;
}
.link-body {
display: flex;
flex-direction: column;
gap: 0.1rem;
min-width: 0;
}
.link-name {
font-weight: 600;
color: var(--text-primary);
}
.link-url {
font-size: 0.8rem;
color: var(--text-secondary);
text-decoration: none;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.link-url:hover { color: var(--brand-blue); text-decoration: underline; }
.user-table {
width: 100%;
border-collapse: collapse;
}
.user-table th, .user-table td {
text-align: left;
padding: 0.5rem 0.6rem;
border-bottom: 1px solid var(--border-subtle);
font-size: 0.9rem;
}
.user-table th {
color: var(--text-secondary);
font-weight: 600;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.role-pill {
display: inline-flex;
align-items: center;
border-radius: 999px;
padding: 2px 9px;
font-size: 0.78rem;
font-weight: 600;
background: var(--surface-hover);
color: var(--text-secondary);
}
.role-pill.role-companyadmin { background: var(--surface-active); color: var(--brand-blue); }
.role-pill.role-globaladmin  { background: #FCE5BF;             color: #8B4D00; }
.text-muted { color: var(--text-secondary); }
/* Compact button variant for card headers */
.btn-small {
height: 32px;
padding: 0 0.75rem;
font-size: 0.82rem;
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
/* —— Companies admin overview —— */
.search-wrapper {
max-width: 320px;
flex: 1 1 240px;
}
.muted-count {
color: var(--text-secondary);
font-variant-numeric: tabular-nums;
}
.company-row {
display: inline-flex;
align-items: center;
gap: 0.6rem;
}
.company-mark-initial {
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--surface-active);
color: var(--brand-blue);
font-weight: 700;
font-size: 0.78rem;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.company-row-name {
font-weight: 600;
color: var(--text-primary);
}
.clickable-row {
cursor: pointer;
transition: background-color 0.08s ease;
}
.clickable-row:hover {
background: var(--surface-hover);
}
.user-table .icon-btn {
color: var(--text-secondary);
}
.user-table .icon-btn:hover {
color: var(--brand-blue);
background: var(--surface-active);
}
.user-table-actions {
width: 1%;
white-space: nowrap;
text-align: right;
}
.user-table .text-end { text-align: right; }
/* —— Admin toolbar (filters in card head) —— */
.admin-toolbar {
display: flex;
gap: 0.5rem;
align-items: center;
flex-wrap: wrap;
}
.admin-toolbar-select {
height: 36px;
min-width: 160px;
flex: 0 0 auto;
}
.card-count {
display: inline-block;
background: var(--surface-active);
color: var(--brand-blue);
border-radius: 999px;
padding: 0 0.55rem;
font-size: 0.78rem;
font-weight: 700;
margin-left: 0.4rem;
vertical-align: middle;
}
.link-soft {
color: var(--brand-blue);
text-decoration: none;
}
.link-soft:hover { text-decoration: underline; }
/* —— Admin company detail page —— */
.detail-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 1024px) {
.detail-grid { grid-template-columns: minmax(280px, 1fr) 2fr; }
.detail-card-wide { grid-column: 1 / -1; }
}
.header-actions {
display: flex;
gap: 0.5rem;
align-items: center;
flex-wrap: wrap;
}
.profile-rows {
display: flex;
flex-direction: column;
gap: 0.6rem;
}
.profile-row {
display: flex;
justify-content: space-between;
gap: 1rem;
padding: 0.5rem 0;
border-bottom: 1px dashed var(--border-subtle);
}
.profile-row:last-child { border-bottom: 0; }
.profile-k { color: var(--text-secondary); font-size: 0.85rem; }
.profile-v { color: var(--text-primary); font-weight: 600; text-align: right; }
.category-block {
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
margin-bottom: 0.6rem;
overflow: hidden;
}
.category-block > summary {
list-style: none;
cursor: pointer;
padding: 0.7rem 0.9rem;
display: flex;
justify-content: space-between;
align-items: center;
background: var(--surface-page);
font-weight: 600;
}
.category-block > summary::-webkit-details-marker { display: none; }
.category-block > summary::after {
content: '/f078';
font-family: 'Font Awesome 6 Free';
font-weight: 900;
margin-left: 0.5rem;
font-size: 0.75rem;
color: var(--text-secondary);
transition: transform 0.18s ease;
}
.category-block[open] > summary::after { transform: rotate(180deg); }
.category-name { color: var(--text-primary); }
.category-empty {
padding: 0.8rem 0.9rem;
color: var(--text-secondary);
font-size: 0.85rem;
}
.danger-zone {
border: 1px solid #F1707C;
background: #FFF8F8;
border-radius: var(--radius-md);
padding: 1rem 1.25rem;
margin-top: 1rem;
}
.danger-zone h3 {
color: #C42B1C;
font-size: 0.95rem;
margin: 0 0 0.3rem;
}
.danger-zone p {
color: var(--text-secondary);
font-size: 0.85rem;
margin: 0 0 0.6rem;
}
.icon-btn-danger:hover {
color: #C42B1C;
background: #FDE7E9;
}
.role-pill {
display: inline-block;
padding: 0.1rem 0.55rem;
border-radius: 999px;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
background: var(--surface-active);
color: var(--brand-blue);
}
.role-pill.role-globaladmin { background: #EFD9FF; color: #6B21A8; }
.role-pill.role-companyadmin { background: #E8F1F0; color: #0C3B3A; }
.role-pill.role-companyuser { background: #F4ECD8; color: #6B5510; }
/* —— Languages list polish —— */
.iso-code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
background: var(--surface-page);
border: 1px solid var(--border-subtle);
border-radius: 4px;
padding: 1px 6px;
font-size: 0.78rem;
color: var(--text-primary);
}
.status-pill {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.15rem 0.55rem;
border-radius: 999px;
font-size: 0.78rem;
font-weight: 600;
}
.status-pill.status-ok   { background: #E5F2E5; color: #107C10; }
.status-pill.status-warn { background: #FCE5BF; color: #8B4D00; }
/* —— Consultation row: split link area + PDF action button —— */
.consultation-item {
display: flex;
align-items: stretch;
gap: 0;
padding: 0;
}
.consultation-item .cons-link {
flex: 1 1 auto;
display: flex;
align-items: center;
gap: 0.85rem;
padding: 0.85rem 1rem;
text-decoration: none;
color: inherit;
min-width: 0;
}
.consultation-item .cons-link:hover {
background: var(--surface-hover);
}
.consultation-item .cons-pdf-btn {
flex: 0 0 auto;
padding: 0 1rem;
border-left: 1px solid var(--border-subtle);
border-radius: 0;
color: var(--brand-blue);
background: transparent;
border-top: 0;
border-right: 0;
border-bottom: 0;
}
.consultation-item .cons-pdf-btn:hover {
background: var(--surface-active);
}
/* —— Admin form pages (create/edit) —— */
.form-card {
max-width: 720px;
padding: 1.25rem 1.5rem;
}
.form-card .form-group { margin-bottom: 1rem; }
.form-card label {
display: block;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.35rem;
}
.form-card .form-control { width: 100%; }
.form-card .admin-toolbar-select { width: 240px; max-width: 100%; }
.form-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
}
@media (min-width: 640px) {
.form-grid { grid-template-columns: 1fr 1fr; }
}
.form-hint {
font-size: 0.78rem;
color: var(--text-secondary);
margin-top: 0.3rem;
}
.form-actions {
display: flex;
gap: 0.6rem;
align-items: center;
margin-top: 1.25rem;
padding-top: 1rem;
border-top: 1px solid var(--border-subtle);
}
.detail-header .back-link {
color: var(--text-secondary);
font-size: 0.85rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.35rem;
margin-bottom: 0.4rem;
}
.detail-header .back-link:hover {
color: var(--brand-blue);
text-decoration: underline;
}
/* —— Template editor —— */
.template-page { max-width: none; }
.template-header {
align-items: flex-start;
flex-wrap: wrap;
gap: 1rem;
}
.template-editor {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
align-items: flex-start;
}
@media (min-width: 1024px) {
.template-editor {
grid-template-columns: 320px 1fr;
}
}
.template-tools {
display: flex;
flex-direction: column;
gap: 1rem;
position: sticky;
top: 60px;
}
.preset-card { padding: 1rem; }
.preset-card h3 { font-size: 0.95rem; margin: 0 0 0.2rem; color: var(--text-primary); }
.preset-sub { font-size: 0.78rem; color: var(--text-secondary); margin: 0 0 0.7rem; }
.preset-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
}
.preset-tile {
background: var(--surface-page);
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
padding: 0.5rem 0.6rem;
cursor: pointer;
text-align: left;
display: flex;
align-items: center;
gap: 0.5rem;
transition: border-color 0.1s ease, background 0.1s ease;
}
.preset-tile:hover {
border-color: var(--brand-blue);
background: var(--surface-card);
}
.preset-swatch {
width: 18px;
height: 18px;
border-radius: 50%;
flex-shrink: 0;
border: 1px solid rgba(0, 0, 0, 0.08);
}
.preset-name {
font-size: 0.82rem;
font-weight: 600;
color: var(--text-primary);
}
.toolbox-card { padding: 0.75rem; }
.template-preview-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.template-preview-toolbar {
display: flex;
justify-content: space-between;
width: 100%;
max-width: 720px;
font-size: 0.78rem;
color: var(--text-secondary);
}
.template-preview-toolbar .page-format {
background: var(--surface-card);
border: 1px solid var(--border-default);
border-radius: 999px;
padding: 0.2rem 0.6rem;
font-weight: 600;
}
.template-preview-toolbar .saved-at {
color: #107C10;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 0.3rem;
}
/* A4-proportioned paper card. Width clamped so it shrinks on mobile but
keeps the print-feel aspect on desktop. */
.template-paper {
background: #FFFFFF;
border: 1px solid var(--border-default);
border-radius: 4px;
box-shadow: 0 8px 24px -12px rgba(15, 39, 69, 0.22),
0 2px 4px rgba(15, 39, 69, 0.06);
width: 100%;
max-width: 794px;        /* ≈ A4 width @ 96dpi */
min-height: 1123px;      /* ≈ A4 height @ 96dpi */
padding: 40px 48px;
overflow: hidden;
}
@media (max-width: 768px) {
.template-paper {
padding: 24px;
min-height: 600px;
}
}
/* —— Template editor: brand & visibility panel —— */
.branding-card {
padding: 1rem;
}
.branding-card h3 {
margin: 0 0 0.5rem;
font-size: 0.9rem;
color: var(--text-primary);
font-weight: 600;
}
.brand-row {
display: flex;
align-items: center;
gap: 0.6rem;
margin-bottom: 0.4rem;
}
.brand-row label {
flex: 1 1 auto;
font-size: 0.82rem;
color: var(--text-secondary);
}
.brand-color-input {
width: 36px;
height: 36px;
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
padding: 0;
cursor: pointer;
background: transparent;
}
.brand-color-hex {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.75rem;
color: var(--text-secondary);
min-width: 60px;
text-align: right;
}
.brand-divider {
border: 0;
border-top: 1px solid var(--border-subtle);
margin: 0.85rem 0;
}
.visibility-rows {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.visibility-row {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.85rem;
color: var(--text-primary);
cursor: pointer;
padding: 0.3rem 0;
}
.visibility-row input[type="checkbox"] {
width: 16px;
height: 16px;
cursor: pointer;
}
/* —— Template editor: dirty badge + sliders —— */
.dirty-badge {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: 0.8rem;
color: #8B4D00;
background: #FFF8E6;
border: 1px solid #F1C26A;
border-radius: 999px;
padding: 0.15rem 0.55rem;
font-weight: 600;
}
.dirty-badge .fa-circle { font-size: 0.4rem; color: #C77700; }
.slider {
flex: 1 1 auto;
height: 24px;
cursor: pointer;
accent-color: var(--brand-blue);
}
.slider:focus { outline: none; }
/* —— Override legacy .toolbox styling when it lives inside the new
dashboard sidebar card. The legacy rule positions it as a full-width
sticky bar with a gray-blue background and horizontal layout — that
broke the new two-column editor layout. Reset everything here. —— */
.toolbox-card .toolbox {
position: static;
margin: 0;
padding: 0;
background: transparent;
box-shadow: none;
height: auto;
display: flex;
flex-direction: column;
gap: 0.5rem;
z-index: auto;
}
.toolbox-card .toolbox > div {
width: 100%;
padding: 0;
}
.toolbox-card .toolbox .tool {
margin-left: 0;
background: var(--surface-page);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
padding: 0.7rem;
width: 100%;
display: flex;
flex-direction: column;
gap: 0.5rem;
overflow: visible;
}
.toolbox-card .toolbox .tool button {
font-size: 0.85rem;
width: 100%;
height: auto;
padding: 0.5rem 0.85rem;
text-align: left;
}
.toolbox-card .toolbox .tool > .form-group {
width: 100%;
}
.toolbox-card #toolbox-selection {
display: flex;
flex-direction: column;
gap: 0.6rem;
width: 100%;
}
.toolbox-card #toolbox-selection .form-group {
margin: 0;
}
.toolbox-card #toolbox-selection label {
font-size: 0.82rem;
color: var(--text-secondary);
font-weight: 600;
margin-bottom: 0.25rem;
}
/* Forward/back navigation arrows inside the new toolbox card */
.toolbox-card .toolbox .tool-button-high {
width: 100%;
height: auto;
padding: 0.5rem 0.85rem;
margin: 0;
font-size: 1rem;
font-weight: 700;
}
/* —— Template editor: save-as-preset + contrast badge —— */
.preset-card .card-head-row {
margin-bottom: 0.4rem;
}
.save-preset-row {
display: flex;
gap: 0.4rem;
margin: 0.4rem 0 0.85rem;
align-items: center;
}
.save-preset-row .form-control { flex: 1 1 auto; }
.preset-tile-row {
display: flex;
align-items: stretch;
gap: 0;
}
.preset-tile-row .preset-tile {
flex: 1 1 auto;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-right: 0;
}
.preset-tile-delete {
background: var(--surface-page);
border: 1px solid var(--border-default);
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
cursor: pointer;
color: var(--text-secondary);
padding: 0 0.55rem;
}
.preset-tile-delete:hover {
color: #C42B1C;
background: #FDE7E9;
}
.preset-tile-user .preset-name {
font-style: italic;
}
.contrast-row {
margin: 0.35rem 0 0.85rem;
}
.contrast-badge {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.18rem 0.6rem;
border-radius: 999px;
font-size: 0.78rem;
font-weight: 700;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
cursor: help;
}
.contrast-aaa  { background: #E5F2E5; color: #0B5D0B; }
.contrast-aa   { background: #FFF4CE; color: #614D00; }
.contrast-fail { background: #FDE7E9; color: #921A0F; }
/* —— Override legacy id-scoped toolbox styling for new editor layout —— */
.toolbox-card #toolbox-selection,
.toolbox-card #default-tools,
.toolbox-card #toolbox-menu {
width: 100%;
height: auto;
background-color: transparent;
box-shadow: none;
}
.toolbox-card #toolbox-selection select {
width: 100%;
}
.toolbox-card #default-tools {
flex-direction: column;
gap: 0.4rem;
}
.toolbox-card #toolbox-menu button {
width: 100%;
margin: 0;
}
/* —— Template editor: polish pass (font overflow, button bulk, layout) —— */
/* Wider sidebar column on big screens so the editor controls breathe;
on >1280 the paper still has 720px+ of room which is enough for A4 preview. */
@media (min-width: 1280px) {
.template-editor {
grid-template-columns: 340px 1fr;
gap: 1.25rem;
}
}
/* Stop the font-family <select> from blowing past the card edge.
`box-sizing: border-box` + explicit width + min-width:0 keeps it in. */
.toolbox-card #brand-font,
.brand-row #brand-font {
width: 100%;
max-width: 100%;
min-width: 0;
box-sizing: border-box;
text-overflow: ellipsis;
}
.brand-row select {
flex: 1 1 auto;
min-width: 0;
}
/* The font row needs its select to consume remaining width without
pushing the label. */
.brand-row label[for="brand-font"] { flex: 0 0 auto; }
/* Legacy ToolboxMenu buttons (Watermark/Logo/Header/Headings/Tables) used to
render as bulky full-width primary-blue rectangles. Demote to compact
outlined chips so they stop dominating the sidebar visually and match
the new preset/branding panels. */
.toolbox-card #toolbox-menu {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.toolbox-card #toolbox-menu button.btn-primary,
.toolbox-card .tool button.btn-primary {
background: var(--surface-page);
color: var(--text-primary);
border: 1px solid var(--border-default);
font-weight: 600;
font-size: 0.85rem;
padding: 0.5rem 0.85rem;
}
.toolbox-card #toolbox-menu button.btn-primary:hover,
.toolbox-card .tool button.btn-primary:hover {
background: var(--surface-active);
color: var(--brand-blue);
border-color: var(--brand-blue);
}
/* Forward/back arrows: smaller pill, not full-width — they're navigation,
not primary actions. */
.toolbox-card .toolbox .tool-button-high {
width: 36px;
height: 28px;
padding: 0;
font-size: 0.85rem;
line-height: 1;
background: var(--surface-page);
color: var(--text-secondary);
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
align-self: flex-end;
}
.toolbox-card .toolbox .tool-button-high:hover {
background: var(--surface-active);
color: var(--brand-blue);
border-color: var(--brand-blue);
}
/* Hide the legacy DefaultTools Save/Discard buttons — the header now has
Save changes + Reset and they were duplicating actions in two places.
Keep "Show full example" but compact it. */
.toolbox-card #default-tools #buttons { display: none; }
.toolbox-card #default-tools #show-ful-example {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.4rem 0;
font-size: 0.82rem;
color: var(--text-secondary);
}
.toolbox-card #default-tools #show-ful-example label {
margin: 0;
cursor: pointer;
}
/* WCAG contrast badge slightly smaller so it doesn't dominate the brand row. */
.contrast-badge { font-size: 0.72rem; padding: 0.15rem 0.5rem; }
/* Preset tile content respects card width — long names ellipsize instead
of wrapping awkwardly. */
.preset-tile {
overflow: hidden;
}
.preset-tile .preset-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
/* Paper preview: tighter side padding so the form content uses more of
the A4 width and the "compact report next to whitespace" feeling goes
away. Still keeps the print-feel margins on actual print. */
.template-paper {
padding: 32px 36px;
}
/* —— Branding panel polish (icons + count + line-height preview + states) —— */
.branding-card h3 {
display: flex;
align-items: center;
justify-content: space-between;
}
.hidden-count {
font-size: 0.7rem;
font-weight: 700;
background: #FFF8E6;
color: #8B4D00;
border: 1px solid #F1C26A;
border-radius: 999px;
padding: 0.05rem 0.45rem;
}
.visibility-row {
display: grid;
grid-template-columns: 18px 18px 1fr;
align-items: center;
gap: 0.55rem;
}
.visibility-row .visibility-icon {
color: var(--text-secondary);
font-size: 0.85rem;
text-align: center;
}
.visibility-row.is-hidden {
opacity: 0.55;
}
.visibility-row.is-hidden .visibility-icon {
color: var(--text-muted);
}
.visibility-row.is-hidden .visibility-label {
text-decoration: line-through;
text-decoration-color: var(--text-muted);
}
.line-height-preview {
background: var(--surface-page);
border: 1px dashed var(--border-default);
border-radius: var(--radius-sm);
padding: 0.5rem 0.7rem;
font-size: 0.78rem;
color: var(--text-secondary);
margin: 0.2rem 0 0.6rem;
}
/* Preset state pill (Active / Modified from X) */
.preset-state-active {
color: #107C10;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 0.35rem;
}
.preset-state-active .fa-check {
color: #107C10;
}
.preset-state-modified {
color: #8B4D00;
font-weight: 600;
}
.preset-state-modified strong {
color: #614D00;
}
/* —— Back-link rendered as button (uses history.back) —— */
.back-link-btn {
background: transparent;
border: 0;
padding: 0;
cursor: pointer;
font: inherit;
color: var(--text-secondary);
font-size: 0.85rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.35rem;
margin-bottom: 0.4rem;
}
.back-link-btn:hover {
color: var(--brand-blue);
text-decoration: underline;
}
/* Legacy .final-form RTL fixes block was removed — FinalForm.razor and
its `.final-form-*` class soup were retired with the v2 migration.
Any RTL flipping the v2 template renderer needs goes in templates-v2.css
alongside the rules that govern .tpl-canvas / .tpl-page / .tpl-block. */
/* Form-flow UX — mobile-first.
Covers: progress stepper, sticky bottom action bar, visit tab strip,
resume-draft banner, review summary accordion. */
/* —————————————————————————————————
Progress stepper (top of every form step)
————————————————————————————————— */
.form-stepper {
display: flex;
align-items: center;
gap: 0;
padding: 1rem 1.25rem 1rem;
border-bottom: 1px solid var(--color-border);
background: var(--color-soft-white);
}
.form-stepper .step {
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
color: var(--color-secondary);
flex: 0 0 auto;
}
.form-stepper .step + .step::before {
content: '';
display: inline-block;
width: 24px;
height: 2px;
background: var(--color-border);
margin: 0 0.5rem;
flex: 0 0 auto;
transition: background-color 0.2s ease;
}
.form-stepper .step.completed + .step::before,
.form-stepper .step.current + .step::before {
/* The connector "after" a completed step glows */
background: var(--brand-blue);
}
.form-stepper .step .step-num {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--color-border);
color: #FFFFFF;
font-weight: 600;
font-size: 0.85rem;
flex: 0 0 auto;
transition: background 0.2s ease, box-shadow 0.2s ease;
}
.form-stepper .step.completed .step-num {
background: var(--brand-blue);
}
.form-stepper .step.current .step-num {
background: var(--brand-gradient);
box-shadow: 0 0 0 4px var(--brand-cyan-soft);
}
.form-stepper .step .step-label {
font-size: 0.9rem;
font-weight: 500;
}
.form-stepper .step.current .step-label {
color: var(--brand-navy);
font-weight: 600;
}
.form-stepper .step.completed .step-label {
color: var(--brand-navy);
}
/* On mobile every step keeps its label — clinicians need to see
where the form is going. Labels shrink + truncate to fit. */
@media (max-width: 640px) {
.form-stepper {
justify-content: space-between;
padding: 0.85rem 0.5rem;
gap: 0.25rem;
}
.form-stepper .step {
min-width: 0;
flex: 1 1 0;
}
.form-stepper .step .step-label {
font-size: 0.72rem;
display: inline;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.form-stepper .step:not(.current) .step-label {
color: var(--text-muted);
}
.form-stepper .step + .step::before {
width: 16px;
margin: 0 0.25rem;
}
}
/* —————————————————————————————————
Sticky bottom action bar — opt-in
————————————————————————————————— */
.form-actions-sticky {
position: sticky;
bottom: 0;
z-index: 10;
background: var(--color-soft-white);
border-top: 1px solid var(--color-border);
padding: 0.75rem 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
box-shadow: 0 -4px 12px -8px rgba(12, 31, 63, 0.08);
margin: 1rem -1rem -1rem;   /* break out of .card-body padding */
}
.form-actions-sticky .spacer { flex: 1 1 auto; }
.form-actions-sticky .step-counter {
font-size: 0.78rem;
color: var(--color-secondary);
font-weight: 500;
margin-right: auto;
margin-left: 0.5rem;
}
/* On tiny screens the buttons should each take half the width. */
@media (max-width: 480px) {
.form-actions-sticky .step-counter { display: none; }
.form-actions-sticky .btn { flex: 1 1 0; }
}
/* —————————————————————————————————
Visit tab strip (replaces vertical side-buttons)
————————————————————————————————— */
.visit-tabs {
display: flex;
gap: 0.4rem;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--color-border);
background: var(--color-soft-white);
}
.visit-tabs::-webkit-scrollbar { height: 4px; }
.visit-tabs::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: 4px; }
.visit-tabs .visit-tab {
flex: 0 0 auto;
min-width: 64px;
height: 40px;
padding: 0 0.85rem;
border-radius: var(--radius-sm);
background: transparent;
border: 1px solid var(--color-border);
color: var(--brand-navy);
font-weight: 500;
font-size: 0.9rem;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.35rem;
transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.visit-tabs .visit-tab:hover {
border-color: var(--brand-cyan);
}
.visit-tabs .visit-tab.active {
background: var(--brand-blue);
color: var(--color-soft-white);
border-color: var(--brand-blue);
}
.visit-tabs .visit-tab .check {
color: var(--brand-cyan);
font-size: 0.75rem;
}
.visit-tabs .visit-tab.active .check {
color: var(--color-soft-white);
}
/* Add / Remove visit buttons */
.visit-tabs .visit-tab-add {
min-width: 36px;
color: var(--brand-blue);
border-color: var(--brand-blue);
opacity: 0.75;
}
.visit-tabs .visit-tab-add:hover {
opacity: 1;
background: var(--brand-blue);
color: #fff;
}
.visit-tabs .visit-tab-remove {
min-width: 36px;
color: #c0392b;
border-color: #c0392b;
opacity: 0.65;
}
.visit-tabs .visit-tab-remove:hover {
opacity: 1;
background: #c0392b;
color: #fff;
}
/* —————————————————————————————————
Resume-draft banner
————————————————————————————————— */
.resume-banner {
display: flex;
align-items: center;
gap: 0.85rem;
margin: 0 0 1rem;
padding: 0.9rem 1.1rem;
border-radius: var(--radius-md);
background: var(--brand-gradient-soft);
border: 1px solid var(--brand-cyan);
color: var(--brand-navy);
}
.resume-banner .resume-icon {
flex: 0 0 auto;
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--brand-gradient);
color: var(--color-soft-white);
display: inline-flex;
align-items: center;
justify-content: center;
}
.resume-banner .resume-text {
flex: 1 1 auto;
min-width: 0;
}
.resume-banner .resume-title {
font-weight: 600;
color: var(--brand-navy);
}
.resume-banner .resume-sub {
font-size: 0.83rem;
color: var(--color-secondary);
margin-top: 0.1rem;
}
.resume-banner .resume-actions {
display: flex;
gap: 0.5rem;
flex: 0 0 auto;
}
@media (max-width: 480px) {
.resume-banner {
flex-wrap: wrap;
}
.resume-banner .resume-actions {
width: 100%;
}
.resume-banner .resume-actions .btn { flex: 1 1 0; }
}
/* Warning variant: corrupt-draft / storage-blocked notices. Same layout as
the neutral resume banner so users see a familiar shape, but in amber so
they don't mistake it for a positive "you can resume" signal. */
.resume-banner-warning {
background: #FFF6E5;
border-color: #E0A500;
color: #5C4400;
}
.resume-banner-warning .resume-icon {
background: #E0A500;
color: #FFFFFF;
}
.resume-banner-warning .resume-title {
color: #5C4400;
}
.resume-banner-warning .resume-sub {
color: #6E5500;
}
/* —————————————————————————————————
Review summary (accordion on Note step)
————————————————————————————————— */
.review-summary {
margin-bottom: 1rem;
}
.review-summary details {
background: var(--color-soft-white);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
margin-bottom: 0.6rem;
overflow: hidden;
}
.review-summary summary {
list-style: none;
cursor: pointer;
padding: 0.85rem 1rem;
display: flex;
align-items: center;
gap: 0.6rem;
font-weight: 600;
color: var(--brand-navy);
}
.review-summary summary::-webkit-details-marker { display: none; }
.review-summary summary::after {
content: '/f078';                  /* chevron-down */
font-family: 'Font Awesome 6 Free';
font-weight: 900;
margin-left: auto;
font-size: 0.78rem;
color: var(--color-secondary);
transition: transform 0.18s ease;
}
.review-summary details[open] summary::after {
transform: rotate(180deg);
}
.review-summary .review-body {
padding: 0.25rem 1rem 1rem;
font-size: 0.92rem;
color: var(--brand-navy);
}
.review-summary .review-body .kv {
display: flex;
justify-content: space-between;
gap: 0.5rem;
padding: 0.3rem 0;
border-bottom: 1px dashed var(--color-border);
}
.review-summary .review-body .kv:last-child { border-bottom: 0; }
.review-summary .review-body .kv .k { color: var(--color-secondary); }
.review-summary .review-body .kv .v { font-weight: 500; text-align: right; }
/* —————————————————————————————————
Health-condition pill picker (.select-box)
Used on PatientData step — clickable pills toggling enum values.
————————————————————————————————— */
.select-box {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
margin-bottom: 0.25rem;
}
.select-box .option {
display: inline-flex;
align-items: center;
padding: 0.35rem 0.9rem;
border-radius: var(--radius-pill, 9999px);
border: 1.5px solid var(--border-default);
background: var(--surface-card);
color: var(--text-secondary);
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
user-select: none;
transition:
background-color 0.13s ease,
border-color 0.13s ease,
color 0.13s ease,
box-shadow 0.13s ease;
}
.select-box .option:hover {
border-color: var(--brand-blue);
color: var(--brand-blue);
background: var(--surface-muted);
}
.select-box .option.selected {
background: var(--brand-blue);
border-color: var(--brand-blue);
color: #fff;
box-shadow: 0 1px 4px rgba(12, 59, 58, 0.18);
}
.select-box .option.selected:hover {
background: var(--brand-navy);
border-color: var(--brand-navy);
color: #fff;
}
/* —————————————————————————————————
Friendlier inline validation
————————————————————————————————— */
.validation-message {
color: #B00020;
font-size: 0.82rem;
margin-top: 0.3rem;
display: block;
}
input.invalid,
select.invalid,
textarea.invalid {
border-color: #B00020 !important;
background-color: #FDEEEE;
}
/* ValidationSummary is generic; we keep it for completeness but
visually de-emphasize it in favor of per-field messages. */
.validation-errors {
background: #FDEEEE;
border: 1px solid #B00020;
border-radius: var(--radius-sm);
color: #B00020;
font-size: 0.85rem;
padding: 0.6rem 0.85rem;
margin: 0 0 1rem;
}
.validation-errors ul { margin: 0; padding-left: 1.1rem; }
/* Dental chart panel and treatment editor.
Layout:
.service-tabs               horizontal tab strip above the chart
.dental-chart-panel         flex row → [chart wrap | detail pane]
.dental-chart-wrap        chart + quick-select chips + status line
.dental-detail-pane       right-side editor (260–340px) */
/* —— Tab strip above the chart —— */
.service-tabs {
display: flex;
gap: 0.25rem;
border-bottom: 1px solid var(--border-subtle);
margin: 0 0 1rem;
}
.service-tab {
appearance: none;
background: transparent;
border: 0;
border-bottom: 2px solid transparent;
color: var(--text-secondary);
cursor: pointer;
padding: 0.55rem 0.9rem;
font-size: 0.9rem;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 0.45rem;
transition: color 0.12s ease, border-color 0.12s ease;
}
.service-tab:hover { color: var(--brand-blue); }
.service-tab.active {
color: var(--brand-blue);
border-bottom-color: var(--brand-blue);
}
/* —— Outer layout —— */
.dental-chart-panel {
display: flex;
flex-wrap: wrap;
gap: 1.25rem;
align-items: flex-start;
outline: 0;
}
.dental-chart-wrap { flex: 1 1 560px; min-width: 0; }
.dental-detail-pane {
flex: 0 0 320px;
background: var(--surface-card);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
padding: var(--space-md);
min-height: 280px;
align-self: stretch;
}
/* —— Quick-select chip strip —— */
.dental-chart-toolbar {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
align-items: center;
margin-bottom: 0.6rem;
}
.dental-chart-toolbar .chip {
appearance: none;
background: var(--surface-card);
color: var(--text-secondary);
border: 1px solid var(--border-default);
border-radius: 999px;
padding: 0.25rem 0.7rem;
font-size: 0.78rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.dental-chart-toolbar .chip:hover {
border-color: var(--brand-blue);
color: var(--brand-blue);
}
.dental-chart-toolbar .chip-clear { color: var(--text-muted); }
.dental-chart-toolbar .chip-clear:hover { color: #C42B1C; border-color: #F1707C; }
.dental-chart-toolbar .chip-divider {
width: 1px;
height: 18px;
background: var(--border-default);
margin: 0 0.15rem;
}
/* —— The SVG chart itself —— */
.dental-chart {
width: 100%;
max-width: 720px;
height: auto;
user-select: none;
background: var(--surface-card);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
padding: 0.25rem;
}
.dental-midline {
stroke: var(--border-subtle);
stroke-width: 1;
stroke-dasharray: 3 3;
}
.dental-axis-label,
.dental-arch-label {
fill: var(--text-muted);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.tooth-slot {
cursor: pointer;
outline: none;
}
.tooth-slot .tooth-shape {
fill: var(--surface-card);
stroke: var(--border-default);
stroke-width: 1.25;
vector-effect: non-scaling-stroke;   /* keep stroke crisp under per-tooth scale */
stroke-linejoin: round;
stroke-linecap: round;
transition: fill 0.12s ease, stroke 0.12s ease;
}
/* Cervical line — subtle inner detail showing the crown/root pinch.
Inherits parent slot color states so selected/treated teeth keep
their interior detail visible against the colored fill. */
.tooth-slot .tooth-cervical {
fill: none;
stroke: var(--border-default);
stroke-width: 0.9;
stroke-linecap: round;
vector-effect: non-scaling-stroke;
opacity: 0.7;
pointer-events: none;
}
.tooth-slot:hover .tooth-cervical,
.tooth-slot.has-treatment .tooth-cervical,
.tooth-slot.is-selected .tooth-cervical {
stroke: var(--brand-blue);
opacity: 0.55;
}
.tooth-slot .tooth-number {
fill: var(--text-secondary);
font-size: 12px;
font-weight: 700;
pointer-events: none;
}
.tooth-slot:hover .tooth-shape {
fill: var(--surface-hover);
stroke: var(--brand-blue);
}
.tooth-slot.has-treatment .tooth-shape {
fill: var(--surface-active);
stroke: var(--brand-blue);
}
.tooth-slot.has-treatment .tooth-number {
fill: var(--brand-blue);
}
.tooth-slot.has-treatment .tooth-treatment-dot {
fill: var(--brand-blue);
}
.tooth-slot.is-selected .tooth-shape {
fill: #DBE8FB;
stroke: var(--brand-blue);
stroke-width: 2;
}
.tooth-slot.is-selected .tooth-number {
fill: var(--brand-navy);
}
.tooth-slot:focus-visible .tooth-shape {
stroke: var(--brand-blue);
stroke-width: 2;
filter: drop-shadow(0 0 0.25rem rgba(30, 95, 174, 0.35));
}
/* —— Status line under the chart —— */
.dental-chart-status {
margin-top: 0.5rem;
padding: 0.4rem 0.75rem;
background: var(--surface-active);
color: var(--brand-navy);
border-radius: var(--radius-sm);
font-size: 0.82rem;
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.dental-chart-status .fa-circle-check { color: var(--brand-blue); }
/* —— Detail pane —— */
.dental-empty {
text-align: center;
padding: 2rem 1rem;
color: var(--text-secondary);
}
.dental-empty-icon {
font-size: 1.6rem;
color: var(--brand-blue);
margin-bottom: 0.5rem;
}
.dental-empty-title {
font-weight: 600;
color: var(--text-primary);
margin-bottom: 0.25rem;
}
.dental-empty-sub { font-size: 0.85rem; }
.dental-detail-head h3 {
margin: 0 0 0.75rem;
font-size: 1rem;
color: var(--brand-navy);
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.dental-detail-numbers {
font-size: 0.72rem;
color: var(--text-muted);
font-weight: 500;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.dental-detail-pane .form-group { margin-bottom: 0.85rem; }
.dental-detail-pane .form-group > label {
display: block;
font-size: 0.78rem;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.3rem;
}
.dental-detail-pane .form-hint {
font-size: 0.72rem;
color: var(--text-muted);
margin-top: 0.3rem;
}
/* —— Surface picker (M / O / D / B / Li) —— */
.surface-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0.3rem;
}
.surface-btn {
appearance: none;
background: var(--surface-card);
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
color: var(--text-secondary);
cursor: pointer;
padding: 0.45rem 0;
font-weight: 700;
font-size: 0.85rem;
text-align: center;
transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.surface-btn:hover {
border-color: var(--brand-blue);
color: var(--brand-blue);
}
.surface-btn.active {
background: var(--brand-blue);
border-color: var(--brand-blue);
color: #fff;
}
.dental-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 0.5rem;
}
.dental-toast {
margin-top: 0.7rem;
padding: 0.45rem 0.7rem;
background: #E6F4EA;
color: #1F6D3E;
border: 1px solid #B8DDC4;
border-radius: var(--radius-sm);
font-size: 0.82rem;
display: inline-flex;
align-items: center;
gap: 0.4rem;
}
.dental-toast .fa-check { color: #1F6D3E; }
/* —— Responsive: stack chart over detail pane on narrow screens —— */
@media (max-width: 900px) {
.dental-detail-pane { flex: 1 1 100%; }
}
/* ===== Phase 2: multiple lines + planned/existing ===== */
/* Chart visual: differentiate planned vs existing-only teeth */
.tooth-slot.has-planned .tooth-shape {
fill: var(--surface-active);
stroke: var(--brand-blue);
}
.tooth-slot.has-planned .tooth-number  { fill: var(--brand-blue); }
.tooth-slot.has-planned .tooth-treatment-dot { fill: var(--brand-blue); }
.tooth-slot.has-existing .tooth-shape {
fill: #F3F2F1;
stroke: #8A8886;
}
.tooth-slot.has-existing .tooth-number { fill: var(--text-secondary); }
.tooth-slot.has-existing .tooth-treatment-dot { fill: var(--text-secondary); }
.tooth-line-count {
fill: var(--text-secondary);
font-size: 9px;
font-weight: 700;
pointer-events: none;
}
/* Existing-lines list above the add form */
.dental-existing-list {
display: flex;
flex-direction: column;
gap: 0.4rem;
margin-bottom: 1rem;
}
.dental-existing-row {
display: flex;
align-items: flex-start;
gap: 0.5rem;
padding: 0.55rem 0.7rem;
background: var(--surface-page);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
}
.dental-existing-row-body { flex: 1 1 auto; min-width: 0; }
.dental-existing-row-title {
font-size: 0.88rem;
font-weight: 600;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 0.4rem;
flex-wrap: wrap;
}
.dental-existing-row-note {
font-size: 0.78rem;
color: var(--text-secondary);
margin-top: 0.15rem;
}
.dental-existing-row-meta {
font-size: 0.78rem;
color: var(--text-muted);
margin-top: 0.15rem;
font-variant-numeric: tabular-nums;
}
.line-tag {
display: inline-flex;
align-items: center;
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
padding: 1px 6px;
border-radius: 999px;
}
.line-tag-planned  { background: var(--surface-active);    color: var(--brand-blue); }
.line-tag-existing { background: #F3F2F1;                  color: var(--text-secondary); }
/* Add-treatment card */
.dental-add-card {
border-top: 1px solid var(--border-subtle);
padding-top: 0.85rem;
}
.dental-add-head {
font-size: 0.72rem;
font-weight: 700;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.06em;
margin-bottom: 0.5rem;
}
/* Planned / Existing segmented toggle */
.planned-existing-toggle {
display: inline-flex;
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
overflow: hidden;
background: var(--surface-card);
}
.pe-btn {
appearance: none;
background: transparent;
border: 0;
color: var(--text-secondary);
padding: 0.4rem 0.85rem;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 0.4rem;
transition: background-color 0.12s ease, color 0.12s ease;
}
.pe-btn + .pe-btn { border-left: 1px solid var(--border-default); }
.pe-btn:hover { background: var(--surface-hover); color: var(--text-primary); }
.pe-btn.active { background: var(--brand-blue); color: #fff; }
.pe-btn-existing.active { background: var(--text-secondary); }
/* ===== Readonly (PDF / print) chart ===== */
.dental-readonly { margin: 0.5rem 0 1rem; page-break-inside: avoid; }
.dental-section-header { margin-bottom: 0.5rem; }
.dental-chart-print {
max-width: 720px;
width: 100%;
background: var(--surface-card, #fff);
}
/* Per-tooth caption list under the chart — print-friendly grid */
.dental-readonly-list {
margin-top: 0.6rem;
border: 1px solid var(--border-subtle, #EDEBE9);
border-radius: 4px;
padding: 0.4rem 0.6rem;
page-break-inside: avoid;
}
.dental-readonly-list-head {
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-muted);
margin-bottom: 0.35rem;
}
.dental-readonly-row {
display: grid;
grid-template-columns: 30px 1fr 2fr;
column-gap: 0.5rem;
padding: 0.25rem 0;
border-top: 1px solid var(--border-subtle, #EDEBE9);
font-size: 0.88rem;
align-items: baseline;
}
.dental-readonly-row:first-of-type { border-top: 0; }
.dental-readonly-fdi {
font-weight: 700;
color: var(--brand-blue, #0F4E4C);
font-variant-numeric: tabular-nums;
}
.dental-readonly-name { color: var(--text-secondary); }
.dental-readonly-treatments {
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.dental-readonly-treatment.is-existing {
color: var(--text-secondary);
font-style: italic;
}
.dental-readonly-tag {
font-size: 0.72rem;
color: var(--text-muted);
margin-inline-start: 0.3rem;
}
/* Print tweaks — keep colours legible, avoid orphan breaks.
Palette swap (H1.4 Phase B): old Fluent-blue (#1E5FAE) hex's
replaced with teal-700 (#0F4E4C) + teal-mist (#E8F1F0) so the
patient's printed plan matches the screen colours. */
@media print {
.dental-chart-print { border: 1px solid #999; }
.dental-readonly { page-break-inside: avoid; break-inside: avoid; }
.dental-readonly-list { page-break-inside: avoid; break-inside: avoid; }
.tooth-slot.has-planned .tooth-shape  { fill: #E8F1F0 !important; stroke: #0F4E4C !important; }
.tooth-slot.has-existing .tooth-shape { fill: #F3F2F1 !important; stroke: #8A8886 !important; }
.tooth-treatment-dot { fill: #0F4E4C !important; }
}
/* ===== Existing-line row styling (detail page + PDF visit table) ===== */
.line-row-existing td > span:first-child,
.line-row-existing td:first-child {
font-style: italic;
color: var(--text-secondary, #605E5C);
}
.line-row-note {
font-size: 0.78rem;
color: var(--text-muted, #8A8886);
margin-top: 0.15rem;
font-style: normal;
}
.visit-products td .line-tag,
.product-table td .line-tag {
margin-inline-start: 0.4rem;
vertical-align: baseline;
}
/* ===== Phase 2D: tooth conditions + dentition toggle ===== */
/* Adult / Paediatric toggle in the chart toolbar */
.dental-chart-toolbar .dentition-toggle {
display: inline-flex;
gap: 0.25rem;
margin-inline-start: 0.25rem;
}
.dental-chart-toolbar .dentition-toggle .chip.active {
background: var(--brand-blue);
border-color: var(--brand-blue);
color: #fff;
}
/* Condition visuals on tooth slots — small left-side cue */
.tooth-slot.cond-caries .tooth-shape {
stroke: #C42B1C;
stroke-width: 1.8;
}
.tooth-slot.cond-missing .tooth-shape {
stroke: #8A8886;
stroke-dasharray: 3 2;
fill: var(--surface-page);
}
.tooth-slot.cond-missing .tooth-number {
text-decoration: line-through;
fill: var(--text-muted);
}
.tooth-slot.cond-fractured .tooth-shape {
stroke: #8B4D00;
stroke-width: 1.8;
stroke-dasharray: 5 1;
}
.tooth-slot.cond-mobile .tooth-shape {
stroke: #B45309;
stroke-width: 1.8;
}
.tooth-slot.cond-watch .tooth-shape {
stroke: #B58900;
stroke-width: 1.8;
}
.tooth-slot.cond-impacted .tooth-shape {
stroke: #6B21A8;
stroke-width: 1.8;
stroke-dasharray: 4 2;
}
/* Detail-pane Tooth condition card */
.dental-condition-card {
border-top: 1px solid var(--border-subtle);
padding-top: 0.85rem;
margin-bottom: 0.85rem;
}
/* Per-tooth condition list (used on detail page + PDF caption) */
.tooth-conditions {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-top: 0.6rem;
}
.tooth-condition-row {
display: grid;
grid-template-columns: 28px 1fr auto;
column-gap: 0.5rem;
padding: 0.3rem 0;
border-top: 1px solid var(--border-subtle);
font-size: 0.88rem;
align-items: baseline;
}
.tooth-condition-row:first-of-type { border-top: 0; }
.tooth-condition-fdi { font-weight: 700; color: var(--brand-blue); font-variant-numeric: tabular-nums; }
.tooth-condition-name { color: var(--text-secondary); }
.tooth-condition-tag {
font-size: 0.72rem;
font-weight: 600;
color: var(--text-primary);
background: var(--surface-active);
padding: 1px 8px;
border-radius: 999px;
}
.tooth-condition-row.cond-caries .tooth-condition-tag    { background: #FCDDD8; color: #921A0F; }
.tooth-condition-row.cond-missing .tooth-condition-tag   { background: #F3F2F1; color: #605E5C; }
.tooth-condition-row.cond-fractured .tooth-condition-tag { background: #FCE5BF; color: #8B4D00; }
.tooth-condition-row.cond-mobile .tooth-condition-tag    { background: #FCE5BF; color: #8B4D00; }
.tooth-condition-row.cond-watch .tooth-condition-tag     { background: #FFF4CE; color: #614D00; }
.tooth-condition-row.cond-impacted .tooth-condition-tag  { background: #EFD9FF; color: #6B21A8; }
/* Reports + Consultations list — mobile-first.
Mobile shows lists / single-col charts; desktop adds the 2-col report grid. */
/* —— Filters (used by Consultations list and Reports range picker) —— */
.filters {
display: flex;
flex-direction: column;
gap: 0.6rem;
margin-bottom: 0.4rem;
}
@media (min-width: 640px) {
.filters {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
}
/* —— Filter pills — modern dental-SaaS segmented control look. Sit in
a single rail with subtle muted background; active pill picks up
the brand-blue surface tint and bolder text. —— */
.filter-tabs {
display: flex;
gap: 0.35rem;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
flex-wrap: nowrap;
background: var(--surface-muted);
border-radius: var(--radius-pill, 9999px);
padding: 0.25rem;
border: 1px solid var(--border-subtle);
}
.filter-tabs.filter-tabs-inline { padding: 0.25rem; }
.filter-tabs .filter-tab {
background: transparent;
border: 1px solid transparent;
/* Inactive tabs sit at text-muted (not text-secondary) so the
contrast between inactive and active reads at a glance. The
prior secondary/teal-700 split was a 6-8% perceptual delta —
you saw the white pill but the text differences were noise. */
color: var(--text-muted);
padding: 0.45rem 1rem;
border-radius: var(--radius-pill, 9999px);
font-weight: var(--fw-medium, 500);
font-size: 0.85rem;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 0.45rem;
flex: 0 0 auto;
white-space: nowrap;
transition:
background-color 0.15s ease,
color 0.15s ease,
box-shadow 0.15s ease;
}
/* Inactive icon shares the muted tone so the whole inactive
chip reads as "background". Active state below restores
full text-primary + teal accent for unmistakable contrast. */
.filter-tabs .filter-tab .fa-solid,
.filter-tabs .filter-tab .fa-regular,
.filter-tabs .filter-tab .fa-brands {
color: var(--text-muted);
}
.filter-tabs .filter-tab:hover {
background: var(--surface-card);
color: var(--text-primary);
}
.filter-tabs .filter-tab:hover .fa-solid,
.filter-tabs .filter-tab:hover .fa-regular,
.filter-tabs .filter-tab:hover .fa-brands {
color: var(--text-primary);
}
.filter-tabs .filter-tab.active {
background: var(--surface-card);
color: var(--brand-blue);
font-weight: var(--fw-semibold, 600);
/* Bumped from shadow-sm → shadow-md so the active pill
clearly lifts off the warm-neutral container. The white
background alone wasn't enough contrast against the new
#F0EEE6 surface-muted bg. */
box-shadow: var(--shadow-md);
}
.filter-tabs .filter-tab.active .fa-solid,
.filter-tabs .filter-tab.active .fa-regular,
.filter-tabs .filter-tab.active .fa-brands {
color: var(--brand-blue);
}
.filter-tabs .filter-tab .filter-count {
font-size: 0.7rem;
padding: 0.1rem 0.5rem;
background: var(--surface-muted);
border-radius: var(--radius-pill, 9999px);
color: var(--text-muted);
font-weight: var(--fw-semibold, 600);
min-width: 22px;
text-align: center;
}
.filter-tabs .filter-tab.active .filter-count {
background: var(--surface-active);
color: var(--brand-blue);
}
/* —— Search input with leading magnifying-glass affordance ————————
The plain text input gave no signal it was a search field. We wrap
it with an icon-bearing container, push the input's left padding so
the icon doesn't sit on top of the text, and use a pill radius to
match the global palette.
Note: don't set a flex-basis here. `.filters` is row-flex on desktop
but column-flex below ~640px, and a `flex: 1 1 280px` basis stretches
the wrap's height to 280px on mobile — sending the absolutely-positioned
icon to the centre of that 280px box, way below the input. Keeping it
block-level + min-width-only sizes it to the input on both axes. */
.filter-search-wrap {
position: relative;
width: 100%;
max-width: 340px;
min-width: min(280px, 100%);
}
.filter-search-wrap .filter-search-icon {
position: absolute;
left: 0.85rem;
top: 50%;
transform: translateY(-50%);
color: var(--text-secondary);
font-size: 0.85rem;
pointer-events: none;
transition: color 0.15s ease;
}
.filter-search-wrap:focus-within .filter-search-icon {
color: var(--brand-blue);
}
.filter-search {
width: 100%;
max-width: 340px;
border-radius: var(--radius-pill, 9999px);
padding: 0.5rem 1rem 0.5rem 2.35rem;
}
/* —— Consultations list rows (extends .list-item from dashboard.css) —— */
.list-item.consultation-item {
display: grid;
grid-template-columns: auto 1fr auto auto;
gap: 0.85rem;
align-items: center;
}
.cons-body { min-width: 0; }
.cons-line-1 {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 600;
color: var(--brand-navy);
min-width: 0;
}
.cons-line-1 .cons-patient {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1 1 auto;
min-width: 0;
}
.cons-line-2 {
display: flex;
flex-wrap: wrap;
gap: 0.85rem;
margin-top: 0.2rem;
font-size: 0.8rem;
color: var(--color-secondary);
}
.cons-line-2 .fa-solid,
.cons-line-2 .fa-regular { margin-right: 0.2rem; }
.cons-amount { text-align: right; }
.cons-total {
font-weight: 600;
color: var(--brand-navy);
font-size: 0.95rem;
}
.cons-when {
font-size: 0.76rem;
color: var(--color-secondary);
margin-top: 0.15rem;
}
/* Status pills */
.status-pill {
font-size: 0.66rem;
font-weight: 700;
padding: 0.15rem 0.55rem;
border-radius: 999px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.status-pill.status-draft     { background: #FFF6E2; color: #8A6500; }
.status-pill.status-confirmed { background: #E2F4EA; color: #006B3A; }
.status-pill.status-cancelled { background: #FDE6E6; color: #9F1A1A; }
/* —— Empty state — softer dashed card, gentle inner padding. —— */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
padding: 4rem 1.5rem;
gap: 0.55rem;
text-align: center;
background: var(--surface-card);
border: 1px dashed var(--border-default);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
}
.empty-state .empty-icon {
font-size: 2.4rem;
color: var(--color-border);
margin-bottom: 0.3rem;
}
.empty-state .empty-title {
font-weight: 600;
color: var(--brand-navy);
font-size: 1.05rem;
}
.empty-state .empty-sub {
color: var(--color-secondary);
font-size: 0.9rem;
margin-bottom: 0.6rem;
}
/* —— Reports — section blocks —— */
.reports-page .stat-card .stat-foot {
font-size: 0.78rem;
color: var(--color-secondary);
margin-top: 0.2rem;
}
.reports-page .stat-card.stat-card-primary .stat-foot {
color: rgba(255, 255, 255, 0.75);
}
.report-section {
background: var(--color-soft-white);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 1.1rem 1.2rem;
box-shadow:
0 1px 2px rgba(12, 31, 63, 0.04),
0 6px 18px -6px rgba(12, 31, 63, 0.05);
}
.report-section .section-title {
display: flex;
align-items: baseline;
gap: 0.5rem;
margin: 0 0 0.85rem;
font-size: 1rem;
font-weight: 600;
color: var(--brand-navy);
}
.report-section .section-title .section-sub {
font-size: 0.78rem;
font-weight: 400;
color: var(--color-secondary);
}
.report-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.25rem;
}
@media (min-width: 1024px) {
.report-grid { grid-template-columns: 1fr 1fr; }
}
/* —— Horizontal bar chart —— */
.bar-chart .bar-row {
display: grid;
grid-template-columns: minmax(120px, 30%) 1fr auto;
gap: 0.7rem;
align-items: center;
padding: 0.4rem 0;
}
.bar-chart .bar-label {
font-weight: 500;
color: var(--brand-navy);
font-size: 0.9rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bar-chart .bar-track {
position: relative;
height: 26px;
background: var(--color-border);
border-radius: 13px;
overflow: hidden;
}
.bar-chart .bar-fill {
height: 100%;
background: var(--brand-gradient);
border-radius: 13px;
transition: width 0.4s ease;
min-width: 4px;
}
.bar-chart .bar-fill.bar-fill-alt {
background: var(--brand-cyan);
}
.bar-chart .bar-value {
font-size: 0.82rem;
color: var(--brand-navy);
font-weight: 600;
white-space: nowrap;
text-align: right;
}
.bar-chart .bar-meta {
grid-column: 2 / 4;
font-size: 0.76rem;
color: var(--color-secondary);
margin-top: -0.1rem;
}
/* —— Time series (stacked column chart) —— */
.time-bars {
display: flex;
align-items: flex-end;
gap: 0.3rem;
height: 200px;
padding: 0 0.25rem;
}
.time-bar {
flex: 1 1 0;
min-width: 4px;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.3rem;
height: 100%;
}
.time-bar-stack {
width: 100%;
flex: 1 1 auto;
display: flex;
flex-direction: column-reverse;   /* confirmed on bottom, draft stacked on top */
min-height: 4px;
background: transparent;
}
.time-bar-confirmed {
background: var(--brand-blue);
border-radius: 4px 4px 0 0;
min-height: 1px;
}
.time-bar-draft {
background: var(--brand-cyan);
opacity: 0.65;
min-height: 0;
}
.time-bar-label {
font-size: 0.62rem;
color: var(--color-secondary);
white-space: nowrap;
transform: rotate(-45deg);
transform-origin: top right;
}
.legend {
display: flex;
gap: 1.2rem;
justify-content: flex-end;
margin-top: 0.6rem;
font-size: 0.78rem;
color: var(--color-secondary);
}
.legend .legend-item {
display: inline-flex;
align-items: center;
gap: 0.35rem;
}
.legend .legend-swatch {
width: 12px;
height: 12px;
border-radius: 3px;
display: inline-block;
}
.legend .legend-confirmed { background: var(--brand-blue); }
.legend .legend-draft     { background: var(--brand-cyan); opacity: 0.65; }
/* —— Ghost button variant (used on the Notes step) —— */
.btn-ghost {
background: transparent;
color: var(--brand-navy);
border: 1px solid var(--color-border);
}
.btn-ghost:hover {
border-color: var(--brand-blue);
background: var(--brand-gradient-soft);
color: var(--brand-navy);
}
/* Login — SMDK Solutions
Centred lockup: logo above, branded card below, tagline footer. */
.login-page {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: var(--space-lg-big) var(--space-md);
background: var(--background-color);
}
.login-page .login-brand {
margin-bottom: var(--space-md);
height: 140px;
width: auto;
max-width: 80vw;
/* The PNG has its own internal spacing; let it breathe. */
}
.login-page .login-tagline {
margin: 0 0 var(--space-lg);
font-size: 0.85rem;
letter-spacing: 4px;
text-transform: uppercase;
color: var(--brand-blue);
font-weight: 500;
}
.login-page .card {
width: 100%;
max-width: 420px;
background: var(--color-soft-white);
border-radius: var(--radius-lg);
border: 1px solid var(--color-border);
box-shadow:
0 1px 2px rgba(12, 31, 63, 0.04),
0 16px 40px -10px rgba(12, 31, 63, 0.18);
overflow: hidden;
/* Subtle accent line at the top edge — brand gradient. */
position: relative;
}
.login-page .card::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 4px;
background: var(--brand-gradient);
}
.login-page .card-header {
background: transparent;
border-bottom: 0;
padding: var(--space-lg) var(--space-lg) 0;
text-align: center;
}
.login-page .card-header .login-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--brand-navy);
margin: 0 0 0.25rem;
}
.login-page .card-header .login-subtitle {
font-size: 0.95rem;
color: var(--color-secondary);
margin: 0;
}
.login-page .card-body {
padding: var(--space-md) var(--space-lg) var(--space-lg);
}
.login-page .login-footer {
margin-top: var(--space-lg);
font-size: 0.8rem;
color: var(--color-secondary);
text-align: center;
opacity: 0.85;
}
/* tighten on small screens */
@media (max-width: 480px) {
.login-page {
padding: var(--space-lg) var(--space-sm);
}
.login-page .login-brand {
height: 110px;
}
.login-page .login-tagline {
letter-spacing: 3px;
}
}
/* Sign in: full-width primary button + Forgot-password help link */
.login-page .login-submit {
display: block;
width: 100%;
text-align: center;
padding: 0.6rem 1rem;
font-weight: 600;
}
.login-page .login-help {
margin-top: 0.85rem;
text-align: center;
font-size: 0.85rem;
}
.login-page .login-link {
color: var(--brand-blue);
text-decoration: none;
}
.login-page .login-link:hover { text-decoration: underline; }
/* ───────────────────────── Template designer v2 ─────────────────────────
Block-based document designer. The editor is intentionally separated
from the rendered template output (.tpl-* classes) so the page chrome
and the printable template can evolve independently. */
/* ── Editor chrome ─────────────────────────────────────────────────── */
.tplv2-page { max-width: none; }
.badge-v2 {
display: inline-block;
margin-left: 0.4rem;
padding: 0.05rem 0.45rem;
border-radius: 999px;
background: var(--surface-active);
color: var(--brand-blue);
font-size: 0.6em;
font-weight: 700;
vertical-align: middle;
}
.tplv2-editor {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
align-items: flex-start;
}
@media (min-width: 1280px) {
.tplv2-editor {
grid-template-columns: 260px 1fr 320px;
gap: 1.25rem;
}
}
.tplv2-library,
.tplv2-properties {
background: var(--surface-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 1rem;
position: sticky;
top: 60px;
}
.tplv2-library h3,
.tplv2-properties h3 {
margin: 0 0 0.5rem;
font-size: 0.95rem;
color: var(--text-primary);
display: flex;
align-items: center;
justify-content: space-between;
}
.tplv2-lib-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.4rem;
margin-bottom: 0.85rem;
}
.tplv2-lib-tile {
background: var(--surface-page);
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
padding: 0.55rem 0.4rem;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.3rem;
font-size: 0.75rem;
color: var(--text-primary);
transition: border-color 0.1s ease, background 0.1s ease;
}
.tplv2-lib-tile:hover {
border-color: var(--brand-blue);
background: var(--surface-active);
color: var(--brand-blue);
}
.tplv2-lib-icon {
font-size: 1.05rem;
color: var(--brand-blue);
}
.tplv2-page-list {
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.tplv2-page-entry {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.4rem 0.55rem;
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
background: var(--surface-page);
cursor: pointer;
font-size: 0.85rem;
}
.tplv2-page-entry:hover { border-color: var(--brand-blue); }
.tplv2-page-entry.is-active {
background: var(--surface-active);
border-color: var(--brand-blue);
color: var(--brand-blue);
}
.tplv2-page-entry .tplv2-page-num {
background: var(--brand-blue);
color: white;
width: 22px;
height: 22px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
flex: 0 0 auto;
}
.tplv2-page-entry .tplv2-page-name { flex: 1 1 auto; }
.tplv2-page-entry .icon-btn { opacity: 0.7; }
.tplv2-page-entry .icon-btn:hover { opacity: 1; }
.tplv2-canvas-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.tplv2-toolbar {
width: 100%;
max-width: 794px;
display: flex;
justify-content: flex-start;
}
.tplv2-paper {
background: #FFFFFF;
border: 1px solid var(--border-default);
border-radius: 4px;
box-shadow: 0 8px 24px -12px rgba(15, 39, 69, 0.22),
0 2px 4px rgba(15, 39, 69, 0.06);
width: 100%;
max-width: 794px;        /* A4 width @ 96dpi */
min-height: 1123px;      /* A4 height @ 96dpi */
overflow: hidden;
}
/* Properties panel rows */
.tplv2-properties .brand-row { flex-wrap: wrap; }
.tplv2-properties .brand-row label { min-width: 80px; }
.tplv2-textarea-row { align-items: flex-start; }
.tplv2-textarea-row textarea { width: 100%; resize: vertical; }
.tplv2-info-row { display: grid; grid-template-columns: 1fr 1fr 32px; gap: 0.3rem; align-items: center; }
.tplv2-info-row input { font-size: 0.8rem; }
.tplv2-move-btns { display: flex; gap: 0.4rem; margin-bottom: 0.6rem; }
/* ── Rendered template output (.tpl-*) ───────────────────────────────
These classes drive what the form ACTUALLY looks like. Use CSS
variables from the theme tokens injected by TemplateRenderer. */
.tpl-canvas {
color: var(--tpl-text, #2B3540);
font-family: var(--tpl-font, system-ui, sans-serif);
font-size: var(--tpl-fs-base, 14px);
}
.tpl-page {
background: var(--tpl-surface, #FFFFFF);
min-height: 1123px;
display: flex;
flex-direction: column;
position: relative;  /* anchor watermark */
}
.tpl-page-landscape { aspect-ratio: 297/210; }
/* ── Watermark ───────────────────────────────────────────────── */
.tpl-watermark {
position: absolute;
top: 50%;
left: 50%;
/* transform: translate(-50%,-50%) rotate(…)  — set inline */
pointer-events: none;
z-index: 0;
user-select: none;
white-space: nowrap;
text-align: center;
width: var(--tpl-watermark-size, 80%);
max-width: 90%;
}
.tpl-watermark-text {
color: var(--tpl-accent, #0C3B3A);
font-weight: 900;
letter-spacing: 0.18em;
font-family: var(--tpl-font-display, var(--tpl-font, sans-serif));
}
.tpl-watermark-image img {
width: 100%;
height: auto;
display: block;
}
/* All block content sits on top of the watermark */
.tpl-block, .tpl-running { position: relative; z-index: 1; }
/* The running footer should sit at the bottom of every page, so push
it down with auto margin. Running header sits at the top naturally. */
.tpl-running-footer { margin-top: auto; }
.tpl-block {
position: relative;
}
.tpl-block.is-editable {
cursor: pointer;
transition: outline-color 0.12s ease;
outline: 2px solid transparent;
outline-offset: 2px;
}
.tpl-block.is-editable:hover {
outline-color: rgba(30, 95, 174, 0.25);
}
.tpl-block.is-selected {
outline-color: var(--tpl-primary, #0F4E4C);
}
/* Cover block — sizing, background, padding, alignment all driven by
inline style on .tpl-cover from CoverBlockView (schema-driven). Keep
only the structural rules here. */
.tpl-cover {
display: flex;
flex-direction: column;
justify-content: center;
/* min-height, padding, background, align-items, text-align provided inline */
}
@media print {
/* Collapse the cover's schema-driven min-height so it doesn't push
"Consultation details" onto a second physical page. The inline style
has higher specificity than a class rule, so !important is required. */
.tpl-cover {
min-height: auto !important;
padding-top: 1.5rem !important;
padding-bottom: 1.5rem !important;
}
}
.tpl-cover-logo {
display: inline-flex;
align-items: center;
justify-content: center;
margin-bottom: 1.5rem;
}
.tpl-cover-logo > img {
/* max-width / max-height applied inline based on LogoMaxWidthPx so
admins can resize the logo. height/width:auto preserves aspect ratio. */
height: auto;
width: auto;
}
.tpl-cover-title {
/* font-size applied inline when CoverBlock.TitleSizeRem is set;
otherwise this default keeps the legacy display scale
(~base * scale^3 with default theme 14px * 1.25^3 ≈ 27px). */
font-size: 2rem;
font-weight: 700;
color: var(--tpl-accent, #0C3B3A);
font-family: var(--tpl-font-display, var(--tpl-font, sans-serif));
margin: 0 0 0.4rem;
line-height: 1.15;
}
.tpl-cover-subtitle {
font-size: 1rem;
color: var(--tpl-primary, #0F4E4C);
text-transform: uppercase;
letter-spacing: 0.2em;
margin: 0 0 3rem;
font-weight: 600;
}
.tpl-cover-patient {
margin: 2rem 0 2.5rem;
}
.tpl-cover-patient-label {
font-size: 0.8rem;
color: #8A93A1;
text-transform: uppercase;
letter-spacing: 0.18em;
margin-bottom: 0.45rem;
}
.tpl-cover-patient-name {
font-size: 1.8rem;
font-weight: 600;
color: var(--tpl-accent, #0C3B3A);
border-bottom: 2px solid var(--tpl-accent, #0C3B3A);
display: inline-block;
padding: 0 1rem 0.35rem;
}
.tpl-cover-meta {
display: flex;
justify-content: center;
gap: 2.5rem;
flex-wrap: wrap;
font-size: 0.92rem;
}
.tpl-cover-meta-label {
text-transform: uppercase;
letter-spacing: 0.12em;
font-size: 0.7rem;
color: #8A93A1;
margin-inline-end: 0.4rem;
}
/* Heading */
.tpl-heading {
color: var(--tpl-primary, #0F4E4C);
font-family: var(--tpl-font-display, var(--tpl-font, sans-serif));
font-weight: 700;
margin: 0 0 0.6rem;
}
.tpl-heading-1 { font-size: 1.8rem; }
.tpl-heading-2 { font-size: 1.4rem; }
.tpl-heading-3 { font-size: 1.15rem; }
.tpl-heading-4 { font-size: 1rem; }
/* Paragraph */
.tpl-paragraph {
margin: 0 0 0.85rem;
line-height: 1.55;
white-space: pre-wrap;
}
/* Info card / patient card */
.tpl-info-card,
.tpl-patient-card {
padding: 0.7rem 1rem;
}
.tpl-info-row,
.tpl-patient-row {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 1rem;
padding: 0.25rem 0;
border-bottom: 1px dashed var(--tpl-border, #E1E5EA);
}
.tpl-info-row:last-child,
.tpl-patient-row:last-child { border-bottom: 0; }
.tpl-info-label,
.tpl-patient-card .tpl-info-label {
color: #6B7280;
font-size: 0.85rem;
}
.tpl-info-value {
color: var(--tpl-text, #2B3540);
font-weight: 500;
text-align: end;
}
.tpl-patient-heading {
color: var(--tpl-primary, #0F4E4C);
font-size: 1rem;
font-weight: 700;
margin: 0 0 0.5rem;
}
/* Product table */
.tpl-product-table { margin-bottom: 1rem; }
.tpl-visit-block { margin-bottom: 1.5rem; }
.tpl-visit-head {
font-size: 1.05rem;
color: var(--tpl-primary, #0F4E4C);
font-weight: 700;
margin-bottom: 0.4rem;
}
.tpl-visit-duration { color: #6B7280; font-weight: 400; margin-inline-start: 0.3rem; }
.tpl-table {
width: 100%;
border-collapse: collapse;
font-size: 0.92rem;
}
.tpl-table th,
.tpl-table td {
padding: 0.45rem 0.5rem;
border-bottom: 1px solid var(--tpl-border, #E1E5EA);
text-align: start;
}
.tpl-table thead th {
background: #F8FAFC;
color: #6B7280;
font-weight: 600;
text-transform: uppercase;
font-size: 0.72rem;
letter-spacing: 0.04em;
}
.tpl-table td.num,
.tpl-table th.num { text-align: end; font-variant-numeric: tabular-nums; }
.tpl-table tr.is-existing { color: #8A93A1; }
/* Totals */
.tpl-totals {
padding: 0.6rem 1rem;
}
.tpl-total-row {
display: flex;
justify-content: space-between;
padding: 0.3rem 0;
}
.tpl-total-final {
border-top: 2px solid var(--tpl-accent, #0C3B3A);
margin-top: 0.4rem;
padding-top: 0.6rem;
font-size: 1.05rem;
}
/* Signature */
.tpl-signature {
margin: 2.5rem 0 1rem;
}
.tpl-signature-line {
height: 2px;
background: var(--tpl-accent, #0C3B3A);
width: 240px;
}
.tpl-signature-label {
color: #6B7280;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.12em;
margin-top: 0.4rem;
}
.tpl-signature-date {
display: inline-flex;
align-items: baseline;
gap: 0.4rem;
margin-top: 1rem;
}
.tpl-signature-date-label {
color: #6B7280;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.12em;
}
.tpl-signature-date-line {
display: inline-block;
width: 120px;
border-bottom: 1px solid var(--tpl-accent, #0C3B3A);
}
/* Divider */
.tpl-divider {
border: 0;
border-top: 1px solid var(--tpl-border, #E1E5EA);
margin: 1rem 0;
}
.tpl-divider-dashed { border-top-style: dashed; }
.tpl-divider-dotted { border-top-style: dotted; }
/* Page break — invisible at render time, fires page-break-after on print.
The dashed divider + "PAGE BREAK" label are editor-only affordances
so the designer can see where a break will land; gating both on
.tpl-block.is-editable keeps them out of /form/pdf and the
server-rendered output (TemplateRenderer only adds is-editable
when EditMode="true"). */
.tpl-page-break {
margin: 1rem 0;
height: 1px;
position: relative;
}
.tpl-block.is-editable .tpl-page-break {
border-top: 2px dashed var(--tpl-primary, #0F4E4C);
}
.tpl-block.is-editable .tpl-page-break::after {
content: 'PAGE BREAK';
position: absolute;
top: -7px;
left: 50%;
transform: translateX(-50%);
background: var(--tpl-surface, #FFFFFF);
padding: 0 0.5rem;
color: var(--tpl-primary, #0F4E4C);
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.15em;
}
@media print {
.tpl-page-break {
page-break-after: always;
break-after: page;
border: 0;
height: 0;
margin: 0;
}
.tpl-block.is-editable .tpl-page-break::after { display: none; }
}
.tpl-empty-note {
color: #8A93A1;
font-style: italic;
padding: 1rem;
text-align: center;
background: #F8FAFC;
border: 1px dashed var(--tpl-border, #E1E5EA);
border-radius: 4px;
}
.tpl-empty {
padding: 2rem;
text-align: center;
color: var(--text-secondary);
}
.tpl-section-heading {
color: var(--tpl-primary, #0F4E4C);
font-family: var(--tpl-font-display, var(--tpl-font, sans-serif));
font-size: 1.15rem;
font-weight: 700;
margin: 0 0 0.5rem;
}
/* ── Panel mode tabs (Theme | Page | Block) ─────────────────────── */
.tplv2-panel-tabs {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.25rem;
margin-bottom: 0.85rem;
background: var(--surface-page);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
padding: 0.2rem;
}
.tplv2-tab {
background: transparent;
border: 0;
padding: 0.45rem 0.4rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.78rem;
font-weight: 600;
color: var(--text-secondary);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.35rem;
}
.tplv2-tab:hover:not(:disabled) {
color: var(--text-primary);
}
.tplv2-tab.is-active {
background: var(--surface-card);
color: var(--brand-blue);
box-shadow: 0 1px 2px rgba(15,39,69,0.08);
}
.tplv2-tab:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.tplv2-block-actions {
display: inline-flex;
gap: 0.3rem;
margin-left: auto;
}
/* ── Header template picker ───────────────────────────────────────── */
.tplv2-template-picker {
height: 34px;
min-width: 180px;
max-width: 240px;
font-weight: 600;
}
.tplv2-company-picker {
height: 34px;
min-width: 200px;
max-width: 260px;
font-weight: 600;
background-color: var(--surface-card);
}
/* ── Empty state: no company picked ──────────────────────────────── */
.tplv2-no-company {
background: var(--surface-card);
border: 1px dashed var(--border-default);
border-radius: var(--radius-md);
padding: 3rem 1.5rem;
text-align: center;
margin: 1rem auto;
max-width: 520px;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.6rem;
}
.tplv2-no-company h2 {
margin: 0;
color: var(--text-primary);
font-size: 1.15rem;
}
/* ── Starter gallery modal ────────────────────────────────────────── */
.tplv2-modal-backdrop {
position: fixed;
inset: 0;
background: rgba(15, 39, 69, 0.4);
z-index: 200;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.tplv2-modal {
background: var(--surface-card);
border-radius: var(--radius-md);
width: 100%;
max-width: 720px;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 24px 64px -16px rgba(15, 39, 69, 0.4);
}
.tplv2-modal-small { max-width: 420px; }
.tplv2-modal-actions {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 1rem;
}
.tplv2-shortcut-table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
.tplv2-shortcut-table td {
padding: 0.4rem 0.3rem;
border-bottom: 1px solid var(--border-subtle);
}
.tplv2-shortcut-table td:first-child {
width: 45%;
white-space: nowrap;
}
.tplv2-shortcut-table kbd {
display: inline-block;
padding: 0.1rem 0.4rem;
margin: 0 0.05rem;
border: 1px solid var(--border-default);
border-bottom-width: 2px;
border-radius: 4px;
background: var(--surface-page);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.78rem;
color: var(--text-primary);
box-shadow: 0 1px 0 rgba(15, 39, 69, 0.06);
}
.tplv2-modal-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1.25rem;
border-bottom: 1px solid var(--border-subtle);
}
.tplv2-modal-head h2 {
margin: 0;
font-size: 1.1rem;
}
.tplv2-modal-body {
padding: 1.25rem;
}
.tplv2-starter-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.7rem;
margin-top: 1rem;
}
@media (max-width: 720px) {
.tplv2-starter-grid { grid-template-columns: 1fr; }
}
.tplv2-starter-tile {
background: var(--surface-page);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
padding: 1rem;
cursor: pointer;
text-align: left;
display: flex;
flex-direction: column;
gap: 0.4rem;
transition: border-color 0.1s ease, transform 0.1s ease;
}
.tplv2-starter-tile:hover:not(:disabled) {
border-color: var(--brand-blue);
transform: translateY(-2px);
}
.tplv2-starter-tile:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.tplv2-starter-swatch {
width: 32px;
height: 32px;
border-radius: 8px;
border: 1px solid rgba(0,0,0,0.08);
}
.tplv2-starter-name {
font-weight: 700;
color: var(--text-primary);
font-size: 0.95rem;
}
.tplv2-starter-desc {
color: var(--text-secondary);
font-size: 0.78rem;
line-height: 1.4;
}
/* ── Drag handle on each block (edit mode only) ─────────────────── */
.tpl-block .tpl-block-handle {
position: absolute;
top: 6px;
left: -28px;
width: 22px;
height: 22px;
border-radius: var(--radius-sm);
background: var(--surface-card);
border: 1px solid var(--border-default);
color: var(--text-secondary);
display: none;
align-items: center;
justify-content: center;
cursor: grab;
user-select: none;
z-index: 2;
}
.tpl-block.is-editable:hover .tpl-block-handle,
.tpl-block.is-selected .tpl-block-handle {
display: inline-flex;
}
.tpl-block-handle:active {
cursor: grabbing;
background: var(--surface-active);
}
/* SortableJS drag preview states */
.tplv2-sortable-ghost {
opacity: 0.35;
background: var(--surface-active) !important;
outline: 2px dashed var(--brand-blue) !important;
}
.tplv2-sortable-chosen {
outline: 2px solid var(--brand-blue) !important;
}
/* Block whose condition currently fails — shown in editor as muted */
.tpl-block.is-condition-failing {
opacity: 0.5;
position: relative;
}
.tpl-block.is-condition-failing::after {
content: '⚠ Condition not met';
position: absolute;
top: 4px;
right: 4px;
background: #FFF8E6;
color: #8B4D00;
border: 1px solid #F1C26A;
padding: 0.1rem 0.45rem;
border-radius: 999px;
font-size: 0.65rem;
font-weight: 700;
pointer-events: none;
}
/* Block hidden via visibility flag — editor preview only */
.tpl-block.is-hidden-by-flag {
opacity: 0.35;
filter: grayscale(1);
}
.tpl-block.is-hidden-by-flag::before {
content: 'HIDDEN';
position: absolute;
top: 4px;
left: 4px;
background: #ECEFF3;
color: #6B7280;
border: 1px solid var(--border-default);
padding: 0.1rem 0.45rem;
border-radius: 999px;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.12em;
pointer-events: none;
}
.tplv2-page-list .icon-btn:disabled { opacity: 0.3; cursor: not-allowed; }
.tplv2-header .icon-btn:disabled { opacity: 0.3; cursor: not-allowed; }
/* ── Image block ─────────────────────────────────────────────────── */
.tpl-image-wrap {
margin: 0.5rem 0;
width: 100%;
}
.tpl-image {
display: inline-block;
height: auto;
max-width: 100%;
}
.tpl-image-placeholder {
display: inline-block;
width: 100%;
max-width: 320px;
}
/* ── Two-column layout block ────────────────────────────────────── */
.tpl-two-col {
display: grid;
width: 100%;
align-items: start;
margin: 0.4rem 0;
}
.tpl-col {
min-width: 0;   /* allow children to shrink within the grid track */
}
.tpl-col > .tpl-canvas {
/* The inner renderer wraps content in .tpl-canvas — strip its
background so it doesn't clip out theme overrides above. */
background: transparent;
}
.tpl-col .tpl-page {
min-height: 0;
background: transparent;
}
/* ── Terms & conditions block ───────────────────────────────────── */
.tpl-terms {
border: 1px solid var(--tpl-border, #E1E5EA);
border-radius: var(--tpl-radius, 6px);
padding: 0.85rem 1rem 1rem;
margin: 0.75rem 0;
background: rgba(15, 39, 69, 0.02);
}
.tpl-terms-label {
text-transform: uppercase;
letter-spacing: 0.18em;
font-size: 0.7rem;
font-weight: 700;
color: var(--tpl-primary, #0F4E4C);
margin-bottom: 0.5rem;
}
.tpl-terms-body {
font-size: 0.78rem;
line-height: 1.55;
color: var(--tpl-text, #2B3540);
margin: 0;
white-space: pre-wrap;
}
/* ── QR code block ─────────────────────────────────────────────── */
.tpl-qr-wrap {
display: inline-flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
margin: 0.5rem 0;
padding: 0.4rem;
}
.tpl-qr {
display: block;
border: 1px solid var(--tpl-border, #E1E5EA);
background: #FFFFFF;
padding: 4px;
border-radius: 4px;
}
.tpl-qr-caption {
font-size: 0.7rem;
color: #6B7280;
text-transform: uppercase;
letter-spacing: 0.12em;
}
/* ── Print stylesheet (Preview / Ctrl-P) ────────────────────────── */
@media print {
/* Hide editor chrome — printed pages should be just the template */
.dashboard-header,
.tplv2-library,
.tplv2-properties,
.tplv2-toolbar,
.tplv2-modal-backdrop,
.navmenu,
.sidebar { display: none !important; }
.tplv2-paper,
.tpl-preview-paper {
box-shadow: none !important;
border: 0 !important;
max-width: none !important;
width: 100% !important;
}
.tpl-block-handle,
.tpl-block.is-selected,
.tpl-block.is-editable {
outline: 0 !important;
}
.tpl-block-handle { display: none !important; }
/* Don't print the is-condition-failing badge */
.tpl-block.is-condition-failing { opacity: 1 !important; }
.tpl-block.is-condition-failing::after { display: none !important; }
}
/* ── Result page action bar ────────────────────────────────────────
Top button strip on /form/pdf (Home, Back, New form, Email patient,
Generate PDF). Previously lived in finalForm.css as `.final-form-options`;
moved here when the legacy FinalForm.razor was retired in favour of
<TemplateRenderer>. The layout/spacing is now driven by .action-bar
(button.css); this rule only contributes the surface + bottom hairline. */
.tplv2-action-bar {
overflow: hidden;
margin-bottom: var(--space-lg, 1.5rem);
padding: 0.75rem 4%;
background-color: var(--surface-card);
border-bottom: 1px solid var(--border-subtle);
}
/* ── Preview route layout ──────────────────────────────────────── */
.tpl-preview-host {
background: #F5F7FB;
min-height: 100vh;
padding: 1.5rem 0 3rem;
}
.tpl-preview-toolbar {
max-width: 794px;
margin: 0 auto 1rem;
padding: 0 1rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.6rem;
}
.tpl-preview-paper {
background: #FFFFFF;
width: 100%;
max-width: 794px;
margin: 0 auto 1.25rem;
box-shadow: 0 8px 24px -12px rgba(15, 39, 69, 0.22),
0 2px 4px rgba(15, 39, 69, 0.06);
border-radius: 4px;
overflow: hidden;
}
@media print {
.tpl-preview-host { background: #FFFFFF; padding: 0; }
.tpl-preview-toolbar { display: none !important; }
.tpl-preview-paper {
margin: 0;
page-break-after: always;
break-after: page;
}
.tpl-preview-paper:last-child {
page-break-after: auto;
break-after: auto;
}
}
/* ── Running header / footer ───────────────────────────────────── */
.tpl-running {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
align-items: center;
gap: 0.5rem;
font-size: 0.72rem;
color: #6B7280;
padding: 0.4rem 0;
flex: 0 0 auto;
}
.tpl-running.has-divider.tpl-running-header {
border-bottom: 1px solid var(--tpl-border, #E1E5EA);
padding-bottom: 0.5rem;
margin-bottom: 0.6rem;
}
.tpl-running.has-divider.tpl-running-footer {
border-top: 1px solid var(--tpl-border, #E1E5EA);
padding-top: 0.5rem;
margin-top: 0.6rem;
}
.tpl-running-slot {
min-width: 0;            /* allow truncation in grid track */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tpl-running-left   { justify-self: start;  text-align: start; }
.tpl-running-center { justify-self: center; text-align: center; }
.tpl-running-right  { justify-self: end;    text-align: end; }
/* ── Token picker dropdown ─────────────────────────────────────── */
.tplv2-tokenpick {
position: relative;
display: inline-flex;
}
.tplv2-tokenpick-trigger {
color: var(--brand-blue);
background: transparent;
}
.tplv2-tokenpick-trigger:hover { background: var(--surface-active); }
.tplv2-tokenpick-menu {
position: absolute;
top: 100%;
right: 0;
margin-top: 4px;
background: var(--surface-card);
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
box-shadow: 0 8px 24px -8px rgba(15, 39, 69, 0.25);
z-index: 30;
width: 240px;
max-height: 280px;
overflow-y: auto;
padding: 0.25rem;
}
.tplv2-tokenpick-head {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: var(--text-secondary);
padding: 0.4rem 0.5rem 0.2rem;
}
.tplv2-tokenpick-item {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.1rem;
width: 100%;
background: transparent;
border: 0;
padding: 0.4rem 0.5rem;
border-radius: 4px;
cursor: pointer;
text-align: left;
}
.tplv2-tokenpick-item:hover { background: var(--surface-active); }
.tplv2-tokenpick-item code {
color: var(--brand-blue);
font-size: 0.78rem;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.tplv2-tokenpick-desc {
font-size: 0.7rem;
color: var(--text-secondary);
}
/* ── Contact footer block ─────────────────────────────────────────── */
.tpl-contact-footer {
display: flex;
flex-direction: column;
gap: 0.5rem;
padding: 0.85rem 1rem 1rem;
border-top: 1px solid var(--tpl-border, #E1E5EA);
color: var(--tpl-text, #2B3540);
font-family: var(--tpl-font, inherit);
}
.tpl-contact-footer-empty {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 0.85rem;
border: 1px dashed var(--tpl-border, #E1E5EA);
border-radius: var(--tpl-radius, 6px);
color: #6B7280;
font-size: 0.78rem;
font-style: italic;
}
.tpl-contact-title {
font-family: var(--tpl-font-display, inherit);
font-weight: 600;
color: var(--tpl-accent, #0C3B3A);
font-size: 0.95rem;
letter-spacing: 0.02em;
}
.tpl-contact-tagline {
font-size: 0.82rem;
color: #6B7280;
}
.tpl-contact-phone {
display: inline-flex;
align-items: center;
gap: 0.4rem;
font-size: 0.85rem;
color: var(--tpl-text, #2B3540);
}
.tpl-contact-phone a {
color: inherit;
text-decoration: none;
border-bottom: 1px dotted currentColor;
}
.tpl-contact-phone a:hover { color: var(--tpl-primary, #0F4E4C); }
.tpl-contact-links {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 0.75rem;
}
.tpl-contact-footer.layout-row .tpl-contact-links {
flex-direction: row;
}
.tpl-contact-footer.layout-grid .tpl-contact-links {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.5rem 0.75rem;
}
.tpl-contact-footer.layout-stack .tpl-contact-links {
flex-direction: column;
}
.tpl-contact-link {
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.35rem 0.7rem;
border-radius: 999px;
border: 1px solid var(--tpl-border, #E1E5EA);
background: var(--tpl-surface, #FFFFFF);
color: var(--tpl-text, #2B3540);
text-decoration: none;
font-size: 0.8rem;
line-height: 1.2;
transition: border-color 120ms ease, color 120ms ease, background 120ms ease;
}
.tpl-contact-link:hover {
border-color: var(--tpl-primary, #0F4E4C);
color: var(--tpl-primary, #0F4E4C);
}
.tpl-contact-icon {
font-size: 0.95rem;
color: var(--tpl-primary, #0F4E4C);
}
.tpl-contact-link:hover .tpl-contact-icon {
color: var(--tpl-primary, #0F4E4C);
}
.tpl-contact-label {
font-weight: 500;
}
/* IncludeTypes checklist in the property panel */
.tplv2-include-types {
display: flex;
flex-direction: column;
gap: 0.2rem;
margin-top: 0.25rem;
}
/* ── SignaturePad block ─────────────────────────────────────────── */
.tpl-signature-pad { display: flex; flex-direction: column; gap: 0.5rem; }
.tpl-signature-pad-label { font-size: 0.85rem; font-weight: 500; color: var(--tpl-text, #2B3540); }
.tpl-signature-pad-canvas-wrap { position: relative; border: 1px dashed var(--tpl-border, #E1E5EA); border-radius: var(--tpl-radius, 6px); background: var(--tpl-surface, #FFFFFF); overflow: hidden; }
.tpl-signature-pad-canvas { display: block; width: 100%; height: 100%; cursor: crosshair; touch-action: none; }
.tpl-signature-pad-img { display: block; border-bottom: 1px solid var(--tpl-text, #2B3540); padding-bottom: 4px; }
.tpl-signature-pad-empty { display: flex; align-items: center; justify-content: center; border-bottom: 1px solid var(--tpl-text, #2B3540); color: rgba(43, 53, 64, 0.5); }
.tpl-signature-pad-clear { align-self: flex-start; }
.tpl-signature-pad-fallback { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; text-align: center; padding: 0.75rem; font-size: 0.8rem; color: #b3261e; background: rgba(255, 255, 255, 0.85); }
/* ── Barcode block ──────────────────────────────────────────────── */
.tpl-barcode { position: relative; display: flex; align-items: center; justify-content: center; }
.tpl-barcode-svg { max-width: 100%; height: auto; }
.tpl-barcode-fallback { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; padding: 0.75rem; font-size: 0.8rem; color: #b3261e; text-align: center; }
.tpl-barcode-fallback-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: rgba(179, 38, 30, 0.08); border-radius: 4px; padding: 0.1rem 0.4rem; }
/* ── Chart block ────────────────────────────────────────────────── */
.tpl-chart { display: flex; flex-direction: column; gap: 0.5rem; }
.tpl-chart-title { font-size: 1rem; font-weight: 600; color: var(--tpl-accent, #0C3B3A); }
.tpl-chart-canvas-wrap { position: relative; width: 100%; }
.tpl-chart-canvas-wrap canvas { max-width: 100%; }
.tpl-chart-fallback { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 0.75rem; font-size: 0.8rem; color: #b3261e; text-align: center; background: rgba(255, 255, 255, 0.85); }
/* ── Versions panel (Phase 6 — snapshot history) ───────────────────── */
.tplv2-versions-list {
display: flex;
flex-direction: column;
gap: 0.45rem;
margin-top: 0.4rem;
}
.tplv2-version-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
padding: 0.55rem 0.7rem;
background: var(--surface-page);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
font-size: 0.82rem;
}
.tplv2-version-row:hover {
border-color: var(--brand-blue);
}
.tplv2-version-meta {
display: flex;
flex-direction: column;
gap: 0.1rem;
color: var(--text-primary);
line-height: 1.25;
min-width: 0;
}
.tplv2-version-meta strong {
font-weight: 700;
color: var(--brand-blue);
}
.tplv2-version-meta span {
color: var(--text-secondary);
font-size: 0.78rem;
}
.tplv2-version-meta em {
color: var(--text-secondary);
font-style: italic;
font-size: 0.75rem;
}
.tplv2-version-actions {
display: inline-flex;
gap: 0.3rem;
flex-shrink: 0;
}
/* ── Theme presets gallery modal ──────────────────────────────────── */
.tplv2-palette-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 0.7rem;
margin-top: 0.6rem;
}
@media (max-width: 720px) {
.tplv2-palette-grid { grid-template-columns: 1fr; }
}
.tplv2-palette-tile {
background: var(--surface-page);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
padding: 0.85rem;
cursor: pointer;
text-align: left;
display: flex;
flex-direction: column;
gap: 0.45rem;
transition: border-color 0.1s ease, transform 0.1s ease;
}
.tplv2-palette-tile:hover {
border-color: var(--brand-blue);
transform: translateY(-2px);
}
.tplv2-palette-swatches {
display: inline-flex;
gap: 0.3rem;
align-items: center;
}
.tplv2-palette-swatch {
width: 18px;
height: 18px;
border-radius: 50%;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.06);
}
.tplv2-palette-name {
font-weight: 700;
color: var(--text-primary);
font-size: 0.92rem;
}
.tplv2-palette-desc {
color: var(--text-secondary);
font-size: 0.76rem;
line-height: 1.4;
}
/* Inline preview paper inside the version inspect modal — smaller scale
than the full editor canvas so it fits the modal viewport. */
.tplv2-paper-inline {
margin-top: 0.5rem;
transform-origin: top left;
max-height: 60vh;
overflow: auto;
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
}
/* ── Rich-text editor (Paragraph + Terms property panel) ───────── */
.tplv2-richtext {
display: flex;
flex-direction: column;
border: 1px solid var(--border-default, #d6dde5);
border-radius: 6px;
background: #fff;
transition: border-color 120ms ease, box-shadow 120ms ease;
width: 100%;
}
.tplv2-richtext.is-focused {
border-color: var(--tpl-primary, #0F4E4C);
box-shadow: 0 0 0 3px rgba(30, 95, 174, 0.12);
}
.tplv2-richtext-toolbar {
display: flex;
flex-wrap: wrap;
gap: 2px;
padding: 4px 6px;
border-bottom: 1px solid #eef1f4;
background: #f7f9fb;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.tplv2-rt-btn {
appearance: none;
border: 1px solid transparent;
background: transparent;
border-radius: 4px;
color: #344155;
width: 26px;
height: 26px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.78rem;
cursor: pointer;
transition: background 100ms ease, border-color 100ms ease;
}
.tplv2-rt-btn:hover {
background: rgba(30, 95, 174, 0.08);
border-color: rgba(30, 95, 174, 0.18);
}
.tplv2-rt-btn:active { background: rgba(30, 95, 174, 0.16); }
.tplv2-rt-sep {
width: 1px;
background: #e1e5ea;
margin: 2px 4px;
}
.tplv2-richtext-surface {
padding: 8px 10px;
font-size: 0.85rem;
line-height: 1.5;
outline: none;
border: none;
border-top: 0;
border-radius: 0 0 5px 5px;
box-shadow: none;
min-height: 88px;
white-space: pre-wrap;
overflow-wrap: anywhere;
}
.tplv2-richtext-surface:empty::before {
content: attr(data-placeholder);
color: #98a3b1;
pointer-events: none;
}
.tplv2-richtext-surface p { margin: 0 0 0.5em; }
.tplv2-richtext-surface ul,
.tplv2-richtext-surface ol { margin: 0 0 0.5em 1.25em; padding: 0; }
.tplv2-richtext-surface a { color: var(--tpl-primary, #0F4E4C); text-decoration: underline; }
/* Inline contenteditable shown directly on the canvas for Paragraph
and Terms blocks when the designer is in EditMode. Smaller chrome
so it blends with the final render. */
.tplv2-rt-inline {
position: relative;
border: 1px dashed rgba(30, 95, 174, 0.35);
border-radius: 4px;
padding: 2px 4px;
background: rgba(30, 95, 174, 0.03);
transition: border-color 120ms ease, background 120ms ease;
}
.tplv2-rt-inline:focus-within {
border-color: var(--tpl-primary, #0F4E4C);
background: rgba(30, 95, 174, 0.06);
}
.tplv2-rt-inline > [contenteditable] { outline: none; min-height: 1.4em; }
/* ── Image upload drop zone (ImageBlockView EditMode) ──────────── */
.tplv2-image-dropzone {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.4rem;
margin: 0.5rem auto;
padding: 1.2rem 1rem;
width: 100%;
max-width: 320px;
border: 2px dashed rgba(30, 95, 174, 0.4);
border-radius: 8px;
background: rgba(30, 95, 174, 0.04);
color: var(--tpl-primary, #0F4E4C);
font-size: 0.78rem;
line-height: 1.4;
cursor: pointer;
transition: background 120ms ease, border-color 120ms ease;
}
.tplv2-image-dropzone:hover,
.tplv2-image-dropzone.is-dragover {
background: rgba(30, 95, 174, 0.1);
border-color: var(--tpl-primary, #0F4E4C);
}
.tplv2-image-dropzone .fa-cloud-arrow-up { font-size: 1.4rem; }
.tplv2-image-dropzone-hint { color: #5b6878; font-size: 0.72rem; }
.tplv2-image-upload-error {
color: #b3261e;
font-size: 0.72rem;
margin-top: 0.25rem;
}
/* ── Inline editing affordances (double-click to edit) ─────────────── */
/* When a Heading or Terms-label is being edited inline, replace the
rendered element with an <input> that adopts the same scale. */
.tpl-heading-inline-edit {
width: 100%;
background: rgba(30, 95, 174, 0.05);
border: 1px dashed var(--tpl-primary, #0F4E4C);
border-radius: 4px;
padding: 0.1rem 0.4rem;
color: var(--tpl-primary, #0F4E4C);
font-family: inherit;
font-weight: 700;
font-size: 1.4rem;            /* matches default H2 — H1/3/4 inherit OK */
outline: none;
}
.tpl-terms-label-input {
width: 100%;
background: rgba(30, 95, 174, 0.05);
border: 1px dashed var(--tpl-primary, #0F4E4C);
border-radius: 4px;
padding: 0.1rem 0.4rem;
text-transform: uppercase;
letter-spacing: 0.18em;
font-size: 0.7rem;
font-weight: 700;
color: var(--tpl-primary, #0F4E4C);
margin-bottom: 0.5rem;
outline: none;
}
.tpl-paragraph.is-empty-edit { min-height: 1.4em; opacity: 0.7; }
.tpl-paragraph-placeholder { color: #98a3b1; font-style: italic; }
/* QR fallback (when qrcode-generator CDN load fails) */
.tpl-qr-fallback {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
padding: 0.5rem;
font-size: 0.78rem;
color: #b3261e;
text-align: center;
}
.tpl-qr-fallback-value {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
background: rgba(179, 38, 30, 0.08);
border-radius: 4px;
padding: 0.1rem 0.4rem;
}
/* Locked block badge — sibling X2 surfaces .is-locked / .tpl-block-lock-badge */
.tpl-block-lock-badge {
position: absolute;
top: 6px;
right: 6px;
color: var(--text-secondary, #6B7280);
pointer-events: none;
z-index: 2;
}
/* ── Print stylesheet (Phase 7 — A4 / Letter pixel-mapping) ─────────
The earlier @media print block above hides editor chrome. This block
enforces exact paper sizing at print time + page breaks between
.tpl-page elements. Notes on assumptions:
• `@page size:A4` is the default; most engines IGNORE size set
per-page, so we don't try to override per .tpl-page. Instead we
set width/min-height on each .tpl-page-* and let the browser
flow into pages naturally.
• Landscape pages still print correctly because the inner
.tpl-page-landscape rule swaps the explicit width/min-height
below to landscape dimensions, all in mm/in units that match
physical paper.
• Letter / Legal users should override @page to size:letter in
a print profile; the .tpl-page-letter/legal rules below keep
the visual sizing correct regardless.
*/
/* Physical page margins for the PDF render. Applied per-physical-page
by the paged-media engine, so an overflowing .tpl-page keeps the same
12 mm gutter on every continuation page — previously the inset came
from .tpl-page's `padding`, which only insets the first physical page
and let content slam into the top of pages 2+. Puppeteer's MarginOptions
must MATCH this @page value (Result.razor passes 12 mm) — if the two
disagree the .tpl-page-a4 sizing fights the printable area and only
page 1 renders. */
/* Reverted to @page { margin: 12mm } after testing showed Chromium
only honours this on the very first page anyway. We pair it with
Puppeteer's MarginOptions = 12 mm in Result.razor so the visible
inset is consistent on page 1; continuation pages from a long
.tpl-page overflow remain a Chromium paged-media limitation that
neither rule fixes — see the README perf-and-print notes. */
@page { size: A4; margin: 12mm; }
@media print {
html, body { background: #FFFFFF !important; }
.tplv2-paper, .tpl-preview-paper { box-shadow: none !important; border: 0 !important; }
.tpl-block { break-inside: avoid; page-break-inside: avoid; }
/* —— Flatten the wrapper hierarchy in print ——
Previously .tpl-canvas and .tpl-page were block elements with
layout (flex column, min-height, padding). When a long .tpl-page
fragmented across multiple physical PDF pages, Chromium failed
to re-apply the @page top margin to the continuation pages —
content on pages 3+ ended up flush against the top edge while
page 1 had the proper 12 mm gutter.
`display: contents` makes both wrappers invisible to the layout
engine. Their children (the .tpl-block elements) become direct
children of <body> for layout purposes, so the paged-media engine
paginates a flat block flow — and @page margins now apply on
EVERY physical page including continuation pages.
The .tpl-page boundary that used to force a page break between
cover and body content is rebuilt via the adjacency selector
below — `display: contents` removes the box so `break-after:
page` on .tpl-page itself can't fire. */
.tpl-canvas,
.tpl-page {
display: contents;
}
/* When a second (or later) .tpl-page follows another, its first
block must start on a new physical page — replaces the
`.tpl-page { break-after: page }` rule we lost above. Even
though .tpl-page is `display: contents` and has no box, CSS
selectors still operate on the DOM tree so the selector targets
the right .tpl-block. */
.tpl-page + .tpl-page > .tpl-block:first-child {
break-before: page;
page-break-before: always;
}
/* Drop editor chrome on print — sidebar, library, properties.
Selectors here are deliberately wider than the earlier block so
new toolbars don't leak into a print job. */
.tplv2-library,
.tplv2-properties,
.tplv2-toolbar,
.dashboard-header,
.tplv2-modal-backdrop,
.navmenu,
.sidebar { display: none !important; }
/* Inline editors should not bleed into a printed PDF. */
.tpl-heading-inline-edit,
.tpl-terms-label-input { display: none !important; }
.tpl-paragraph-placeholder { display: none !important; }
/* Removed the per-paper-size width/min-height block (.tpl-page-a4 etc.)
that pinned each .tpl-page to the full physical paper size. With
@page margin: 12 mm controlling the printable area, those fixed
210mm/297mm constraints just made the element overflow the
printable area; let content flow inside the @page area instead. */
}
/* ── Responsive editor (Phase 7 — tablet / phone) ────────────────────
Existing min-width:1280px rule above goes 3-column when the viewport
is wide enough. These breakpoints harden the small-screen experience
for clinicians editing on a tablet (~768-1280px) or a phone (<700px).
1280px — collapse to single-column, library on top, canvas middle,
properties at the bottom. Pin order via flex `order`.
700px  — phone view: shrink library tiles to 3 columns, drop the
drag handles (no precision on touch), trim modal padding.
*/
@media (max-width: 1280px) {
.tplv2-editor {
grid-template-columns: 1fr;
gap: 1rem;
}
.tplv2-library,
.tplv2-properties {
position: static;
top: auto;
}
.tplv2-properties  { order: 3; }
.tplv2-library     { order: 1; }
.tplv2-canvas-wrap { order: 2; }
}
@media (max-width: 700px) {
.tplv2-lib-grid { grid-template-columns: 1fr 1fr 1fr; }
.tplv2-page-list { font-size: 0.85rem; }
.tplv2-modal { max-width: 100% !important; margin: 0 0.5rem; }
.tpl-block-handle { display: none !important; }
/* Library tiles get a bit more breathing room on a touch device. */
.tplv2-lib-tile { padding: 0.7rem 0.4rem; min-height: 64px; }
}
/* ── Library search filter ───────────────────────────────────────── */
/* Lives above the block-tile grid in the left panel — substring matches
against tile Name and Description, plus snippet names when applicable. */
.tplv2-library-search {
position: relative;
margin: 0 0 0.6rem 0;
}
.tplv2-library-search .form-control {
padding-left: 2rem;
padding-right: 2rem;
font-size: 0.85rem;
}
.tplv2-library-search-icon {
position: absolute;
left: 0.55rem;
top: 50%;
transform: translateY(-50%);
color: #94a3b8;
pointer-events: none;
font-size: 0.8rem;
}
.tplv2-library-search-clear {
position: absolute;
right: 0.35rem;
top: 50%;
transform: translateY(-50%);
width: 1.6rem;
height: 1.6rem;
padding: 0;
background: transparent;
border: 0;
color: #6b7280;
}
.tplv2-library-search-clear:hover { color: #1f2937; }
/* ── Snippets section in the library panel ───────────────────────── */
.tplv2-snippets-head {
margin-top: 1rem;
}
.tplv2-snippets {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.4rem;
margin-bottom: 0.6rem;
}
.tplv2-snippet-tile {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.2rem;
padding: 0.55rem 0.4rem;
min-height: 64px;
border-radius: 6px;
background: rgba(30, 95, 174, 0.04);
border: 1px dashed rgba(30, 95, 174, 0.35);
color: var(--text-primary, #2B3540);
font-size: 0.72rem;
text-align: center;
cursor: pointer;
transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
}
.tplv2-snippet-tile:hover {
background: rgba(30, 95, 174, 0.1);
border-color: var(--tpl-primary, #0F4E4C);
transform: translateY(-1px);
}
.tplv2-snippet-icon {
font-size: 1rem;
color: var(--tpl-primary, #0F4E4C);
}
.tplv2-snippet-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.tplv2-snippet-delete {
position: absolute;
top: 2px;
right: 2px;
width: 1.3rem;
height: 1.3rem;
padding: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.85);
color: #b3261e;
border: 0;
opacity: 0;
transition: opacity 120ms ease;
}
.tplv2-snippet-tile:hover .tplv2-snippet-delete { opacity: 1; }
/* ── Locked-block visuals (edit mode) ────────────────────────────── */
/* Locked blocks get a subtle ring + a small padlock badge in the
corner. The drag handle is faded to communicate that drag is off. */
.tpl-block.is-locked {
outline: 1px dashed rgba(15, 39, 69, 0.35);
outline-offset: 2px;
}
.tpl-block.is-locked .tpl-block-handle {
opacity: 0.35;
pointer-events: none;
cursor: not-allowed;
}
.tpl-block-lock-badge {
position: absolute;
top: 4px;
right: 4px;
width: 1.4rem;
height: 1.4rem;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(15, 39, 69, 0.2);
color: #475569;
font-size: 0.7rem;
line-height: 1;
z-index: 3;
pointer-events: none;
}
/* ── TableBlock zebra + bold-header toggles ─────────────────────── */
.tpl-table.is-zebra tbody tr:nth-child(even) td {
background: rgba(15, 39, 69, 0.03);
}
.tpl-table.is-bold-header thead th {
font-weight: 700;
color: #1f2937;
}
.tpl-table-caption {
caption-side: top;
text-align: start;
padding: 0.3rem 0.1rem 0.4rem;
color: var(--tpl-accent, #0C3B3A);
font-weight: 600;
font-size: 0.85rem;
}
.tpl-table .tpl-table-empty td {
color: #94a3b8;
text-align: center;
font-style: italic;
}
/* Editor: a single row's per-cell editor grid expands to fit columns,
keeping the input fields snug. */
.tplv2-table-row-editor {
align-items: center;
flex-wrap: wrap;
}
.tplv2-table-row-editor .form-control {
min-width: 80px;
flex: 1 1 100px;
}
/* ── CalendarBlock ─────────────────────────────────────────────── */
.tpl-calendar {
width: 100%;
}
.tpl-calendar .tpl-cal-title {
font-weight: 700;
font-size: 0.95rem;
text-align: center;
color: var(--tpl-accent, #0C3B3A);
margin-bottom: 0.5rem;
}
.tpl-cal-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
background: var(--tpl-border, #E1E5EA);
border: 1px solid var(--tpl-border, #E1E5EA);
border-radius: 4px;
overflow: hidden;
}
.tpl-cal-dow {
background: #f1f5f9;
text-align: center;
padding: 0.3rem 0;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.04em;
font-weight: 600;
color: #475569;
}
.tpl-cal-day {
background: var(--tpl-surface, #ffffff);
padding: 0.25rem 0.3rem;
min-height: 64px;
font-size: 0.72rem;
display: flex;
flex-direction: column;
gap: 2px;
}
.tpl-cal-day-num {
font-weight: 600;
color: var(--tpl-accent, #0C3B3A);
}
.tpl-cal-day.is-other-month {
background: #f8fafc;
}
.tpl-cal-day.is-other-month .tpl-cal-day-num {
color: #cbd5e1;
}
.tpl-cal-day.is-today {
background: rgba(30, 95, 174, 0.08);
box-shadow: inset 0 0 0 1px var(--tpl-primary, #0F4E4C);
}
.tpl-cal-day.is-today .tpl-cal-day-num {
color: var(--tpl-primary, #0F4E4C);
}
.tpl-cal-event {
display: inline-block;
padding: 1px 4px;
border-radius: 3px;
color: #ffffff;
background: var(--tpl-primary, #0F4E4C);
font-size: 0.66rem;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
.tpl-cal-event-more {
font-size: 0.65rem;
color: #6b7280;
font-style: italic;
}
/* Agenda mode */
.tpl-calendar-agenda {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.tpl-cal-agenda-empty {
color: #94a3b8;
font-style: italic;
padding: 0.5rem;
text-align: center;
}
.tpl-cal-agenda-header {
font-weight: 700;
color: var(--tpl-accent, #0C3B3A);
border-bottom: 1px solid var(--tpl-border, #E1E5EA);
padding: 0.4rem 0 0.2rem;
margin-top: 0.4rem;
font-size: 0.85rem;
}
.tpl-cal-agenda-header:first-of-type { margin-top: 0; }
.tpl-cal-agenda-row {
display: grid;
grid-template-columns: 7rem 1fr;
gap: 0.5rem;
padding: 0.2rem 0;
font-size: 0.82rem;
}
.tpl-cal-agenda-row.is-today { background: rgba(30, 95, 174, 0.06); border-radius: 3px; }
.tpl-cal-agenda-date {
color: #475569;
font-variant-numeric: tabular-nums;
}
.tpl-cal-agenda-label {
display: inline-flex;
align-items: center;
gap: 0.4rem;
flex-wrap: wrap;
}
.tpl-cal-agenda-dot {
width: 0.7rem;
height: 0.7rem;
border-radius: 50%;
background: var(--tpl-primary, #0F4E4C);
display: inline-block;
}
.tpl-cal-agenda-note {
color: #64748b;
font-style: italic;
}
/* Editor: per-event row inside the property panel. Keep inputs from
wrapping into tiny columns. */
.tplv2-calendar-event-row {
align-items: center;
flex-wrap: wrap;
}
.tplv2-calendar-event-row .form-control { min-width: 90px; }
.d-row {
display: flex;
flex-direction: row;
gap: var(--space-md);
margin-bottom: var(--space-lg);
width: 100%;
}
.d-row * {
flex: 1;
margin: 0;
}
.form-select {
/* Remove default styling */
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
/* Optional: remove background image if any */
background-image: none;
background-repeat: no-repeat;
background-position: right center;
}
/* Remove arrow in IE10+ / Edge */
select::-ms-expand {
display: none;
}
.clickable {
cursor: pointer;
display: inline-block;
padding: 8px;
margin: revert-layer 8px;
background: transparent;
}
.clickable-min {
cursor: pointer;
display: inline-block;
background: transparent;
}
.clickable:hover {
background-color: var(--color-primary);
border-radius: 10%;
color: snow;
}
/* wwwroot/css/utils.css */
/* =============================
Utility classes for common tasks
============================= */
/*-----------------------------
Spacing utilities (margin)
------------------------------*/
.m-0 {
margin: 0 !important;
}
.m-1 {
margin: 0.25rem !important;
}
.m-2 {
margin: 0.5rem !important;
}
.m-3 {
margin: 1rem !important;
}
.m-4 {
margin: 1.5rem !important;
}
.m-5 {
margin: 3rem !important;
}
.mt-0 {
margin-top: 0 !important;
}
.mt-1 {
margin-top: 0.25rem !important;
}
.mt-2 {
margin-top: 0.5rem !important;
}
.mt-3 {
margin-top: 1rem !important;
}
.mt-4 {
margin-top: 1.5rem !important;
}
.mt-5 {
margin-top: 3rem !important;
}
.me-0 {
margin-inline-end: 0 !important;
}
.me-1 {
margin-inline-end: 0.25rem !important;
}
.me-2 {
margin-inline-end: 0.5rem !important;
}
.me-3 {
margin-inline-end: 1rem !important;
}
.me-4 {
margin-inline-end: 1.5rem !important;
}
.me-5 {
margin-inline-end: 3rem !important;
}
.mb-0 {
margin-bottom: 0 !important;
}
.mb-1 {
margin-bottom: 0.25rem !important;
}
.mb-2 {
margin-bottom: 0.5rem !important;
}
.mb-3 {
margin-bottom: 1rem !important;
}
.mb-4 {
margin-bottom: 1.5rem !important;
}
.mb-5 {
margin-bottom: 3rem !important;
}
.ms-0 {
margin-inline-start: 0 !important;
}
.ms-1 {
margin-inline-start: 0.25rem !important;
}
.ms-2 {
margin-inline-start: 0.5rem !important;
}
.ms-3 {
margin-inline-start: 1rem !important;
}
.ms-4 {
margin-inline-start: 1.5rem !important;
}
.ms-5 {
margin-inline-start: 3rem !important;
}
.mx-0 {
margin-inline: 0 !important;
}
.mx-1 {
margin-inline: 0.25rem !important;
}
.mx-2 {
margin-inline: 0.5rem !important;
}
.mx-3 {
margin-inline: 1rem !important;
}
.mx-4 {
margin-inline: 1.5rem !important;
}
.mx-5 {
margin-inline: 3rem !important;
}
.my-0 {
margin-block: 0 !important;
}
.my-1 {
margin-block: 0.25rem !important;
}
.my-2 {
margin-block: 0.5rem !important;
}
.my-3 {
margin-block: 1rem !important;
}
.my-4 {
margin-block: 1.5rem !important;
}
.my-5 {
margin-block: 3rem !important;
}
/*-----------------------------
Spacing utilities (padding)
------------------------------*/
.p-0 {
padding: 0 !important;
}
.p-1 {
padding: 0.25rem !important;
}
.p-2 {
padding: 0.5rem !important;
}
.p-3 {
padding: 1rem !important;
}
.p-4 {
padding: 1.5rem !important;
}
.p-5 {
padding: 3rem !important;
}
.pt-0 {
padding-top: 0 !important;
}
.pt-1 {
padding-top: 0.25rem !important;
}
.pt-2 {
padding-top: 0.5rem !important;
}
.pt-3 {
padding-top: 1rem !important;
}
.pt-4 {
padding-top: 1.5rem !important;
}
.pt-5 {
padding-top: 3rem !important;
}
.pe-0 {
padding-inline-end: 0 !important;
}
.pe-1 {
padding-inline-end: 0.25rem !important;
}
.pe-2 {
padding-inline-end: 0.5rem !important;
}
.pe-3 {
padding-inline-end: 1rem !important;
}
.pe-4 {
padding-inline-end: 1.5rem !important;
}
.pe-5 {
padding-inline-end: 3rem !important;
}
.pb-0 {
padding-bottom: 0 !important;
}
.pb-1 {
padding-bottom: 0.25rem !important;
}
.pb-2 {
padding-bottom: 0.5rem !important;
}
.pb-3 {
padding-bottom: 1rem !important;
}
.pb-4 {
padding-bottom: 1.5rem !important;
}
.pb-5 {
padding-bottom: 3rem !important;
}
.ps-0 {
padding-inline-start: 0 !important;
}
.ps-1 {
padding-inline-start: 0.25rem !important;
}
.ps-2 {
padding-inline-start: 0.5rem !important;
}
.ps-3 {
padding-inline-start: 1rem !important;
}
.ps-4 {
padding-inline-start: 1.5rem !important;
}
.ps-5 {
padding-inline-start: 3rem !important;
}
.px-0 {
padding-inline: 0 !important;
}
.px-1 {
padding-inline: 0.25rem !important;
}
.px-2 {
padding-inline: 0.5rem !important;
}
.px-3 {
padding-inline: 1rem !important;
}
.px-4 {
padding-inline: 1.5rem !important;
}
.px-5 {
padding-inline: 3rem !important;
}
.py-0 {
padding-block: 0 !important;
}
.py-1 {
padding-block: 0.25rem !important;
}
.py-2 {
padding-block: 0.5rem !important;
}
.py-3 {
padding-block: 1rem !important;
}
.py-4 {
padding-block: 1.5rem !important;
}
.py-5 {
padding-block: 3rem !important;
}
/*-----------------------------
Display & visibility
------------------------------*/
.d-none {
display: none !important;
}
.d-inline {
display: inline !important;
}
.d-inline-block {
display: inline-block !important;
}
.d-block {
display: block !important;
}
.d-flex {
display: flex !important;
}
.d-inline-flex {
display: inline-flex !important;
}
.d-grid {
display: grid !important;
}
.visible {
visibility: visible !important;
}
.invisible {
visibility: hidden !important;
}
/*-----------------------------
Flexbox utilities
------------------------------*/
.flex-row {
display: flex !important;
flex-direction: row !important;
}
.flex-column {
flex-direction: column !important;
}
.flex-wrap {
flex-wrap: wrap !important;
}
.justify-content-start {
justify-content: flex-start !important;
}
.justify-content-center {
justify-content: center !important;
}
.justify-content-end {
justify-content: flex-end !important;
}
.align-items-start {
align-items: flex-start !important;
}
.align-items-center {
align-items: center !important;
}
.align-items-end {
align-items: flex-end !important;
}
.flex-grow-0 {
flex-grow: 0 !important;
}
.flex-grow-1 {
flex-grow: 1 !important;
}
.flex-shrink-0 {
flex-shrink: 0 !important;
}
.flex-shrink-1 {
flex-shrink: 1 !important;
}
/*-----------------------------
Flexbox utilities
------------------------------*/
.flex-gap {
gap: 0.5rem;
}
.flex-gap-1 {
gap: 1rem;
}
.flex-gap-2 {
gap: 1.5rem;
}
/*-----------------------------
Text utilities
------------------------------*/
.text-start {
text-align: left !important;
}
.text-center {
text-align: center !important;
}
.text-end {
text-align: right !important;
}
.text-lowercase {
text-transform: lowercase !important;
}
.text-uppercase {
text-transform: uppercase !important;
}
.text-capitalize {
text-transform: capitalize !important;
}
.font-weight-light {
font-weight: 300 !important;
}
.font-weight-normal {
font-weight: 400 !important;
}
.font-weight-bold {
font-weight: 700 !important;
}
/*-----------------------------
Sizing & width
------------------------------*/
.w-25 {
width: 25% !important;
}
.w-50 {
width: 50% !important;
}
.w-75 {
width: 75% !important;
}
.w-100 {
width: 100% !important;
}
.w-auto {
width: auto !important;
}
.min-w-300 {
min-width: 300px !important;
}
.max-wp-100 {
max-width: 100% !important;
}
.h-25 {
height: 25% !important;
}
.h-50 {
height: 50% !important;
}
.h-75 {
height: 75% !important;
}
.h-100 {
height: 100% !important;
}
.h-auto {
height: auto !important;
}
/*-----------------------------
Float utilities
------------------------------*/
.float-start {
float: left !important;
}
.float-end {
float: right !important;
}
.float-none {
float: none !important;
}
/*-----------------------------
Position utilities
------------------------------*/
.position-static {
position: static !important;
}
.position-relative {
position: relative !important;
}
.position-absolute {
position: absolute !important;
}
.position-fixed {
position: fixed !important;
}
.position-sticky {
position: sticky !important;
}
/*-----------------------------
Border & rounding
------------------------------*/
.border {
border: 1px solid #dee2e6 !important;
}
.border-top {
border-top: 1px solid #dee2e6 !important;
}
.border-end {
border-inline-end: 1px solid #dee2e6 !important;
}
.border-bottom {
border-bottom: 1px solid #dee2e6 !important;
}
.border-start {
border-inline-start: 1px solid #dee2e6 !important;
}
.rounded {
border-radius: 0.25rem !important;
}
.rounded-circle {
border-radius: 50% !important;
}
.rounded-pill {
border-radius: 50rem !important;
}
/*-----------------------------
Shadow utilities
------------------------------*/
.shadow-none {
box-shadow: none !important;
}
.shadow-sm {
box-shadow: 0 0.125rem 0.25rem rgba(0,0,0,0.075) !important;
}
.shadow {
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.15) !important;
}
.shadow-lg {
box-shadow: 0 1rem 3rem rgba(0,0,0,0.175) !important;
}
/*-----------------------------
Helper utilities
------------------------------*/
/* Clearfix */
.clearfix::after {
content: "";
display: table;
clear: both;
}
/* Visually hidden (accessibility) */
.visually-hidden,
.sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0,0,0,0) !important;
white-space: nowrap !important;
border: 0 !important;
}
.pos-wrapper {
position: relative;
width: 100%;
}
.pos-right-top {
position: absolute;
top: 0;
right: 0;
}
.pos-right-middle {
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
margin-right: 0.5rem;
}
.pos-right-bottom {
position: absolute;
right: 0;
bottom: 0;
}
.pos-left-middle {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
margin-left: 0.5rem;
margin-left: 0.5rem;
}
