/*!
 * theme36t_engine.css — Theme36T static consumer sheet
 * ----------------------------------------------------
 * Loaded ONCE via app_include_css / web_include_css. Never changes per theme.
 *
 * CONTRACT
 *   Every rule in this file is INERT until the rendered variables sheet supplies a --t36t-*
 *   value. Each declaration falls back to the exact stock v16 value it replaces, so with no
 *   theme applied — or after uninstall — the desk renders as stock ERPNext.
 *
 *   Anything v16 already exposes as a token (accent, surfaces, borders, text, radius, shadows,
 *   fonts, scrollbar colours, --page-head-height, --sidebar-width, --page-max-width, padding
 *   scale) is set by the variables sheet and has NO RULE HERE. If you are about to add a rule,
 *   check docs/theme36t-v16-selector-map.md first — if a token exists, use the token.
 *
 *   Every rule below is justified by a tokenless feature or a verified v16 gap. The MAP
 *   reference points at the section of docs/theme36t-v16-selector-map.md that proves it.
 *
 * Verified against frappe 16.31.0 / erpnext 16.32.3 (version-16).
 */

/* ==========================================================================
   1. Engine defaults
   Only values with no v16 equivalent. Colours are NOT defaulted here — each
   rule inlines the stock token it falls back to.
   ========================================================================== */

:root {
	--t36t-anim-fast: 120ms;
	--t36t-anim-normal: 220ms;
	--t36t-anim-slow: 380ms;
	--t36t-anim-duration: var(--t36t-anim-normal);
	--t36t-anim-ease: cubic-bezier(0.4, 0, 0.2, 1);
	--t36t-anim-distance: 6px;
}

/* ==========================================================================
   2. Global
   ========================================================================== */

/* GAP: core scopes ::selection under `html, body` (desk/desktop.scss:4, dark.scss:259),
   so a bare ::selection would lose. Match the descendant specificity; we load later. */
html ::selection,
body ::selection {
	color: var(--t36t-selection-fg, var(--neutral-black));
	background: var(--t36t-selection-bg, var(--gray-300));
}

/* TOKENLESS: scrollbar *width* has no variable (colours do — --scrollbar-thumb-color /
   --scrollbar-track-color are set by the variables sheet). Gated, so Default = untouched. */
body[data-t36t-scrollbar="thin"] * {
	scrollbar-width: thin;
}
body[data-t36t-scrollbar="thin"] *::-webkit-scrollbar {
	width: 4px;
	height: 4px;
}
/* core sets `body::-webkit-scrollbar { width: 12px !important }` (desk/scrollbar.scss) */
body[data-t36t-scrollbar="thin"]::-webkit-scrollbar {
	width: 6px !important;
	height: 6px !important;
}
body[data-t36t-scrollbar="hidden"] * {
	scrollbar-width: none;
}
body[data-t36t-scrollbar="hidden"] *::-webkit-scrollbar,
body[data-t36t-scrollbar="hidden"]::-webkit-scrollbar {
	display: none !important;
}

/* ==========================================================================
   3. Typography
   Font FAMILY is --font-stack (a token) — no rule needed.
   Heading weights are --weight-* (tokens) — no rule needed.
   ========================================================================== */

/* GAP: base size is Bootstrap's hardcoded `body { font-size: .875rem }`; the --text-*
   scale is a separate ramp and does not drive it. */
body {
	font-size: var(--t36t-base-font-size, 0.875rem);
}

/* GAP: bootstrap pins `h1..h6 { font-weight: 500 }`; the --weight-* ramp never reaches
   headings. Fallback repeats the compiled literal. */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
	font-weight: var(--t36t-heading-weight, 500);
}

/* GAP: `a { color: var(--text-color) }` (desk/global.scss) — links borrow the body text
   token and have none of their own. Bare `a` is specificity (0,0,1), so every scoped
   link rule in core (breadcrumbs, list rows, sidebars) still wins. */
a {
	color: var(--t36t-link-color, var(--text-color));
}

/* ==========================================================================
   4. Left rail — .body-sidebar (v16's home for app identity)
   Rail COLOURS are tokens (--surface-menu-bar, --sidebar-hover-color,
   --sidebar-active-color, --sidebar-border-color) and --sidebar-width sets the
   expanded width. Only the tokenless parts live here.
   MAP §2.1
   ========================================================================== */

/* TOKENLESS: no variable for a rail background image.
   GAP: collapsed width is a hardcoded 50px on both the rail and its placeholder.
   The expanded rules (.body-sidebar-container.expanded .body-sidebar) are more
   specific, so var(--sidebar-width) still wins when the rail is open. */
.body-sidebar {
	/* veil first so it composites OVER the image, fading it toward the rail colour */
	/* veil over image over fill: a gradient rail still accepts an image on top. */
	background-image: var(--t36t-sidebar-veil, none), var(--t36t-sidebar-bg-image, none),
		var(--t36t-sidebar-fill, none);
	background-size: var(--t36t-chrome-bg-size, cover);
	background-position: center;
	background-repeat: var(--t36t-chrome-bg-repeat, no-repeat);
	width: var(--t36t-sidebar-collapsed-width, 50px);
}
.body-sidebar-container .body-sidebar-placeholder {
	width: var(--t36t-sidebar-collapsed-width, 50px);
}

/* GAP: --ink-gray-6 is global; scoping it to the rail lets the rail carry brand
   text colour without shifting ink everywhere else. */
.body-sidebar .item-anchor {
	color: var(--t36t-sidebar-text, var(--ink-gray-6));
}
.body-sidebar .active-sidebar .item-anchor {
	color: var(--t36t-sidebar-active-text, var(--t36t-sidebar-text, var(--ink-gray-6)));
}

/* GAP: item radius is hardcoded 8px in the hover mixin and .active-sidebar.
   The first selector reproduces core's chain exactly so specificity matches. */
.body-sidebar .standard-sidebar-item:not(.active-sidebar):has(a:not(.section-break)):hover,
.active-sidebar {
	border-radius: var(--t36t-sidebar-item-radius, 8px);
}

