jey_theme/jey_theme/public/css/shared.css

1142 lines
40 KiB
CSS

/* ==========================================================================
Jey Theme — Shared Styles (theme-independent)
========================================================================== */
/* Hide Organization desktop icon by default */
.desktop-icon[data-id="Organization"] {
display: none !important;
}
/* Fallback hide for blocked modules (main filtering happens in access_control.py).
Desktop Icon records use label "Quality" and "Subcontracting" — match those. */
.desktop-icon[data-id="Quality"],
.desktop-icon[data-id="Quality Management"],
.desktop-icon[data-id="Subcontracting"] {
display: none !important;
}
/* ==========================================================================
Toggle Switch Checkboxes
========================================================================== */
input[type="checkbox"] {
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
width: 28px !important;
min-width: 28px !important;
height: 16px !important;
background-color: var(--gray-400, #c0c6cc) !important;
background-image: none !important;
border-radius: 999px !important;
border: none !important;
position: relative !important;
cursor: pointer !important;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.12) !important;
transition: background-color 0.25s ease !important;
vertical-align: middle !important;
margin: 0 !important;
margin-right: 8px !important;
outline: none !important;
}
input[type="checkbox"]::after {
content: "" !important;
width: 12px !important;
height: 12px !important;
background-color: #fff !important;
border-radius: 50% !important;
position: absolute !important;
top: 2px !important;
left: 2px !important;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25) !important;
transition: transform 0.25s ease !important;
border: none !important;
}
input[type="checkbox"]:checked,
input[type="checkbox"].disabled-selected {
background-color: #2da44e !important;
background-image: none !important;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.08) !important;
}
input[type="checkbox"]:checked::after,
input[type="checkbox"].disabled-selected::after {
transform: translateX(12px) !important;
}
input[type="checkbox"]:focus {
outline: none !important;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.12), 0 0 0 2px rgba(45, 164, 78, 0.25) !important;
}
input[type="checkbox"]:disabled {
opacity: 0.5 !important;
cursor: not-allowed !important;
background-image: none !important;
}
input[type="checkbox"].disabled-selected {
opacity: 0.7 !important;
cursor: not-allowed !important;
}
/* ==========================================================================
Table Grid — restore Frappe default checkboxes
========================================================================== */
.row-check input[type="checkbox"],
.grid-row-check {
width: var(--checkbox-size, 14px) !important;
min-width: var(--checkbox-size, 14px) !important;
height: var(--checkbox-size, 14px) !important;
border-radius: 4px !important;
border: 1px solid var(--gray-500) !important;
background-color: transparent !important;
background-image: none !important;
box-shadow: none !important;
margin-right: 0 !important;
transition: none !important;
}
.row-check input[type="checkbox"]::after,
.grid-row-check::after {
content: "" !important;
width: auto !important;
height: auto !important;
background: none !important;
border-radius: 0 !important;
position: absolute !important;
top: auto !important;
left: auto !important;
box-shadow: none !important;
transition: none !important;
transform: none !important;
inset: -8px !important;
opacity: 0 !important;
}
.row-check input[type="checkbox"]:checked,
.grid-row-check:checked {
background-color: var(--primary) !important;
background-image: url("data:image/svg+xml, <svg viewBox='0 0 8 7' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M1 4.00001L2.66667 5.80001L7 1.20001' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/></svg>"), var(--checkbox-gradient) !important;
background-size: 57%, 100% !important;
background-repeat: no-repeat !important;
background-position: center !important;
border: none !important;
box-shadow: none !important;
}
.row-check input[type="checkbox"]:checked::after,
.grid-row-check:checked::after {
transform: none !important;
}
/* ==========================================================================
List View — restore toggle knob
========================================================================== */
input.list-row-checkbox::after,
input.list-header-checkbox::after {
inset: auto !important;
width: 12px !important;
height: 12px !important;
background-color: #fff !important;
border-radius: 50% !important;
position: absolute !important;
top: 2px !important;
left: 2px !important;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25) !important;
transition: transform 0.25s ease !important;
opacity: 1 !important;
}
input.list-row-checkbox:checked::after,
input.list-header-checkbox:checked::after {
transform: translateX(12px) !important;
}
.frappe-control[data-fieldtype="Check"] .checkbox label {
align-items: center !important;
}
.frappe-control[data-fieldtype="Check"] .checkbox .input-area {
padding-top: 0 !important;
display: flex !important;
align-items: center !important;
}
/* ==========================================================================
List View — swap filter and sort order
========================================================================== */
.filter-section {
display: flex !important;
}
.filter-section .sort-selector {
order: -1 !important;
margin-right: 8px !important;
}
/* ==========================================================================
Table Horizontal Scrollbar — thicker & easier to grab
========================================================================== */
/* Force scrollbar always visible & push it below table content */
.form-grid {
overflow-x: scroll !important;
padding-bottom: 12px !important;
scrollbar-width: auto !important;
}
.form-grid::-webkit-scrollbar {
height: 10px !important;
}
.form-grid::-webkit-scrollbar-track {
background: var(--gray-100, #f5f5f5) !important;
}
.form-grid::-webkit-scrollbar-thumb {
background: var(--gray-400, #c0c6cc) !important;
border-radius: 5px !important;
border: 2px solid var(--gray-100, #f5f5f5) !important;
}
.form-grid::-webkit-scrollbar-thumb:hover {
background: var(--gray-500, #98a2ab) !important;
}
/* ==========================================================================
Grid Column Resize Handles
========================================================================== */
.jey-col-resize {
position: absolute;
top: 0;
right: -2px;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 2;
background: transparent;
transition: background 0.15s;
}
.jey-col-resize:hover,
.jey-col-resize:active {
background: var(--primary, #2490ef);
opacity: 0.5;
}
/* Column drag & drop */
.grid-heading-row .grid-static-col[data-jey-draggable] {
cursor: grab;
}
.grid-heading-row .grid-static-col.jey-drop-target.jey-drop-left {
box-shadow: inset 3px 0 0 var(--primary, #2490ef);
background: rgba(36, 144, 239, 0.05);
}
.grid-heading-row .grid-static-col.jey-drop-target.jey-drop-right {
box-shadow: inset -3px 0 0 var(--primary, #2490ef);
background: rgba(36, 144, 239, 0.05);
}
/* ==========================================================================
List View Column Resize + Reorder
========================================================================== */
.list-row-head .list-row-col {
position: relative;
}
/* Column dividers — hint that headers can be resized / reordered. */
.list-row-head .list-row-col:not(:last-child) {
border-right: 1px solid var(--border-color, #e2e6e9);
}
.list-row-head .jey-col-resize {
position: absolute;
top: 0;
right: -2px;
width: 5px;
height: 100%;
cursor: col-resize;
z-index: 3;
background: transparent;
transition: background 0.15s;
}
.list-row-head .jey-col-resize:hover,
.list-row-head .jey-col-resize:active {
background: var(--primary, #2490ef);
opacity: 0.5;
}
.list-row-head .list-row-col[data-jey-draggable]:not(.list-subject) {
cursor: grab;
}
.list-row-head .list-row-col.jey-drop-target.jey-drop-left {
box-shadow: inset 3px 0 0 var(--primary, #2490ef);
background: rgba(36, 144, 239, 0.05);
}
.list-row-head .list-row-col.jey-drop-target.jey-drop-right {
box-shadow: inset -3px 0 0 var(--primary, #2490ef);
background: rgba(36, 144, 239, 0.05);
}
/* ==========================================================================
Sidebar — Full Width button
========================================================================== */
.jey-fullwidth-link,
.jey-theme-switch-link,
.jey-collapse-link {
text-decoration: none;
font-size: var(--text-sm);
display: flex;
align-items: center;
cursor: pointer;
margin-bottom: 8px;
}
.jey-fullwidth-link svg,
.jey-theme-switch-link svg,
.jey-collapse-link svg {
margin: 0;
flex: 0 0 auto;
}
.jey-fullwidth-link span,
.jey-theme-switch-link span,
.jey-collapse-link span {
margin-left: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ==========================================================================
Modal / Dialog — Smooth Entrance
========================================================================== */
.modal.fade .modal-dialog {
transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
opacity 0.5s cubic-bezier(0.22, 1, 0.36, 1) !important;
transform: translateY(-24px) scale(0.97);
opacity: 0;
}
.modal.show .modal-dialog {
transform: none;
opacity: 1;
}
/* ==========================================================================
Form Sections — Cascade Entrance
========================================================================== */
@keyframes jey-section-in {
from {
opacity: 0;
transform: translateY(-12px);
}
to {
opacity: 1;
}
}
/* Note: `to` intentionally omits `transform` — leaving `translateY(0)` in the
final frame would keep a stacking context on every section after animation
completes, clipping Link-field autocomplete dropdowns behind later sections. */
.form-layout .form-section .section-head,
.form-layout .form-section .section-body {
animation: jey-section-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.form-layout .form-section:nth-child(1) .section-head,
.form-layout .form-section:nth-child(1) .section-body { animation-delay: 0.04s; }
.form-layout .form-section:nth-child(2) .section-head,
.form-layout .form-section:nth-child(2) .section-body { animation-delay: 0.10s; }
.form-layout .form-section:nth-child(3) .section-head,
.form-layout .form-section:nth-child(3) .section-body { animation-delay: 0.16s; }
.form-layout .form-section:nth-child(4) .section-head,
.form-layout .form-section:nth-child(4) .section-body { animation-delay: 0.22s; }
.form-layout .form-section:nth-child(5) .section-head,
.form-layout .form-section:nth-child(5) .section-body { animation-delay: 0.28s; }
.form-layout .form-section:nth-child(6) .section-head,
.form-layout .form-section:nth-child(6) .section-body { animation-delay: 0.34s; }
.form-layout .form-section:nth-child(7) .section-head,
.form-layout .form-section:nth-child(7) .section-body { animation-delay: 0.40s; }
.form-layout .form-section:nth-child(8) .section-head,
.form-layout .form-section:nth-child(8) .section-body { animation-delay: 0.46s; }
.form-layout .form-section:nth-child(9) .section-head,
.form-layout .form-section:nth-child(9) .section-body { animation-delay: 0.52s; }
.form-layout .form-section:nth-child(10) .section-head,
.form-layout .form-section:nth-child(10) .section-body { animation-delay: 0.58s; }
.form-layout .form-section:nth-child(11) .section-head,
.form-layout .form-section:nth-child(11) .section-body { animation-delay: 0.64s; }
.form-layout .form-section:nth-child(12) .section-head,
.form-layout .form-section:nth-child(12) .section-body { animation-delay: 0.70s; }
/* ==========================================================================
Form Tabs — Sliding Active Indicator
========================================================================== */
.form-tabs .nav-link {
position: relative;
border-bottom: none !important;
transition: color 0.3s ease;
}
.form-tabs .nav-link::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: var(--text-color);
transform: scaleX(0);
transform-origin: center;
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.form-tabs .nav-link.active::after {
transform: scaleX(1);
}
/* ==========================================================================
Dropdown Animations
========================================================================== */
.dropdown-menu {
display: block !important;
visibility: hidden;
opacity: 0;
pointer-events: none;
}
.dropdown-menu.show {
visibility: visible;
opacity: 1;
pointer-events: auto;
animation: jey-dropdown-in 0.2s ease;
}
/* Applied by JS during Bootstrap hide so Popper's transform reset
doesn't move the menu mid-fade. See "Dropdown fade-out" in jey_theme.js. */
.dropdown-menu.jey-dropdown-hiding {
visibility: visible !important;
pointer-events: none;
animation: jey-dropdown-out 0.2s ease forwards;
}
@keyframes jey-dropdown-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes jey-dropdown-out {
from { opacity: 1; }
to { opacity: 0; }
}
/* ==========================================================================
Sidebar — Smooth Collapse / Expand
Frappe toggles .body-sidebar-placeholder between display:none and display:flex,
so the container's fit-content width snaps on expand (the inner sidebar goes
position:absolute, placeholder appears at 220px instantly). The collapse path
keeps the sidebar in flow, so its width transition is already smooth.
Override the container with an explicit width transition to make expand
symmetric with collapse.
========================================================================== */
@media (min-width: 576px) {
.body-sidebar-container {
width: 50px;
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.body-sidebar-container.expanded {
width: var(--sidebar-width);
}
}
/* ==========================================================================
Sidebar — No Alignment Shift Between States
Regular items land at 15px from the sidebar's left edge (sidebar padding 8
+ icon padding 7). Two state-dependent paddings break that alignment in
expanded mode:
1. Frappe's sidebar_header.js sets inline padding-left/right: 8px on
.sidebar-header when expanded, 0 when collapsed — pushes the logo to
23px in expanded. Override inline style with !important.
2. SCSS mixin body-sidebar-expanded gives .sidebar-user-button
padding: var(--padding-sm) only when .expanded — pushes the avatar
to 23px and makes the button taller, which shifts the Jey buttons
above it vertically. Zero out the padding.
Zeroing both keeps every icon/avatar at 15px and button heights constant
across expand/collapse, so nothing jumps on toggle.
========================================================================== */
.body-sidebar .sidebar-header {
padding-left: 0 !important;
padding-right: 0 !important;
}
.body-sidebar-container.expanded .sidebar-user-button {
padding: 0 !important;
}
/* ==========================================================================
Sidebar — Hover Effects
========================================================================== */
.body-sidebar .standard-sidebar-item .item-anchor {
transition: padding-left 0.4s cubic-bezier(0.22, 1, 0.36, 1),
background 0.4s ease;
}
.body-sidebar .standard-sidebar-item .item-anchor:hover {
padding-left: calc(var(--padding-xs, 8px) + 4px);
}
.body-sidebar .standard-sidebar-item .sidebar-item-icon {
transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
.body-sidebar .standard-sidebar-item .item-anchor:hover .sidebar-item-icon {
transform: scale(1.15);
}
/* Active item — subtle left accent bar */
.body-sidebar .standard-sidebar-item.active-sidebar .item-anchor {
position: relative;
}
.body-sidebar .standard-sidebar-item.active-sidebar .item-anchor::before {
content: "";
position: absolute;
left: 0;
top: 20%;
bottom: 20%;
width: 3px;
border-radius: 0 3px 3px 0;
background: var(--primary, #2490ef);
opacity: 0.8;
}
/* ==========================================================================
List View — Row Entrance Animation
========================================================================== */
@keyframes jey-list-row-in {
from {
opacity: 0;
transform: translateX(-16px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.frappe-list .result .list-row-container:not(:first-child) {
animation: jey-list-row-in 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.frappe-list .result .list-row-container:nth-child(2) { animation-delay: 0.03s; }
.frappe-list .result .list-row-container:nth-child(3) { animation-delay: 0.05s; }
.frappe-list .result .list-row-container:nth-child(4) { animation-delay: 0.07s; }
.frappe-list .result .list-row-container:nth-child(5) { animation-delay: 0.09s; }
.frappe-list .result .list-row-container:nth-child(6) { animation-delay: 0.11s; }
.frappe-list .result .list-row-container:nth-child(7) { animation-delay: 0.13s; }
.frappe-list .result .list-row-container:nth-child(8) { animation-delay: 0.15s; }
.frappe-list .result .list-row-container:nth-child(9) { animation-delay: 0.17s; }
.frappe-list .result .list-row-container:nth-child(10) { animation-delay: 0.19s; }
.frappe-list .result .list-row-container:nth-child(11) { animation-delay: 0.21s; }
.frappe-list .result .list-row-container:nth-child(12) { animation-delay: 0.23s; }
.frappe-list .result .list-row-container:nth-child(13) { animation-delay: 0.25s; }
.frappe-list .result .list-row-container:nth-child(14) { animation-delay: 0.27s; }
.frappe-list .result .list-row-container:nth-child(15) { animation-delay: 0.29s; }
.frappe-list .result .list-row-container:nth-child(16) { animation-delay: 0.31s; }
.frappe-list .result .list-row-container:nth-child(17) { animation-delay: 0.33s; }
.frappe-list .result .list-row-container:nth-child(18) { animation-delay: 0.35s; }
.frappe-list .result .list-row-container:nth-child(19) { animation-delay: 0.37s; }
.frappe-list .result .list-row-container:nth-child(20) { animation-delay: 0.39s; }
.frappe-list .result .list-row-container:nth-child(21) { animation-delay: 0.41s; }
/* ==========================================================================
Search Dialog — Fade In / Out
========================================================================== */
.modal:has(.cool-awesomebar-modal-footer) {
display: block !important;
visibility: hidden;
opacity: 0;
pointer-events: none;
transition: opacity 0.5s cubic-bezier(0.22, 1, 0.36, 1),
visibility 0.5s;
}
.modal:has(.cool-awesomebar-modal-footer) .modal-dialog {
transform: translateY(-30px);
transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}
.modal.show:has(.cool-awesomebar-modal-footer) {
visibility: visible;
opacity: 1;
pointer-events: auto;
}
.modal.show:has(.cool-awesomebar-modal-footer) .modal-dialog {
transform: none;
}
/* ==========================================================================
Report Summary — Card Cascade
========================================================================== */
@keyframes jey-summary-in {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.report-summary .summary-item {
animation: jey-summary-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.report-summary .summary-item:nth-child(1) { animation-delay: 0.05s; }
.report-summary .summary-item:nth-child(2) { animation-delay: 0.10s; }
.report-summary .summary-item:nth-child(3) { animation-delay: 0.15s; }
.report-summary .summary-item:nth-child(4) { animation-delay: 0.20s; }
.report-summary .summary-item:nth-child(5) { animation-delay: 0.25s; }
.report-summary .summary-item:nth-child(6) { animation-delay: 0.30s; }
/* ==========================================================================
Form Section Collapse / Expand — Smooth
========================================================================== */
.form-section .collapse-indicator {
transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
display: inline-block;
}
.form-section .section-head.collapsed .collapse-indicator {
transform: rotate(-90deg);
}
/* Expanded section-body has no `overflow: hidden` — so Link-field autocomplete
dropdowns (and any other overflow) are not clipped by the section boundary.
`overflow: hidden` is applied via JS only during the collapse/expand
transition (see jey_theme.js), then removed once the animation finishes. */
.form-section .section-head.collapsible + .section-body {
display: flex !important;
flex-wrap: wrap;
transition: max-height 0.5s ease-in-out,
padding 0.5s ease-in-out;
}
.form-section .section-head.collapsed + .section-body {
max-height: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
overflow: hidden;
transition: max-height 0.5s ease-in-out,
padding 0.5s ease-in-out;
}
/* ==========================================================================
Link Field — Awesomplete Dropdown Fade
========================================================================== */
.awesomplete > [role="listbox"] {
animation: jey-awesomplete-in 0.4s cubic-bezier(0.22, 1, 0.36, 1) both;
}
/* Applied by JS on awesomplete-close so the listbox fades instead of
vanishing when [hidden] is set. See "Awesomplete fade-out" in jey_theme.js. */
.awesomplete > [role="listbox"].jey-awesomplete-hiding {
display: block !important;
pointer-events: none;
animation: jey-awesomplete-out 0.2s ease forwards;
}
@keyframes jey-awesomplete-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes jey-awesomplete-out {
from { opacity: 1; }
to { opacity: 0; }
}
/* Lift the section containing an open autocomplete above its siblings so the
dropdown can spill past the section boundary without being painted over by
later sections (which naturally paint above earlier ones in DOM order). */
.form-layout .form-section:focus-within {
position: relative;
z-index: 10;
}
/* ==========================================================================
Input Focus — Inset Press
========================================================================== */
input.form-control:not([type="checkbox"]):not([type="radio"]),
textarea.form-control,
.frappe-control input:not([type="checkbox"]):not([type="radio"]),
.frappe-control textarea {
transition:
background-color 0.45s cubic-bezier(0.22, 1, 0.36, 1),
border-color 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}
input.form-control:not([type="checkbox"]):not([type="radio"]):focus,
textarea.form-control:focus,
.frappe-control input:not([type="checkbox"]):not([type="radio"]):focus,
.frappe-control textarea:focus {
border-color: var(--gray-500, #a8b0b8) !important;
outline: none;
}
/* ==========================================================================
Buttons — Shine on Hover, Press on Click
========================================================================== */
.btn {
position: relative;
overflow: hidden;
transition: box-shadow 0.4s ease, background-color 0.3s ease, color 0.3s ease;
}
.btn::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(105deg,
transparent 35%,
rgba(255, 255, 255, 0.25) 45%,
rgba(255, 255, 255, 0.25) 55%,
transparent 65%);
transform: translateX(-110%);
transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
pointer-events: none;
}
.btn:hover::after {
transform: translateX(110%);
}
.btn:active {
box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.2) !important;
}
/* Hide dropdown items now surfaced as sidebar buttons */
.dropdown-menu-item[onclick*="toggle_full_width"],
.dropdown-menu-item[onclick*="clear_cache"],
.dropdown-menu-item[onclick*="ThemeSwitcher"],
.dropdown-menu-item[data-name="website"],
.dropdown-menu-item.jey-hidden-item {
display: none !important;
}
/* ==========================================================================
Desktop Icons — Shared base (all themes)
========================================================================== */
.desktop-icon:hover {
transform: translateY(-2px) !important;
}
.desktop-icon:active {
transform: scale(0.97) !important;
}
.icon-container.jey-chrome-done {
width: 72px !important;
height: 72px !important;
padding: 0 !important;
border-radius: 50% !important;
background: none !important;
overflow: visible !important;
position: relative !important;
}
.icon-container.jey-chrome-done:hover {
transform: none !important;
}
.icon-container.folder-icon.jey-chrome-done {
box-shadow: none !important;
}
/* ==========================================================================
Icon Animations — Shared (all themes)
========================================================================== */
/* Framework — gentle float */
.ico-framework { animation: ico-float 5s ease-in-out infinite; transform-origin: center; }
@keyframes ico-float {
0%,100% { transform: translateY(0) rotate(0deg); opacity: 0.85; }
30% { transform: translateY(-2px) rotate(0.8deg); opacity: 1; }
70% { transform: translateY(0.5px) rotate(-0.4deg); opacity: 0.9; }
}
.ico-org { animation: ico-org 6s ease-in-out infinite; transform-origin: center bottom; }
@keyframes ico-org {
0%,100% { transform: scaleY(1) scaleX(1); }
25% { transform: scaleY(1.03) scaleX(0.99); }
50% { transform: scaleY(1.05) scaleX(0.98); }
75% { transform: scaleY(1.02) scaleX(1); }
}
.ico-acc .acc-btn { animation: ico-press 3s ease-in-out infinite; }
@keyframes ico-press { 0%,25%,100% { opacity: 0.5; stroke-width: 2.5; } 12% { opacity: 1; stroke-width: 3.2; } }
.ico-assets { animation: ico-safe 6s ease-in-out infinite; transform-origin: center; }
.ico-assets .lock-dot { animation: ico-blink 5s ease-in-out infinite; }
@keyframes ico-safe { 0%,100% { transform: translateY(0); } 40% { transform: translateY(-0.6px); } 60% { transform: translateY(0.3px); } }
@keyframes ico-blink { 0%,35%,65%,100% { opacity: 1; } 50% { opacity: 0.15; } }
.ico-buying { animation: ico-cart 6s ease-in-out infinite; transform-origin: 12px 21px; }
@keyframes ico-cart { 0%,100% { transform: translateX(0) rotate(0deg); } 20% { transform: translateX(1.5px) rotate(0.8deg); } 50% { transform: translateX(-0.5px) rotate(-0.3deg); } 80% { transform: translateX(0.8px) rotate(0.4deg); } }
.ico-mfg .gear1 { animation: ico-spin 9s linear infinite; transform-origin: 9px 12px; }
.ico-mfg .gear2 { animation: ico-spin-r 7s linear infinite; transform-origin: 17px 8px; }
@keyframes ico-spin { to { transform: rotate(360deg); } }
@keyframes ico-spin-r { to { transform: rotate(-360deg); } }
.ico-proj .col1 { animation: ico-g1 5s ease-in-out infinite; transform-origin: 5.5px 21px; }
.ico-proj .col2 { animation: ico-g2 5s ease-in-out infinite 0.5s; transform-origin: 12.5px 15px; }
.ico-proj .col3 { animation: ico-g3 5s ease-in-out infinite 1s; transform-origin: 19.5px 18px; }
@keyframes ico-g1 { 0%,100% { transform: scaleY(1); opacity: 0.75; } 50% { transform: scaleY(0.85); opacity: 1; } }
@keyframes ico-g2 { 0%,100% { transform: scaleY(1); opacity: 0.75; } 50% { transform: scaleY(1.15); opacity: 1; } }
@keyframes ico-g3 { 0%,100% { transform: scaleY(1); opacity: 0.75; } 50% { transform: scaleY(0.88); opacity: 1; } }
.ico-quality { animation: ico-shield 6s ease-in-out infinite; transform-origin: 12px 14px; }
@keyframes ico-shield { 0%,100% { transform: rotate(0deg) scale(1); } 15% { transform: rotate(1.5deg) scale(1.03); } 35% { transform: rotate(-0.5deg) scale(1.01); } 55% { transform: rotate(-1.5deg) scale(1.03); } 75% { transform: rotate(0.5deg) scale(1.01); } }
.ico-selling { animation: ico-sell 4s ease-in-out infinite; transform-origin: center; }
.ico-selling .arrow-head { animation: ico-up 4s ease-in-out infinite; }
@keyframes ico-sell { 0%,100% { opacity: 0.8; } 50% { opacity: 1; } }
@keyframes ico-up { 0%,100% { transform: translate(0,0); } 30% { transform: translate(1px, -1.5px); } 60% { transform: translate(2px, -2.5px); } }
.ico-stock { animation: ico-bob 5s ease-in-out infinite; transform-origin: center; }
@keyframes ico-bob { 0%,100% { transform: rotate(0deg) translateY(0); } 20% { transform: rotate(2deg) translateY(-0.7px); } 50% { transform: rotate(-1deg) translateY(0.3px); } 80% { transform: rotate(1.5deg) translateY(-0.4px); } }
.ico-sub .person-l { animation: ico-rl 6s ease-in-out infinite; transform-origin: 8px 12px; }
.ico-sub .person-r { animation: ico-rr 6s ease-in-out infinite; transform-origin: 16px 12px; }
@keyframes ico-rl { 0%,100% { transform: translateX(0) rotate(0deg); } 30% { transform: translateX(1px) rotate(1deg); } 60% { transform: translateX(1.5px) rotate(0.5deg); } }
@keyframes ico-rr { 0%,100% { transform: translateX(0) rotate(0deg); } 30% { transform: translateX(-1px) rotate(-1deg); } 60% { transform: translateX(-1.5px) rotate(-0.5deg); } }
.ico-settings { animation: ico-gear-main 12s linear infinite; transform-origin: center; }
@keyframes ico-gear-main { to { transform: rotate(360deg); } }
/* HR — plus sign pulsing */
.ico-hr .plus-v { animation: ico-pv 4s ease-in-out infinite; transform-origin: center; }
.ico-hr .plus-h { animation: ico-ph 4s ease-in-out infinite 0.3s; transform-origin: center; }
@keyframes ico-pv { 0%,100% { opacity: 0.4; stroke-width: 1.8; } 50% { opacity: 1; stroke-width: 2.4; } }
@keyframes ico-ph { 0%,100% { opacity: 0.4; stroke-width: 1.8; } 50% { opacity: 1; stroke-width: 2.4; } }
/* ==========================================================================
Theme Switcher Dialog
========================================================================== */
.jey-theme-grid {
display: flex;
gap: 20px;
padding: 20px;
justify-content: center;
}
.jey-theme-card {
cursor: pointer;
text-align: center;
padding: 16px;
border-radius: 12px;
border: 2px solid transparent;
transition: border-color 0.2s, background 0.2s;
min-width: 140px;
}
.jey-theme-card:hover {
background: var(--subtle-fg, #f5f5f5);
}
.jey-theme-card.selected {
border-color: var(--primary, #2490ef);
background: var(--subtle-fg, #f5f5f5);
}
.jey-theme-preview {
width: 72px;
height: 72px;
border-radius: 50%;
margin: 0 auto 12px;
position: relative;
overflow: hidden;
}
.jey-theme-preview--chrome {
background: conic-gradient(from 140deg, #a0a0a0 0deg, #f2f2f2 40deg, #777 80deg, #e5e5e5 120deg, #959595 170deg, #ededed 210deg, #808080 260deg, #d8d8d8 310deg, #a0a0a0 360deg);
box-shadow: 0 3px 8px rgba(0,0,0,0.15), inset 0 1px 1px rgba(255,255,255,0.5);
}
.jey-theme-preview--chrome .jey-theme-preview-icon {
position: absolute;
inset: 9px;
border-radius: 50%;
background: linear-gradient(168deg, #f4f4f4 0%, #e0e0e0 45%, #d4d4d4 100%);
}
.jey-theme-preview--modern {
border-radius: 16px;
background: rgba(255, 255, 255, 0.4);
backdrop-filter: blur(6px);
border: 1px solid rgba(255, 255, 255, 0.5);
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
}
.jey-theme-preview--modern .jey-theme-preview-icon {
position: absolute;
inset: 22px;
border-radius: 6px;
background: #4f46e5;
opacity: 0.25;
}
.jey-theme-label {
font-weight: 600;
font-size: var(--text-base);
margin-bottom: 2px;
}
.jey-theme-info {
font-size: var(--text-sm);
color: var(--text-muted);
}
/* ==========================================================================
Module Widget Cards — Entrance & Hover Animations
========================================================================== */
/* --- Animation variant #2 (final): Slide-up entrance + Accent Bar hover --- */
/* Entrance: fade-in slide-up (from variant #1) */
.ce-block .links-widget-box,
.ce-block .number-widget-box {
opacity: 0;
transform: translateY(24px) scale(0.97);
animation: jey-card-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
border-radius: var(--border-radius-lg, 12px);
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),
box-shadow 0.35s ease;
position: relative;
}
/* Number card value — delayed fade-in after card entrance */
@keyframes jey-number-in {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
.number-widget-box .widget-content .number {
animation: jey-number-in 0.5s cubic-bezier(0.22, 1, 0.36, 1) 0.3s both;
}
/* Allow dropdown menus (card actions) to escape the widget box.
Without this, .widget-control .dropdown-menu is clipped/hidden behind
the widget because Popper renders it as a sibling inside the box. */
.ce-block .links-widget-box .widget-control .dropdown-menu,
.ce-block .number-widget-box .widget-control .dropdown-menu {
z-index: 1050;
}
/* Stagger delays */
.ce-block:nth-child(1) .links-widget-box,
.ce-block:nth-child(1) .number-widget-box { animation-delay: 0.00s; }
.ce-block:nth-child(2) .links-widget-box,
.ce-block:nth-child(2) .number-widget-box { animation-delay: 0.06s; }
.ce-block:nth-child(3) .links-widget-box,
.ce-block:nth-child(3) .number-widget-box { animation-delay: 0.12s; }
.ce-block:nth-child(4) .links-widget-box,
.ce-block:nth-child(4) .number-widget-box { animation-delay: 0.18s; }
.ce-block:nth-child(5) .links-widget-box,
.ce-block:nth-child(5) .number-widget-box { animation-delay: 0.24s; }
.ce-block:nth-child(6) .links-widget-box,
.ce-block:nth-child(6) .number-widget-box { animation-delay: 0.30s; }
.ce-block:nth-child(7) .links-widget-box,
.ce-block:nth-child(7) .number-widget-box { animation-delay: 0.36s; }
.ce-block:nth-child(8) .links-widget-box,
.ce-block:nth-child(8) .number-widget-box { animation-delay: 0.42s; }
.ce-block:nth-child(9) .links-widget-box,
.ce-block:nth-child(9) .number-widget-box { animation-delay: 0.48s; }
.ce-block:nth-child(10) .links-widget-box,
.ce-block:nth-child(10) .number-widget-box { animation-delay: 0.54s; }
.ce-block:nth-child(11) .links-widget-box,
.ce-block:nth-child(11) .number-widget-box { animation-delay: 0.60s; }
.ce-block:nth-child(12) .links-widget-box,
.ce-block:nth-child(12) .number-widget-box { animation-delay: 0.66s; }
@keyframes jey-card-in {
from {
opacity: 0;
transform: translateY(24px) scale(0.97);
}
to {
opacity: 1;
transform: none;
}
}
/* Hover: soft bottom accent line + lift */
.ce-block .links-widget-box::after,
.ce-block .number-widget-box::after {
content: "";
position: absolute;
bottom: 0;
left: 10%;
right: 10%;
height: 2.5px;
border-radius: 2px 2px 0 0;
background: var(--primary, #2490ef);
transform: scaleX(0);
transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
transform-origin: center;
}
.ce-block .links-widget-box:hover::after,
.ce-block .number-widget-box:hover::after {
transform: scaleX(1);
}
.ce-block .links-widget-box:hover,
.ce-block .number-widget-box:hover {
transform: translateY(-3px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08),
0 2px 6px rgba(0, 0, 0, 0.04);
}
/* Lift widget above its siblings while hovered or while its card-actions
dropdown is open, so the dropdown menu (which lives inside the widget's
own stacking context due to transform) is not covered by neighbours. */
.ce-block .links-widget-box:hover,
.ce-block .number-widget-box:hover,
.ce-block .links-widget-box:focus-within,
.ce-block .number-widget-box:focus-within {
z-index: 100;
}
.ce-block .links-widget-box:has(.dropdown-menu.show),
.ce-block .number-widget-box:has(.dropdown-menu.show) {
z-index: 200;
}
/* Link items: colored left accent bar slides in */
.links-widget-box .link-item {
position: relative;
transition: background 0.25s ease, transform 0.25s ease;
border-radius: var(--border-radius, 8px);
overflow: hidden;
}
.links-widget-box .link-item:hover {
transform: translateX(6px);
background: var(--subtle-fg, rgba(0, 0, 0, 0.02));
}
.links-widget-box .link-item .es-icon {
display: none !important;
}
/* ==========================================================================
Select Field — Custom chevron icon
========================================================================== */
.select-icon svg {
display: none !important;
}
.select-icon {
top: 0 !important;
bottom: 0 !important;
display: flex !important;
align-items: center !important;
}
.select-icon::after {
content: "" !important;
display: block;
width: 12px;
height: 12px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23636e72' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
/* ==========================================================================
Empty State — Custom folder icon
========================================================================== */
.msg-box .icon-xl {
display: none !important;
}
.msg-box .mb-4 {
display: flex;
justify-content: center;
}
.msg-box .mb-4::after {
content: "" !important;
display: block;
width: 48px;
height: 48px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23b0b8c0' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='22 12 16 12 14 15 10 15 8 12 2 12'/%3E%3Cpath d='M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
/* ==========================================================================
Hide "Getting Started" onboarding panels
========================================================================== */
.user-onboarding {
display: none !important;
}