/* GAP: core's hover mixin gives the item a background but never a text colour, so
   Sidebar Hover Background could be set dark with the text left unreadable.
   Falls back to the normal rail text, i.e. no change unless chosen. */
.body-sidebar .standard-sidebar-item:hover .item-anchor {
	color: var(--t36t-sidebar-hover-text, var(--t36t-sidebar-text, var(--ink-gray-6)));
}

/* GAP: rail logo box is a hardcoded 32x32 (desk/sidebar_header.scss). */
.body-sidebar .header-logo {
	width: var(--t36t-desk-logo-height, 32px);
	height: var(--t36t-desk-logo-height, 32px);
	padding: var(--t36t-desk-logo-padding, 0);
	box-sizing: content-box;
	/* The collapsed rail is 50px wide; scale down rather than overflow it. */
	max-width: 100%;
}
.body-sidebar .header-logo img {
	object-fit: var(--t36t-logo-fit, contain);
}

/* GAP: .sidebar-header is a fixed 48px with 8px padding, so it clips anything
   over 32px — which makes every larger Desk Logo Size a dead control. max()
   keeps v16's height until the logo actually needs more room. */
.body-sidebar .sidebar-header {
	height: max(48px, calc(var(--t36t-desk-logo-height, 32px) + 16px));
}

/* ==========================================================================
   5. Top bar — .page-head
   DECISION: the bar stays NEUTRAL. It inherits --bg-color exactly like stock
   v16, and nothing here changes that unless an admin explicitly picks a fill.
   Height is --page-head-height (a token) — no rule needed.
   MAP §2.2
   ========================================================================== */

/* TOKENLESS: one rule covers solid / gradient / image. The renderer emits a flat
   linear-gradient(c, c) for a solid fill, so the bar has exactly one lever and
   stays untouched while --t36t-topbar-fill is unset. */
/* On a pseudo-element, not on .page-head itself.

   backdrop-filter makes an element a CONTAINING BLOCK for absolutely positioned
   descendants — which is a different thing from the stacking context it also
   creates. .page-head hosts the Menu and Actions dropdowns, so with the filter on
   the element Popper measures against the wrong reference, paints the menu in the
   wrong place, then corrects on its first update: a visible jump on every open.

   z-index -1 sits above .page-head's own background and below its content, so the
   bar looks identical while the element stays out of the positioning chain. */
.page-head::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-image: var(--t36t-topbar-veil, none), var(--t36t-topbar-fill, none);
	background-size: var(--t36t-topbar-bg-size, var(--t36t-chrome-bg-size, cover));
	background-position: var(--t36t-topbar-bg-position, center);
	background-repeat: var(--t36t-topbar-bg-repeat, var(--t36t-chrome-bg-repeat, no-repeat));
	backdrop-filter: var(--t36t-topbar-blur, none);
	-webkit-backdrop-filter: var(--t36t-topbar-blur, none);
}

/* --- Desktop launcher top bar (/desk) ------------------------------------
   The desktop page does not use .page-head. It renders its own
   `<header class="desktop-navbar navbar navbar-expand navbar-container">`
   (desk/page/desktop/desktop.html) which carries no background of its own and
   sizes from --desktop-navbar-height, a local literal in desktop.css. So the
   moment a Top Bar fill or height was set, /desk diverged from every other desk
   page. Same tokens and the same ::before treatment, so the two bars match.

   .navbar-container is already position: sticky, which is a positioned element,
   so the absolutely positioned ::before insets against it without help. */
.desktop-navbar.navbar-container {
	height: var(--page-head-height, var(--desktop-navbar-height, 52px));
}
.desktop-navbar::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-image: var(--t36t-topbar-veil, none), var(--t36t-topbar-fill, none);
	background-size: var(--t36t-topbar-bg-size, var(--t36t-chrome-bg-size, cover));
	background-position: var(--t36t-topbar-bg-position, center);
	background-repeat: var(--t36t-topbar-bg-repeat, var(--t36t-chrome-bg-repeat, no-repeat));
	backdrop-filter: var(--t36t-topbar-blur, none);
	-webkit-backdrop-filter: var(--t36t-topbar-blur, none);
}

/* GAP: breadcrumb colours are the global --ink-gray-* ramp; scoping lets the bar
   diverge when it carries a fill. */
.page-head .navbar-breadcrumbs a {
	color: var(--t36t-breadcrumb-color, var(--ink-gray-5));
}
.page-head .navbar-breadcrumbs li:last-child > a {
	color: var(--t36t-breadcrumb-active-color, var(--ink-gray-9)) !important; /* core uses !important */
}

/* GAP: upstream hardcodes `background-color: white` (desk/breadcrumb.scss:1-5),
   which breaks in dark mode. The fallback repeats that literal so the sheet stays
   inert; the renderer supplies `transparent` (or a colour) once a theme is active. */
.breadcrumb {
	background-color: var(--t36t-breadcrumb-bg, white);
}

/* --- Sticky bars --------------------------------------------------------
   .form-tabs-list (z 5) and the list column header stay put while rows scroll
   underneath, so they need a surface or the text collides. Both fall back to the
   stock value, and both defer to the translucent panel when no colour is chosen. */
body .form-tabs-list {
	background-color: var(--t36t-tabs-bg, var(--card-bg));
	backdrop-filter: var(--t36t-tabs-blur, none);
	-webkit-backdrop-filter: var(--t36t-tabs-blur, none);
}
body .form-tabs-list .form-tabs .nav-item .nav-link {
	background-color: transparent;
	color: var(--t36t-tabs-text, var(--text-light));
}
/* GAP: v16 styles .active and :focus-visible on a tab but never :hover
   (desk/form.scss:497), so the strip gave no pointer feedback.

   The background goes on the <li class="nav-item">, not the .nav-link button
   inside it. The button carries `margin: 0 var(--margin-md)`, so painting it
   leaves an unhighlighted gutter either side; the li is the full tab cell. The
   rule above already makes the button transparent, so the li's colour shows
   through instead of v16's opaque --card-bg covering it.

   Hover is the pseudo-class, NOT the .show class. Tab.toggle() does
   `tab_link.toggleClass("show", show)` (form/tab.js:75) on the li itself, so
   .show marks a tab as VISIBLE and every visible tab carries it permanently --
   matching it made the hover colours the resting colours. */
body .form-tabs-list .form-tabs .nav-item:hover {
	background-color: var(--t36t-tabs-hover-bg, transparent);
}
body .form-tabs-list .form-tabs .nav-item:hover .nav-link {
	color: var(--t36t-tabs-hover-text, var(--t36t-tabs-text, var(--text-light)));
}
body .form-tabs-list .form-tabs .nav-item .nav-link.active {
	color: var(--t36t-tabs-active-text, var(--text-neutral));
	/* GAP: the active underline is pinned to --text-color and never follows the
	   accent. Chain reproduces core's specificity (0,4,0). */
	border-bottom-color: var(--t36t-tabs-indicator, var(--t36t-accent, var(--text-color)));
}

/* GAP: `.form-tabs-list { position: sticky }` (desk bundle). The strip stays put
   while the form scrolls, so a transparent fallback puts field text through the tab
   labels. An explicit tab colour still wins; only the fallback is now solid. */
body[data-t36t-panels="translucent"] .form-tabs-list {
	background-color: var(--t36t-tabs-bg, var(--t36t-panel-bg-solid, var(--card-bg)));
}

/* List column header — core paints it --subtle-fg and it sits above scrolling rows. */
body .list-row-head {
	background-color: var(--t36t-list-header-bg, var(--subtle-fg));
	color: var(--t36t-list-header-text, inherit);
}
body .list-row-head .level-right,
body .list-row-head:hover .level-right,
body .list-row-head .level-right:hover {
	background-color: var(--t36t-list-header-bg, var(--subtle-fg));
}

/* ==========================================================================
   6. Page background
   Colour is --bg-color (a token). Only the image is tokenless.
   Note: the target is `body`, NOT `#body` — #body is the empty SPA mount point.
   MAP §2.4
   ========================================================================== */

/* Two placements, because where a background belongs depends on the design.
   The desk body is a flex row of [.body-sidebar-container, .main-section], so
   .main-section (desk/main.scss) is exactly the working area beside the rail. */

/* Whole window — spans everything, the rail sits on top and crops it. */
body[data-t36t-pagebg="window"] {
	/* Two layers: a flat veil over the image, veil first. CSS cannot set opacity on
	   a background-image, and `none, none` is valid — so with no image and no
	   opacity chosen this still paints nothing. */
	background-image: var(--t36t-page-bg-veil, none), var(--t36t-page-bg-image, none);
	background-size: cover, var(--t36t-page-bg-size, cover);
	background-position: center, var(--t36t-page-bg-position, center);
	background-repeat: no-repeat, var(--t36t-page-bg-repeat, no-repeat);
	background-attachment: fixed, fixed;
}

/* Content area only — centred beside the rail, which keeps its own colour. */
body[data-t36t-pagebg="content"] .main-section {
	/* Two layers: a flat veil over the image, veil first. */
	background-image: var(--t36t-page-bg-veil, none), var(--t36t-page-bg-image, none);
	background-size: cover, var(--t36t-page-bg-size, cover);
	background-position: center, var(--t36t-page-bg-position, center);
	background-repeat: no-repeat, var(--t36t-page-bg-repeat, no-repeat);
	/* scroll, NOT fixed: with fixed the positioning area is the viewport, which
	   would re-centre the image behind the sidebar and undo the whole point.
	   scroll pins it to this element's own box instead. */
	background-attachment: scroll, scroll;
}

/* Website pages have no left rail, so "content area only" has nothing to mean
   there — fall back to the page rather than showing no image at all. */
body[data-t36t-pagebg="content"]:not(:has(.main-section)) {
	/* Two layers: a flat veil over the image, veil first. CSS cannot set opacity on
	   a background-image, and `none, none` is valid — so with no image and no
	   opacity chosen this still paints nothing. */
	background-image: var(--t36t-page-bg-veil, none), var(--t36t-page-bg-image, none);
	background-size: cover, var(--t36t-page-bg-size, cover);
	background-position: center, var(--t36t-page-bg-position, center);
	background-repeat: no-repeat, var(--t36t-page-bg-repeat, no-repeat);
	background-attachment: fixed, fixed;
}

/* GAP: v16 paints .layout-main-section with an opaque --fg-color, and that panel
   covers almost the whole viewport on list, form and workspace pages — so a page
   background image is invisible exactly where people look. There is no token for
   panel transparency, so this is the tokenless fix.

   Gated: with no Content Panel Opacity chosen, nothing here applies and the panels
   stay solid, exactly like stock v16. .layout-main-section is the universal content
   container (ui/page.js builds it for every view), so one selector covers all three
   page types. */
/* Translucent content panels, so a page background image is visible on list and
   detail pages. v16 stacks several opaque layers between body and the content, and
   they cannot all be made translucent: alphas multiply, so 80% over 80% reads as
   96% and looks solid.

   The rule is therefore ONE surface per page — .layout-main-section, plus .page-head
   as its sibling — and every other layer in the chain goes fully transparent.

   Specificity is deliberate: each selector below was checked against the strongest
   core rule painting the same element, and beats it without !important. The form
   page especially, since core uses a three-class child chain for it. */

/* Wrappers above the surface: pure layout, no surface of their own. */
body[data-t36t-panels="translucent"] .page-container {
	background-color: transparent;
}

/* THE surface — .layout-main-section-wrapper, not .layout-main-section.
   The wrapper holds the content section AND the form footer that form.js appends
   as its sibling, so putting the surface here makes Comments and Activity part of
   one continuous panel instead of a second panel with a gap between them.
   Nothing in core paints the wrapper, so this adds rather than overrides. */
body[data-t36t-panels="translucent"] .layout-main-section-wrapper {
	/* position:relative with z-index:auto does NOT create a stacking context —
	   that distinction is the whole point of this rule. */
	position: relative;
	border-radius: var(--border-radius-md);
	padding-bottom: var(--padding-md);
}

/* The glass lives on a pseudo-element, not on the wrapper itself.

   backdrop-filter creates a stacking context. Put it on the wrapper and every
   z-index inside becomes relative to the wrapper — so the child-table row editor
   (.form-in-grid, z-index 1021) gets trapped and loses to the sticky .page-head
   at z-index 6, which is exactly what "Editing Row #1 appears behind" looks like.

   A ::before at z-index -1 paints the same glass while leaving the wrapper's own
   stacking untouched, so 1021 still resolves against the page. */
body[data-t36t-panels="translucent"] .layout-main-section-wrapper::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: inherit;
	pointer-events: none;
	background-color: var(--t36t-panel-bg, var(--fg-color));
	backdrop-filter: var(--t36t-panel-blur, none);
	-webkit-backdrop-filter: var(--t36t-panel-blur, none);
}

/* Sibling of the surface, so it gets its own — a touch clearer than the panel. */
/* The bar's own background goes transparent so the pseudo's translucent colour
   composites over the page image rather than over an opaque --bg-color. */
body[data-t36t-panels="translucent"] .page-head {
	background-color: transparent;
}
body[data-t36t-panels="translucent"] .page-head::before {
	background-color: var(--t36t-panel-head-bg, var(--bg-color));
	backdrop-filter: var(--t36t-topbar-blur, var(--t36t-panel-blur, none));
	-webkit-backdrop-filter: var(--t36t-topbar-blur, var(--t36t-panel-blur, none));
}

/* Everything nested inside the surface. Transparent, never translucent.
   .page-form  — list filter bar, prepended into .layout-main-section (page.js:164)
   .form-page  — detail body; core paints it via .std-form-layout>.form-layout>.form-page
   .form-tabs-list and its links — the sticky tab strip on a detail page */
body[data-t36t-panels="translucent"] .layout-main-section,
body[data-t36t-panels="translucent"] .layout-main-section.frappe-card,
body[data-t36t-panels="translucent"] .page-form,
body[data-t36t-panels="translucent"] .std-form-layout > .form-layout > .form-page,
body[data-t36t-panels="translucent"] .form-page {
	background-color: transparent;
}

/* GAP: `.list-row .level-right { position: sticky }` (desk bundle) — the right-hand
   strip holds its place while the row scrolls sideways, so it has row content
   passing under it, not the panel. It was previously cleared here, which is what
   made list rows print over their own action column. The header strip is left to
   the list-header rule above, which is already opaque. */
body[data-t36t-panels="translucent"] .list-row .level-right {
	background-color: var(--t36t-panel-bg-solid, var(--bg-color));
}

/* Row hover must still read, and core's rule is (0,3,0). */
body[data-t36t-panels="translucent"] .list-row-container:hover .level-right {
	background-color: var(--highlight-color);
}

/* More nested containers, found by walking each page type rather than guessing:
   report grids, the address/contact cards and child tables on a detail tab, the
   activity timeline, and workspace widgets. All the same rule — they sit inside
   the surface, so they must not paint over it. */
body[data-t36t-panels="translucent"] .report-summary,
body[data-t36t-panels="translucent"] .address-box,
body[data-t36t-panels="translucent"] .contact-box,
body[data-t36t-panels="translucent"] .form-grid,
body[data-t36t-panels="translucent"] .grid-body,
body[data-t36t-panels="translucent"] .grid-body .grid-static-col:not(.sticky-grid-col),
body[data-t36t-panels="translucent"] .new-timeline .activity-title,
body[data-t36t-panels="translucent"] .new-timeline .timeline-actions,
body[data-t36t-panels="translucent"] .new-timeline .timeline-item .timeline-content.frappe-card,
body[data-t36t-panels="translucent"] .widget,
body[data-t36t-panels="translucent"] .widget.onboarding-widget-box {
	background-color: transparent;
}

/* GAP: `.grid-static-col { background-color: var(--fg-color) }` (grid.scss:426).
   A frozen column is position:sticky, so it slides over its own neighbours as the
   grid scrolls sideways. Stripping its background for translucency therefore does
   not reveal the panel behind — it reveals the cells passing underneath, and the
   two sets of text overlap. It keeps a solid fill for that reason, tinted to match
   the panel instead of falling back to a stock white ledge. The checkbox and row
   number are sticky too (grid.scss:139) and need the same treatment. */
body[data-t36t-panels="translucent"] .grid-body .data-row .grid-static-col.sticky-grid-col,
body[data-t36t-panels="translucent"] .grid-body .data-row .row-check,
body[data-t36t-panels="translucent"] .grid-body .data-row .row-index {
	background-color: var(--t36t-panel-bg-solid, var(--fg-color));
}

/* The heading row is the same problem but a different colour: grid.scss:65 tints
   every heading cell with --subtle-fg, so filling the frozen ones with the panel
   colour instead would leave the header two-tone. Keep Frappe's own tint — it is
   already opaque, which is all the frozen cells actually need. */
body[data-t36t-panels="translucent"] .grid-heading-row .grid-static-col.sticky-grid-col,
body[data-t36t-panels="translucent"] .grid-heading-row .row-check,
body[data-t36t-panels="translucent"] .grid-heading-row .row-index {
	background-color: var(--subtle-fg);
}

/* The report grid is token-driven (--dt-cell-bg / --dt-header-cell-bg are declared
   on .datatable itself), so it takes variables rather than a selector fight. The
   header keeps a faint tint so column labels stay distinguishable from the rows. */
body[data-t36t-panels="translucent"] .datatable {
	background-color: transparent;
	--dt-cell-bg: transparent;
	/* Opaque: .dt-header is position:fixed (datatable style.css:236), so rows pass
	   under it rather than the page background showing through. */
	--dt-header-cell-bg: var(--t36t-panel-head-bg-solid, var(--subtle-fg));
}

/* GAP: frappe-datatable paints every cell with --dt-cell-bg (style.css:85), which
   is cleared just above. Its frozen columns are position:sticky
   (.dt-cell--sticky, style.css:179), so — exactly as in the child-table grid —
   clearing the background does not reveal the panel behind but the cells sliding
   underneath, and the two sets of text overlap. Frozen cells keep a solid fill. */
body[data-t36t-panels="translucent"] .datatable .dt-cell--sticky {
	background-color: var(--t36t-panel-bg-solid, var(--fg-color));
}

/* The column menu is position:fixed and painted with --dt-cell-bg (style.css:240),
   the same variable cleared above — so it floated see-through over the rows. It is
   a menu, not a surface, and must be opaque to be readable. */
body[data-t36t-panels="translucent"] .datatable .dt-dropdown__list {
	background-color: var(--t36t-panel-bg-solid, var(--fg-color));
}

/* Header cells carry both classes (cellmanager.js:845), so this needs the higher
   specificity to win — and it takes the header tint, not the panel one, so the
   frozen corner matches the rest of the header rather than the rows below it. */
body[data-t36t-panels="translucent"] .datatable .dt-cell--sticky.dt-cell--sticky-top {
	background-color: var(--t36t-panel-head-bg-solid, var(--subtle-fg));
}

/* Workspaces render through EditorJS in read-only mode, so .codex-editor is present
   in normal viewing and its rules are (0,7,0) — well above the (0,2,1) above. This
   matches their depth to reach header and shortcut widgets. */
body[data-t36t-panels="translucent"] [data-page-route="Workspaces"] .codex-editor
	.codex-editor__redactor .ce-block .ce-block__content .widget {
	background-color: transparent;
}

/* Comments and Activity are NOT inside .layout-main-section — form.js mounts the
   footer with appendTo(this.page.main.parent()), making it a sibling. That is why
   the surface lives on the shared wrapper above: the footer needs no background of
   its own, only room to breathe. */
body[data-t36t-panels="translucent"] .form-footer {
	background-color: transparent;
	padding-bottom: var(--padding-md);
}

/* Website portal pages have none of the desk containers, so there is no surface to
   make translucent — the image would show raw with nothing keeping text readable.

   The surface goes on the element carrying container_attributes() from
   templates/web.html, which is the row wrapping BOTH the portal sidebar and the
   content column. Putting it on <main> instead covered only the content and left
   the sidebar sitting on the bare image.

   div[data-path][id^="page-"] matches only web pages: desk's .page-container gets
   an id but no data-path (views/container.js), and <body> carries data-path but is
   not a div. */
body[data-t36t-panels="translucent"] div[data-path][id^="page-"] {
	position: relative;
	border-radius: var(--border-radius-lg);
	padding-top: var(--padding-md);
	padding-bottom: var(--padding-lg);
}

/* Same pseudo-element treatment as the desk wrapper, for the same reason:
   portal pages have dropdowns and dialogs that must not be trapped. */
body[data-t36t-panels="translucent"] div[data-path][id^="page-"]::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	border-radius: inherit;
	pointer-events: none;
	/* Block-level vars first so "Hide Workspace Block Overlay" can switch this one
	   layer off without touching the other 28 translucent rules. Unset, they fall
	   through to the shared panel values exactly as before. */
	background-color: var(--t36t-block-bg, var(--t36t-panel-bg, transparent));
	backdrop-filter: var(--t36t-block-blur, var(--t36t-panel-blur, none));
	-webkit-backdrop-filter: var(--t36t-block-blur, var(--t36t-panel-blur, none));
}

/* GAP: v16 paints inputs with --control-bg but gives their text no token of its
   own — it inherits the global --text-color. So an admin could darken the input
   background and had no way to lighten the text inside it. Fallback is that same
   global colour, so an unset theme is unchanged. */
.form-control,
select.form-control,
textarea.form-control,
.input-with-feedback {
	color: var(--t36t-control-text, var(--text-color));
}

/* ==========================================================================
   7. Buttons
   Primary button BACKGROUND is --btn-primary (a token) — no rule needed.
   MAP §2.3
   ========================================================================== */

/* GAP: primary button text has no token of its own — it borrows --neutral, which
   is shared with other surfaces and must not be repurposed. */
.btn.btn-primary {
	color: var(--t36t-btn-primary-fg, var(--neutral));
}
.btn.btn-primary:active {
	color: var(--t36t-btn-primary-fg, var(--gray-100)) !important; /* core uses !important */
}

/* GAP: v16 gives .btn-primary no :hover at all (common/buttons.scss:117 defines
   only the base and :active). So Primary Button Hover reached nothing but the
   login page, where .btn-login below is the sole consumer of the variable.

   Fallback is --btn-primary itself — the button's own background — so a theme
   that leaves the field blank produces no visible hover, exactly like stock v16.
   :active is left alone: core sets it with !important and that press feedback
   should stay. */
.btn.btn-primary:hover,
.btn.btn-primary:focus {
	background-color: var(--t36t-btn-primary-hover-bg, var(--btn-primary));
	color: var(--t36t-btn-primary-hover-fg, var(--t36t-btn-primary-fg, var(--neutral)));
}

/* GAP: .btn-default and .btn-secondary borrow --control-bg and --text-color
   (common/buttons.scss:80,106), both shared with inputs and body text, so they
   could not be themed on their own. Hover background is left to v16's real
   --btn-default-hover-bg token, which the renderer now writes when set; only the
   hover TEXT needs a rule, since core pins it to --text-color.
   Every fallback is the value core already used, so an unset theme is unchanged. */
.btn.btn-default,
.btn.btn-secondary {
	background-color: var(--t36t-btn-default-bg, var(--control-bg));
	color: var(--t36t-btn-default-fg, var(--text-color));
}
.btn.btn-default:hover,
.btn.btn-default:active,
.btn.btn-secondary:hover,
.btn.btn-secondary:active {
	color: var(--t36t-btn-default-hover-fg, var(--t36t-btn-default-fg, var(--text-color)));
}

/* GAP: .btn-primary-light compiles to literal greys via Sass button-variant(),
   so no token reaches it. Fallback is the compiled literal ($gray-300). */
.btn-primary-light {
	background-color: var(--t36t-accent-subtle, var(--gray-300));
}

/* ==========================================================================
   8. Shape — border width & style
   Radius is --border-radius* (tokens) and shadows are --shadow-* / --card-shadow
   (tokens) — no rules for those.
   v16 has NO border-width or border-style token, but restyling every border is
   sprawl, so this is opt-in and scoped to verified surfaces only.
   ========================================================================== */

body[data-t36t-borders="custom"] .widget.border,
body[data-t36t-borders="custom"] .page-head,
body[data-t36t-borders="custom"] .form-tabs-list,
body[data-t36t-borders="custom"] .modal-content,
body[data-t36t-borders="custom"] .frappe-card,
body[data-t36t-borders="custom"] .form-section {
	border-width: var(--t36t-border-width, 1px);
	border-style: var(--t36t-border-style, solid);
}

/* ==========================================================================
   9. Login page
   Card background/radius and input colours are tokens (--bg-color,
   --border-radius-lg, --outline-gray-2, --surface-white) — no rules.
   Logo SRC is a write-through to Website Settings.app_logo, not CSS.
   MAP §3
   ========================================================================== */

/* GAP: logo height is a hardcoded max-height: 32px. */
/* `body` prefix is load-bearing. login.bundle.scss:407 compiles to
   `.page-card-head img.app-logo` — the SAME selector as ours, so equal
   specificity (0,2,1) — and the login bundle is linked AFTER this sheet on
   /login. Its `max-height: 32px` therefore won and capped our height back to 32,
   which is why Login Logo Size appeared to do nothing. body takes us to (0,2,2). */
body .page-card-head img.app-logo {
	/* same as the navbar: max-height alone cannot grow a logo. */
	height: var(--t36t-login-logo-height, auto);
	max-height: var(--t36t-login-logo-height, 32px);
	max-width: var(--t36t-login-logo-maxw, 100%);
	width: auto;
	object-fit: var(--t36t-logo-fit, contain);
	padding: var(--t36t-login-logo-padding, 0);
	box-sizing: content-box;
}

/* Login-only override of the page background veil.

   The login page has no .main-section, so the "content" rule above matches it and
   the page background image is painted there too — with the site-wide
   --t36t-page-bg-veil. This lets the login page carry its own fade.

   body[data-path="login"] is set server-side by frappe, so it is available before
   any JS runs. Specificity (0,2,1) ties the rule above; this sheet declares it
   later, so it wins. Falls through to the site-wide veil when unset. */
body[data-path="login"][data-t36t-pagebg] {
	background-image: var(--t36t-login-page-veil, var(--t36t-page-bg-veil, none)),
		var(--t36t-page-bg-image, none);
}

/* TOKENLESS: no variable for a login background image. */
body[data-t36t-login="on"] {
	/* veil first so it composites OVER the image, same as the other bars */
	background-image: var(--t36t-login-bg-veil, none), var(--t36t-login-bg-image, none);
	background-size: var(--t36t-chrome-bg-size, cover);
	background-position: center;
	background-repeat: var(--t36t-chrome-bg-repeat, no-repeat);
}

/* TOKENLESS: blurring the login background image.

   filter: blur() on the body would blur the card and the form with it, so the
   blur goes on a pseudo-element that frosts what is painted BEHIND it. Same
   z-index: -1 trick as .page-head::before above: it sits over the body's own
   background but under every child, so the card stays sharp.

   position: fixed rather than absolute — the login body is short, and an
   absolute layer would stop at its height and leave the lower background
   unblurred. */
body[data-t36t-login="on"]::before {
	content: "";
	position: fixed;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	backdrop-filter: var(--t36t-login-bg-blur, none);
	-webkit-backdrop-filter: var(--t36t-login-bg-blur, none);
}

/* GAP: the login card's background is set six times over as
   `.for-login .page-card`, `.for-reset-password .page-card` and so on — (0,2,0)
   each, in a bundle that loads AFTER this sheet. Matching that would mean
   reproducing every .for-* variant, so !important is the honest fix here, the
   same call already made for .btn-login below. The fallback is --bg-color, the
   value v16 uses, so an unset theme looks identical.

   backdrop-filter needs no !important: nothing else sets it. */
.page-card {
	background-color: var(--t36t-login-card-bg, var(--bg-color)) !important;
	backdrop-filter: var(--t36t-login-card-blur, none);
	-webkit-backdrop-filter: var(--t36t-login-card-blur, none);
}

/* GAP: the card is centred by `margin: 0 auto` inside a fixed 371px max-width
   (login.bundle.scss:35-37), with no way to move it. Both margins are driven
   here: auto on the side the free space should fall. Defaults reproduce v16's
   centring exactly, so an unset theme is unchanged.

   The chain is (0,3,1) against v16's `.for-login .page-card` (0,2,0), so it wins
   despite the login bundle loading later. */
body[data-path="login"] .login-content.page-card {
	margin-left: var(--t36t-login-card-ml, auto);
	margin-right: var(--t36t-login-card-mr, auto);
}

/* GAP: login.bundle.scss:398 pins the card head to `align-items: flex-start` and
   `text-align: left`, so the logo could only sit left. It compiles to a bare
   .page-card-head (0,1,0) and that bundle loads later, so `body` is needed to
   take this to (0,1,1). */
body .page-card-head {
	align-items: var(--t36t-login-logo-align, flex-start);
	text-align: var(--t36t-login-logo-text-align, left);
}

/* GAP: the login button hardcodes `background: var(--gray-900); color: white` inside a
   4-class chain (login.bundle.scss). !important is the honest fix — reproducing
   `.for-login .page-card .page-card-actions .btn-login` across all six `.for-*`
   states would be six times the CSS for the same result. */
.btn-login,
.btn-ldap-login {
	background: var(--t36t-btn-primary-bg, var(--gray-900)) !important;
	color: var(--t36t-btn-primary-fg, #fff) !important;
}
.btn-login:hover,
.btn-login:focus,
.btn-login:active,
.btn-ldap-login:hover,
.btn-ldap-login:focus,
.btn-ldap-login:active {
	background: var(--t36t-btn-primary-hover-bg, var(--gray-800)) !important;
}

/* ==========================================================================
   10. Website (web scope)
   Web colours ride the same tokens. Only the pinned logo sizes are gaps.
   MAP §3
   ========================================================================== */

/* GAP: `.navbar-brand img { max-height: 22px }` (website/navbar.scss:86). */
.navbar-brand img {
	/* `height` (not just max-height) is what makes the setting scale a logo UP;
	   max-height alone only caps, so a 183px-tall image ignored a 200px setting.
	   The unset fallback stays `auto` + v16's 22px cap, so a stock theme is
	   unchanged. max-width releases v16's 150px cap only once a height is set. */
	height: var(--t36t-website-logo-height, auto);
	max-height: var(--t36t-website-logo-height, 22px);
	max-width: var(--t36t-website-logo-maxw, 150px);
	width: auto;
	object-fit: var(--t36t-logo-fit, contain);
	padding: var(--t36t-website-logo-padding, 0);
	box-sizing: content-box;
}
/* GAP: `.footer-logo { height: 1.5rem }` (website/footer.scss:9). */
.footer-logo {
	height: var(--t36t-footer-logo-height, 1.5rem);
	max-width: 100%;
	object-fit: var(--t36t-logo-fit, contain);
	padding: var(--t36t-footer-logo-padding, 0);
	box-sizing: content-box;
}

/* --- Website navbar -----------------------------------------------------
   Everything v16 uses here is a compiled Sass literal, not a token:
     .navbar-light { background: white; border-bottom: 1px solid #e2e2e2 }
     .navbar-light .navbar-nav .nav-link { color: #525252 }
   So an override is the only route, and each fallback repeats the literal it
   replaces. Specificity is set against those chains — (0,3,0) and (0,5,0). */
body .navbar.navbar-light {
	/* GAP: v16 pins no navbar height, so --t36t-webnav-height had nothing to drive
	   and Web Navbar Height was a dead control. It also means a tall Website Logo
	   Size overflowed the bar. max() does both jobs: honour an explicit height,
	   and never let the bar be shorter than the logo it has to hold. Mirrors the
	   desk .sidebar-header guard above. */
	min-height: max(
		var(--t36t-webnav-height, 0px),
		calc(var(--t36t-website-logo-height, 22px) + var(--t36t-website-logo-padding, 0px) * 2 + 16px)
	);
	background-color: var(--t36t-webnav-bg, white);
	/* veil first so it composites OVER the image */
	background-image: var(--t36t-webnav-veil, none), var(--t36t-webnav-fill, none);
	background-size: var(--t36t-webnav-bg-size, var(--t36t-chrome-bg-size, cover));
	background-position: var(--t36t-webnav-bg-position, center);
	background-repeat: var(--t36t-webnav-bg-repeat, var(--t36t-chrome-bg-repeat, no-repeat));
	background-size: var(--t36t-chrome-bg-size, cover);
	background-position: center;
	background-repeat: var(--t36t-chrome-bg-repeat, no-repeat);
	border-bottom-color: var(--t36t-webnav-border, #e2e2e2);
	box-shadow: var(--t36t-webnav-shadow, none);
}

body .navbar.navbar-light .navbar-nav .nav-link,
body .navbar.navbar-light .navbar-brand {
	color: var(--t36t-webnav-text, #525252);
}

body .navbar.navbar-light .navbar-nav .nav-item.active .nav-link,
body .navbar.navbar-light .navbar-nav .nav-link:hover,
body .navbar.navbar-light .navbar-nav .nav-link:focus {
	color: var(--t36t-webnav-active, var(--t36t-webnav-text, #525252));
}

/* The active item's underline is the one thing here that already rides a token. */
body .navbar.navbar-light .navbar-nav .nav-item.active {
	border-bottom-color: var(--t36t-webnav-active, var(--primary));
}

body .navbar .navbar-nav .nav-item {
	height: var(--t36t-webnav-height, var(--navbar-height));
}

body[data-t36t-webnav-sticky="on"] .navbar.navbar-light {
	position: sticky;
	top: 0;
	z-index: 1020;
}

/* ==========================================================================
   11. Layout
   Full width is `body.full-width` + localStorage (set by the loader).
   Content width is --page-max-width, rail width is --sidebar-width,
   density is the --padding-* / --margin-* scale. All tokens. NO RULES.
   MAP §2.4
   ========================================================================== */

/* ==========================================================================
   12. RTL
   Direction is set as dir="rtl" on <html> by the loader — the platform way.
   This is only a safety net for the attribute-driven path.
   MAP §2.5
   ========================================================================== */

[data-t36t-rtl="on"] {
	direction: rtl;
}

/* ==========================================================================
   13. Motion

   Gated entirely on body[data-t36t-anim]. Animation Level "None" emits no
   attribute value that matches, so zero rules apply and there is zero motion.

   The STYLE is a variable, not another set of gated rules: every animated
   element runs `var(--t36t-anim-name, t36t-rise)`, so six motion styles cost six
   @keyframes blocks rather than six copies of every rule.
   ========================================================================== */

@keyframes t36t-fade {
	from { opacity: 0; }
	to { opacity: 1; }
}

@keyframes t36t-rise {
	from { opacity: 0; transform: translateY(var(--t36t-anim-distance)); }
	to { opacity: 1; transform: none; }
}

@keyframes t36t-slide {
	from { opacity: 0; transform: translateX(calc(var(--t36t-anim-distance) * -2)); }
	to { opacity: 1; transform: none; }
}

@keyframes t36t-zoom {
	from { opacity: 0; transform: scale(1.03); }
	to { opacity: 1; transform: none; }
}

@keyframes t36t-pop {
	from { opacity: 0; transform: scale(0.97); }
	to { opacity: 1; transform: none; }
}

@keyframes t36t-blur {
	from { opacity: 0; filter: blur(6px); }
	to { opacity: 1; filter: blur(0); }
}

/* --- Subtle: page transitions only ------------------------------------- */
body[data-t36t-anim="subtle"] .page-content,
body[data-t36t-anim="standard"] .page-content,
body[data-t36t-anim="animated"] .page-content,
body[data-t36t-anim="subtle"] .modal-content,
body[data-t36t-anim="standard"] .modal-content,
body[data-t36t-anim="animated"] .modal-content {
	/* backwards, NOT both. `both` = backwards + forwards, and forwards-filling a
	   transform leaves the element creating a stacking context forever. .page-content
	   is an ancestor of the child-table row editor, so that traps .form-in-grid's
	   z-index:1021 and Frappe's freeze overlay (#freeze.grid-form, z-index 1020)
	   paints over the editor. backwards still applies the `from` state before the
	   animation runs — which is all that was ever needed to avoid a flash. */
	animation: var(--t36t-anim-name, t36t-rise) var(--t36t-anim-duration) var(--t36t-anim-ease) backwards;
}

/* --- Standard: content blocks join in ----------------------------------- */
body[data-t36t-anim="standard"] .widget,
body[data-t36t-anim="animated"] .widget,
body[data-t36t-anim="standard"] .list-row-container,
body[data-t36t-anim="animated"] .list-row-container {
	animation: var(--t36t-anim-name, t36t-rise) var(--t36t-anim-duration) var(--t36t-anim-ease) backwards;
}

/* Dropdowns keep their own quick pop: a menu that slides or blurs feels slow,
   however the rest of the page is set. */
body[data-t36t-anim="standard"] .dropdown-menu.show,
body[data-t36t-anim="animated"] .dropdown-menu.show {
	animation: t36t-pop var(--t36t-anim-fast) var(--t36t-anim-ease) backwards;
}

/* --- Animated: the rail gains movement ---------------------------------- */
body[data-t36t-anim="animated"] .body-sidebar .item-anchor {
	transition: color var(--t36t-anim-fast) var(--t36t-anim-ease),
		padding-left var(--t36t-anim-fast) var(--t36t-anim-ease);
}
body[data-t36t-anim="animated"] .body-sidebar .standard-sidebar-item:hover .item-anchor {
	padding-left: 3px;
}

/* --- Staggered reveal ---------------------------------------------------
   CSS cannot do arithmetic on an nth-child index, so the delays are written out.
   :is() collapses the two target selectors into one, which keeps this from
   costing more than the rest of the motion layer put together. Capped at seven:
   past that a stagger stops reading as deliberate and starts reading as a slow
   page. */
body[data-t36t-stagger="on"] :is(.list-row-container, .widget-group .widget):nth-child(1) { animation-delay: 0ms; }
body[data-t36t-stagger="on"] :is(.list-row-container, .widget-group .widget):nth-child(2) { animation-delay: 25ms; }
body[data-t36t-stagger="on"] :is(.list-row-container, .widget-group .widget):nth-child(3) { animation-delay: 50ms; }
body[data-t36t-stagger="on"] :is(.list-row-container, .widget-group .widget):nth-child(4) { animation-delay: 75ms; }
body[data-t36t-stagger="on"] :is(.list-row-container, .widget-group .widget):nth-child(5) { animation-delay: 100ms; }
body[data-t36t-stagger="on"] :is(.list-row-container, .widget-group .widget):nth-child(6) { animation-delay: 125ms; }
body[data-t36t-stagger="on"] :is(.list-row-container, .widget-group .widget):nth-child(7) { animation-delay: 150ms; }
body[data-t36t-stagger="on"] :is(.list-row-container, .widget-group .widget):nth-child(n + 8) { animation-delay: 150ms; }

/* --- Hover styles (independent of Animation Level) ----------------------- */
body[data-t36t-hover] .widget,
body[data-t36t-hover] .frappe-card {
	transition: transform var(--t36t-anim-fast) var(--t36t-anim-ease),
		box-shadow var(--t36t-anim-fast) var(--t36t-anim-ease),
		background-color var(--t36t-anim-fast) var(--t36t-anim-ease);
}

body[data-t36t-hover="lift"] .widget:hover,
body[data-t36t-hover="lift"] .frappe-card:hover {
	transform: translateY(-2px);
	box-shadow: var(--shadow-md);
}

body[data-t36t-hover="glow"] .widget:hover,
body[data-t36t-hover="glow"] .frappe-card:hover {
	box-shadow: 0 0 0 2px var(--t36t-accent, var(--primary));
}

body[data-t36t-hover="scale"] .widget:hover,
body[data-t36t-hover="scale"] .frappe-card:hover {
	transform: scale(1.02);
}

body[data-t36t-hover="highlight"] .widget:hover,
body[data-t36t-hover="highlight"] .frappe-card:hover {
	background-color: var(--highlight-color);
}

body[data-t36t-hover] .btn {
	transition: filter var(--t36t-anim-fast) var(--t36t-anim-ease);
}
body[data-t36t-hover] .btn:hover {
	filter: brightness(0.96);
}

/* ==========================================================================
   14. Reduced motion — LAST, wins over everything above
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	body[data-t36t-anim] *,
	body[data-t36t-anim] *::before,
	body[data-t36t-anim] *::after,
	body[data-t36t-hover] *,
	body[data-t36t-hover] *::before,
	body[data-t36t-hover] *::after {
		animation-duration: 1ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 1ms !important;
		scroll-behavior: auto !important;
	}
	body[data-t36t-hover] .widget:hover,
	body[data-t36t-hover] .frappe-card:hover {
		transform: none;
	}
	body[data-t36t-stagger="on"] * {
		animation-delay: 0ms !important;
	}
}
