Files
Odoo-Modules/fusion_clock/static/src/css/portal_clock.css
gsinghpal 719853c251 fix(fusion_clock): leave From/To use CSS grid so they don't stack on iOS
iOS Safari date inputs have a large intrinsic min-width that can break a flex
row; switch .fclk-leave-daterange to grid 1fr 1fr + min-width:0 on the inputs
so the two fields always share the row and shrink. Also changes the bundle hash
to force iOS to drop the cached CSS. Live on entech 19.0.3.13.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 23:07:32 -04:00

1902 lines
42 KiB
CSS

/* ============================================================
Fusion Clock - Portal Dark Theme
A modern, mobile-first attendance interface
============================================================ */
/* ---- Light theme (default) ---- */
.fclk-app {
--fclk-bg: #f3f4f6;
--fclk-card: #ffffff;
--fclk-card-border: #e5e7eb;
--fclk-text: #1f2937;
--fclk-text-muted: #6b7280;
--fclk-text-dim: #9ca3af;
--fclk-green: #10B981;
--fclk-green-glow: rgba(16, 185, 129, 0.25);
--fclk-red: #ef4444;
--fclk-red-glow: rgba(239, 68, 68, 0.25);
--fclk-blue: #3b82f6;
--fclk-hover-bg: #f9fafb;
--fclk-overlay-bg: rgba(243, 244, 246, 0.85);
--fclk-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
--fclk-toast-bg: #ffffff;
margin: 0;
padding: 0;
min-height: 100vh;
background: var(--fclk-bg);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
/* ---- Dark theme: OS preference OR Odoo dark mode class ---- */
@media (prefers-color-scheme: dark) {
.fclk-app {
--fclk-bg: #0f1117;
--fclk-card: #1a1d23;
--fclk-card-border: #2a2d35;
--fclk-text: #ffffff;
--fclk-text-muted: #9ca3af;
--fclk-text-dim: #6b7280;
--fclk-green-glow: rgba(16, 185, 129, 0.3);
--fclk-red-glow: rgba(239, 68, 68, 0.3);
--fclk-hover-bg: #1e2128;
--fclk-overlay-bg: rgba(15, 17, 23, 0.85);
--fclk-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
--fclk-toast-bg: #1a1d23;
}
}
html.o_dark .fclk-app,
.fclk-app.fclk-dark {
--fclk-bg: #0f1117;
--fclk-card: #1a1d23;
--fclk-card-border: #2a2d35;
--fclk-text: #ffffff;
--fclk-text-muted: #9ca3af;
--fclk-text-dim: #6b7280;
--fclk-green-glow: rgba(16, 185, 129, 0.3);
--fclk-red-glow: rgba(239, 68, 68, 0.3);
--fclk-hover-bg: #1e2128;
--fclk-overlay-bg: rgba(15, 17, 23, 0.85);
--fclk-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
--fclk-toast-bg: #1a1d23;
}
.fclk-app * {
box-sizing: border-box;
}
/* Hide portal navigation and footer */
.fclk-app ~ .o_portal_navbar,
.fclk-app .o_portal_navbar {
display: none !important;
}
.fclk-app ~ footer,
.fclk-app ~ .o_footer,
.fclk-app ~ .o_footer_copyright {
display: none !important;
}
body:has(.fclk-app) footer,
body:has(.fclk-app) .o_footer {
display: none !important;
}
/* Full-bleed: never let the portal layout's white chrome show as a border
around the dark app. Match the PAGE background to the app's background in
both themes (so the wrapper's container padding reads as an invisible
gutter) and clip horizontal overflow from the .fclk-app full-bleed margins. */
html:has(.fclk-app),
body:has(.fclk-app) {
background: #f3f4f6;
overflow-x: hidden;
}
@media (prefers-color-scheme: dark) {
html:has(.fclk-app),
body:has(.fclk-app) {
background: #0f1117;
}
}
html.o_dark:has(.fclk-app),
html.o_dark body:has(.fclk-app),
body:has(.fclk-app.fclk-dark) {
background: #0f1117;
}
/* Neutralise EVERY portal layout wrapper so the dark app fills edge-to-edge.
Confirmed from the live DOM that the chain is:
#wrapwrap > main > .o_fp_portal_shell > .o_fp_portal_main >
#wrap.o_portal_wrap > .container > .fclk-app
The white frame was .o_fp_portal_shell (the fusion_plating_portal "shell")
plus the Bootstrap .container max-width + "pt-3 pb-5" padding. Make them all
transparent, full-width, no padding/margin/border. */
body:has(.fclk-app) #wrapwrap,
body:has(.fclk-app) main,
body:has(.fclk-app) .o_fp_portal_shell,
body:has(.fclk-app) .o_fp_portal_main,
body:has(.fclk-app) #wrap.o_portal_wrap,
body:has(.fclk-app) #wrap.o_portal_wrap > .container {
background: transparent !important;
padding: 0 !important;
margin: 0 !important;
max-width: 100% !important;
border: 0 !important;
}
.fclk-container {
max-width: 480px;
margin: 0 auto;
padding: 24px 20px 100px;
position: relative;
}
/* ---- Header ---- */
.fclk-header {
margin-bottom: 20px;
}
.fclk-date {
color: var(--fclk-text-muted);
font-size: 14px;
margin-bottom: 4px;
}
.fclk-greeting {
color: var(--fclk-text);
font-size: 28px;
font-weight: 700;
margin: 0;
letter-spacing: -0.5px;
}
/* ---- Status Card ---- */
.fclk-status-card {
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
border-radius: 16px;
padding: 16px 20px;
margin-bottom: 32px;
box-shadow: var(--fclk-shadow);
}
.fclk-status-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.fclk-status-indicator {
display: flex;
align-items: center;
gap: 8px;
}
.fclk-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--fclk-text-dim);
display: inline-block;
transition: all 0.3s ease;
}
.fclk-dot-active {
background: var(--fclk-green);
box-shadow: 0 0 8px var(--fclk-green-glow);
animation: fclk-pulse 2s ease-in-out infinite;
}
@keyframes fclk-pulse {
0%, 100% { box-shadow: 0 0 4px var(--fclk-green-glow); }
50% { box-shadow: 0 0 16px var(--fclk-green-glow); }
}
.fclk-status-text {
color: var(--fclk-text);
font-size: 14px;
font-weight: 500;
}
.fclk-current-time {
color: var(--fclk-text-muted);
font-size: 13px;
}
/* ---- Location Card ---- */
.fclk-location-card {
display: flex;
align-items: center;
gap: 12px;
background: rgba(16, 185, 129, 0.08);
border: 1px solid rgba(16, 185, 129, 0.15);
border-radius: 12px;
padding: 12px 16px;
cursor: pointer;
transition: all 0.2s ease;
}
.fclk-location-card:hover {
background: rgba(16, 185, 129, 0.12);
border-color: rgba(16, 185, 129, 0.25);
}
.fclk-location-icon {
flex-shrink: 0;
width: 36px;
height: 36px;
background: rgba(16, 185, 129, 0.15);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.fclk-location-info {
flex: 1;
min-width: 0;
}
.fclk-location-name {
color: var(--fclk-text);
font-size: 14px;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.fclk-location-address {
color: var(--fclk-text-muted);
font-size: 12px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.fclk-location-arrow {
flex-shrink: 0;
opacity: 0.5;
}
/* ---- Scheduled Shift Card ---- */
.fclk-schedule-card {
display: flex;
align-items: center;
gap: 12px;
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
border-radius: 14px;
padding: 14px 16px;
margin: -14px 0 28px;
box-shadow: var(--fclk-shadow);
}
.fclk-schedule-icon {
width: 38px;
height: 38px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
background: rgba(59, 130, 246, 0.12);
color: var(--fclk-blue);
font-size: 16px;
}
.fclk-schedule-info {
min-width: 0;
flex: 1;
}
.fclk-schedule-label {
color: var(--fclk-text-muted);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.fclk-schedule-value {
color: var(--fclk-text);
font-size: 14px;
font-weight: 650;
margin-top: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.fclk-schedule-hours {
color: var(--fclk-text);
font-size: 18px;
font-weight: 700;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
/* ---- Timer Section ---- */
.fclk-timer-section {
text-align: center;
margin-bottom: 28px;
}
.fclk-timer-label {
color: var(--fclk-text-muted);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 12px;
}
.fclk-timer {
color: var(--fclk-text);
font-size: clamp(32px, 10vw, 52px);
font-weight: 300;
letter-spacing: 2px;
font-variant-numeric: tabular-nums;
font-family: 'SF Mono', 'Fira Code', 'Courier New', monospace;
white-space: nowrap;
}
/* ---- Clock Button ---- */
.fclk-button-section {
text-align: center;
margin-bottom: 36px;
}
.fclk-clock-btn {
width: 120px;
height: 120px;
border-radius: 50%;
border: none;
background: linear-gradient(135deg, #10B981, #059669);
box-shadow: 0 8px 32px var(--fclk-green-glow), 0 0 0 0 var(--fclk-green-glow);
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
outline: none;
-webkit-tap-highlight-color: transparent;
}
.fclk-clock-btn:hover {
transform: scale(1.05);
box-shadow: 0 12px 40px var(--fclk-green-glow);
}
.fclk-clock-btn:active {
transform: scale(0.95);
}
.fclk-clock-btn-out {
background: linear-gradient(135deg, #ef4444, #dc2626);
box-shadow: 0 8px 32px var(--fclk-red-glow);
animation: fclk-btn-pulse 2s ease-in-out infinite;
}
.fclk-clock-btn-out:hover {
box-shadow: 0 12px 40px var(--fclk-red-glow);
}
@keyframes fclk-btn-pulse {
0%, 100% { box-shadow: 0 8px 32px var(--fclk-red-glow); }
50% { box-shadow: 0 8px 48px var(--fclk-red-glow), 0 0 0 12px rgba(239, 68, 68, 0.1); }
}
.fclk-btn-icon {
position: relative;
z-index: 1;
}
#fclk-btn-icon-play {
transform: translateX(4px);
}
.fclk-btn-ripple {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
transform: translate(-50%, -50%);
pointer-events: none;
}
.fclk-btn-ripple.fclk-ripple-active {
animation: fclk-ripple 0.6s ease-out;
}
@keyframes fclk-ripple {
from {
width: 0;
height: 0;
opacity: 1;
}
to {
width: 200px;
height: 200px;
opacity: 0;
}
}
.fclk-btn-label {
color: var(--fclk-text-muted);
font-size: 14px;
margin-top: 16px;
}
/* ---- Stats Row ---- */
.fclk-stats-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 28px;
}
.fclk-stat-card {
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
border-radius: 16px;
padding: 16px;
box-shadow: var(--fclk-shadow);
}
.fclk-stat-header {
display: flex;
align-items: center;
gap: 6px;
color: var(--fclk-text-muted);
font-size: 12px;
margin-bottom: 8px;
}
.fclk-stat-value {
color: var(--fclk-text);
font-size: 32px;
font-weight: 700;
line-height: 1;
margin-bottom: 4px;
}
.fclk-stat-target {
color: var(--fclk-text-dim);
font-size: 12px;
}
/* ---- Request Leave Button ---- */
.fclk-leave-btn {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
border-radius: 14px;
padding: 16px 20px;
margin-bottom: 28px;
color: var(--fclk-text);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: var(--fclk-shadow);
text-align: left;
font-family: inherit;
}
.fclk-leave-btn svg:first-child {
color: var(--fclk-green);
flex-shrink: 0;
}
.fclk-leave-btn-arrow {
margin-left: auto;
color: var(--fclk-text-dim);
flex-shrink: 0;
transition: transform 0.2s ease;
}
.fclk-leave-btn:hover {
background: var(--fclk-hover-bg);
border-color: rgba(16, 185, 129, 0.3);
}
.fclk-leave-btn:hover .fclk-leave-btn-arrow {
transform: translateX(2px);
}
.fclk-leave-btn:active {
transform: scale(0.99);
}
/* ---- Recent Activity ---- */
.fclk-recent-section {
margin-bottom: 24px;
}
.fclk-recent-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.fclk-recent-header h3 {
color: var(--fclk-text);
font-size: 18px;
font-weight: 600;
margin: 0;
}
.fclk-view-all {
color: var(--fclk-blue);
font-size: 13px;
text-decoration: none;
}
.fclk-recent-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.fclk-recent-item {
display: flex;
align-items: center;
gap: 14px;
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
border-radius: 12px;
padding: 14px 16px;
transition: background 0.2s ease;
}
.fclk-recent-item:hover {
background: var(--fclk-hover-bg);
}
.fclk-recent-date {
text-align: center;
min-width: 40px;
}
.fclk-recent-day-name {
color: var(--fclk-text-muted);
font-size: 11px;
text-transform: uppercase;
}
.fclk-recent-day-num {
color: var(--fclk-text);
font-size: 22px;
font-weight: 700;
line-height: 1.1;
}
.fclk-recent-info {
flex: 1;
min-width: 0;
}
.fclk-recent-location {
color: var(--fclk-text);
font-size: 14px;
font-weight: 500;
}
.fclk-recent-times {
color: var(--fclk-text-muted);
font-size: 12px;
}
.fclk-recent-hours {
color: var(--fclk-text);
font-size: 16px;
font-weight: 600;
flex-shrink: 0;
}
.fclk-recent-empty {
text-align: center;
color: var(--fclk-text-dim);
padding: 24px;
font-size: 14px;
}
/* ---- Bottom Navigation ---- */
.fclk-nav-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--fclk-card);
border-top: 1px solid var(--fclk-card-border);
display: flex;
justify-content: center;
gap: 0;
padding: 8px 0;
padding-bottom: max(8px, env(safe-area-inset-bottom));
z-index: 100;
max-width: 100%;
}
.fclk-nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 8px 24px;
color: var(--fclk-text-dim);
text-decoration: none;
font-size: 11px;
transition: color 0.2s ease;
}
.fclk-nav-item:hover,
.fclk-nav-active {
color: var(--fclk-green);
text-decoration: none;
}
/* ---- Legacy Modal (location picker still uses this) ---- */
.fclk-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 200;
display: flex;
align-items: flex-end;
justify-content: center;
}
.fclk-modal-backdrop {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.35);
backdrop-filter: blur(4px);
}
.fclk-modal-content {
position: relative;
background: var(--fclk-card);
border-radius: 20px 20px 0 0;
padding: 24px 20px 40px;
width: 100%;
max-width: 480px;
max-height: 60vh;
overflow-y: auto;
animation: fclk-slide-up 0.3s ease-out;
}
.fclk-modal-content h3 {
color: var(--fclk-text);
font-size: 18px;
font-weight: 600;
margin: 0 0 16px;
}
@keyframes fclk-slide-up {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
/* ============================================================
Wizard Dialogs - Professional modals for reasons, confirmations
Theme-aware, works in both light and dark mode
============================================================ */
/* Standalone fallbacks for wizard modals rendered outside .fclk-app */
.fclk-wizard-overlay {
--fclk-wiz-card: #ffffff;
--fclk-wiz-card-border: #e5e7eb;
--fclk-wiz-bg: #f3f4f6;
--fclk-wiz-text: #1f2937;
--fclk-wiz-text-muted: #6b7280;
--fclk-wiz-text-dim: #9ca3af;
--fclk-wiz-green: #10B981;
--fclk-wiz-green-glow: rgba(16, 185, 129, 0.25);
--fclk-wiz-hover-bg: #f9fafb;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1055;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
@media (prefers-color-scheme: dark) {
.fclk-wizard-overlay {
--fclk-wiz-card: #1a1d23;
--fclk-wiz-card-border: #2a2d35;
--fclk-wiz-bg: #0f1117;
--fclk-wiz-text: #ffffff;
--fclk-wiz-text-muted: #9ca3af;
--fclk-wiz-text-dim: #6b7280;
--fclk-wiz-green-glow: rgba(16, 185, 129, 0.3);
--fclk-wiz-hover-bg: #1e2128;
}
}
html.o_dark .fclk-wizard-overlay {
--fclk-wiz-card: #1a1d23;
--fclk-wiz-card-border: #2a2d35;
--fclk-wiz-bg: #0f1117;
--fclk-wiz-text: #ffffff;
--fclk-wiz-text-muted: #9ca3af;
--fclk-wiz-text-dim: #6b7280;
--fclk-wiz-green-glow: rgba(16, 185, 129, 0.3);
--fclk-wiz-hover-bg: #1e2128;
}
.fclk-wizard-backdrop {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
}
.fclk-wizard-dialog {
position: relative;
background: var(--fclk-wiz-card, #ffffff);
border: 1px solid var(--fclk-wiz-card-border, #e5e7eb);
border-radius: 20px;
width: 100%;
max-width: 440px;
max-height: 85vh;
overflow-y: auto;
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.05);
animation: fclk-wizard-enter 0.3s cubic-bezier(0.32, 0.72, 0, 1);
}
.fclk-wizard-dialog--compact {
max-width: 380px;
}
@keyframes fclk-wizard-enter {
from {
opacity: 0;
transform: scale(0.95) translateY(8px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.fclk-wizard-header {
padding: 28px 24px 20px;
text-align: center;
border-bottom: 1px solid var(--fclk-wiz-card-border, #e5e7eb);
}
.fclk-wizard-header-icon {
width: 56px;
height: 56px;
border-radius: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
margin-bottom: 16px;
}
.fclk-wizard-header--warning .fclk-wizard-header-icon {
background: rgba(245, 158, 11, 0.12);
color: #f59e0b;
}
.fclk-wizard-header--danger .fclk-wizard-header-icon {
background: rgba(239, 68, 68, 0.12);
color: #ef4444;
}
.fclk-wizard-header--info .fclk-wizard-header-icon {
background: rgba(59, 130, 246, 0.12);
color: #3b82f6;
}
.fclk-wizard-title {
color: var(--fclk-wiz-text, #1f2937);
font-size: 20px;
font-weight: 700;
margin: 0 0 6px;
letter-spacing: -0.3px;
}
.fclk-wizard-subtitle {
color: var(--fclk-wiz-text-muted, #6b7280);
font-size: 13px;
line-height: 1.5;
margin: 0;
}
.fclk-wizard-body {
padding: 24px;
}
.fclk-wizard-field {
margin-bottom: 20px;
}
.fclk-wizard-field:last-child {
margin-bottom: 0;
}
.fclk-wizard-label {
display: flex;
align-items: center;
gap: 6px;
color: var(--fclk-wiz-text, #1f2937);
font-size: 13px;
font-weight: 600;
margin-bottom: 8px;
}
.fclk-wizard-label svg {
color: var(--fclk-wiz-text-muted, #6b7280);
flex-shrink: 0;
}
.fclk-wizard-required {
color: #ef4444;
font-weight: 700;
}
.fclk-wizard-input {
width: 100%;
background: var(--fclk-wiz-bg, #f3f4f6);
border: 1.5px solid var(--fclk-wiz-card-border, #e5e7eb);
border-radius: 12px;
padding: 12px 14px;
font-size: 14px;
color: var(--fclk-wiz-text, #1f2937);
transition: border-color 0.2s, box-shadow 0.2s;
outline: none;
font-family: inherit;
}
.fclk-wizard-input:focus {
border-color: var(--fclk-wiz-green, #10B981);
box-shadow: 0 0 0 3px var(--fclk-wiz-green-glow, rgba(16, 185, 129, 0.25));
}
.fclk-wizard-input::placeholder {
color: var(--fclk-wiz-text-dim, #9ca3af);
}
.fclk-wizard-textarea {
resize: vertical;
min-height: 80px;
}
.fclk-wizard-hint {
display: block;
color: var(--fclk-wiz-text-dim, #9ca3af);
font-size: 11px;
margin-top: 6px;
}
.fclk-wizard-footer {
padding: 16px 24px 20px;
display: flex;
gap: 10px;
justify-content: flex-end;
border-top: 1px solid var(--fclk-wiz-card-border, #e5e7eb);
}
.fclk-wizard-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 10px 20px;
border-radius: 12px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
border: none;
transition: all 0.2s ease;
letter-spacing: 0.2px;
}
.fclk-wizard-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.fclk-wizard-btn--primary {
background: linear-gradient(135deg, #10B981, #059669);
color: #fff;
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3);
}
.fclk-wizard-btn--primary:hover:not(:disabled) {
box-shadow: 0 4px 16px rgba(16, 185, 129, 0.4);
transform: translateY(-1px);
}
.fclk-wizard-btn--danger {
background: linear-gradient(135deg, #ef4444, #dc2626);
color: #fff;
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.3);
}
.fclk-wizard-btn--danger:hover:not(:disabled) {
box-shadow: 0 4px 16px rgba(239, 68, 68, 0.4);
transform: translateY(-1px);
}
.fclk-wizard-btn--secondary {
background: var(--fclk-wiz-bg, #f3f4f6);
color: var(--fclk-wiz-text-muted, #6b7280);
border: 1px solid var(--fclk-wiz-card-border, #e5e7eb);
}
.fclk-wizard-btn--secondary:hover:not(:disabled) {
background: var(--fclk-wiz-hover-bg, #f9fafb);
color: var(--fclk-wiz-text, #1f2937);
}
/* Clock-out confirmation summary card */
.fclk-clockout-summary {
background: var(--fclk-wiz-bg, #f3f4f6);
border: 1px solid var(--fclk-wiz-card-border, #e5e7eb);
border-radius: 12px;
padding: 16px;
}
.fclk-clockout-summary-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
}
.fclk-clockout-summary-row + .fclk-clockout-summary-row {
border-top: 1px solid var(--fclk-wiz-card-border, #e5e7eb);
}
.fclk-clockout-summary-label {
color: var(--fclk-wiz-text-muted, #6b7280);
font-size: 13px;
}
.fclk-clockout-summary-value {
color: var(--fclk-wiz-text, #1f2937);
font-size: 14px;
font-weight: 600;
}
.fclk-modal-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.fclk-modal-item {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px;
border-radius: 12px;
cursor: pointer;
transition: background 0.2s ease;
}
.fclk-modal-item:hover {
background: rgba(16, 185, 129, 0.08);
}
.fclk-modal-item-icon {
flex-shrink: 0;
}
.fclk-modal-item-name {
color: var(--fclk-wiz-text, #1f2937);
font-size: 14px;
font-weight: 500;
}
.fclk-modal-item-addr {
color: var(--fclk-wiz-text-muted, #6b7280);
font-size: 12px;
}
/* ---- Toast ---- */
.fclk-toast {
position: fixed;
top: 24px;
left: 50%;
transform: translateX(-50%);
background: var(--fclk-toast-bg, var(--fclk-card));
border: 1px solid var(--fclk-card-border);
border-radius: 12px;
padding: 12px 20px;
display: flex;
align-items: center;
gap: 10px;
z-index: 300;
box-shadow: var(--fclk-shadow);
animation: fclk-toast-in 0.3s ease-out;
max-width: 90%;
}
.fclk-toast-success {
border-color: var(--fclk-green);
}
.fclk-toast-error {
border-color: var(--fclk-red);
}
@keyframes fclk-toast-in {
from { transform: translateX(-50%) translateY(-20px); opacity: 0; }
to { transform: translateX(-50%) translateY(0); opacity: 1; }
}
@keyframes fclk-toast-out {
from { transform: translateX(-50%) translateY(0); opacity: 1; }
to { transform: translateX(-50%) translateY(-20px); opacity: 0; }
}
.fclk-toast-msg {
color: var(--fclk-text);
font-size: 14px;
}
/* ---- GPS Overlay ---- */
.fclk-gps-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--fclk-overlay-bg);
backdrop-filter: blur(8px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 250;
gap: 16px;
}
.fclk-gps-spinner {
width: 48px;
height: 48px;
border: 3px solid var(--fclk-card-border);
border-top: 3px solid var(--fclk-green);
border-radius: 50%;
animation: fclk-spin 0.8s linear infinite;
}
@keyframes fclk-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.fclk-gps-text {
color: var(--fclk-text-muted);
font-size: 14px;
}
/* ---- Error Shake ---- */
.fclk-shake {
animation: fclk-shake 0.5s ease-in-out;
}
@keyframes fclk-shake {
0%, 100% { transform: translateX(0); }
20% { transform: translateX(-10px); }
40% { transform: translateX(10px); }
60% { transform: translateX(-6px); }
80% { transform: translateX(6px); }
}
/* ---- Responsive ---- */
@media (max-width: 420px) {
.fclk-clock-btn {
width: 100px;
height: 100px;
}
.fclk-clock-btn svg {
width: 36px;
height: 36px;
}
.fclk-stat-value {
font-size: 26px;
}
}
/* ---- Timesheet Page ---- */
.fclk-timesheet-container {
max-width: 600px;
margin: 0 auto;
padding: 24px 20px 100px;
}
.fclk-ts-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.fclk-ts-header h2 {
color: var(--fclk-text);
font-size: 22px;
font-weight: 700;
margin: 0;
}
.fclk-ts-period-nav {
display: flex;
gap: 8px;
}
.fclk-ts-period-btn {
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
color: var(--fclk-text-muted);
padding: 8px 16px;
border-radius: 8px;
font-size: 13px;
cursor: pointer;
text-decoration: none;
transition: all 0.2s ease;
}
.fclk-ts-period-btn:hover,
.fclk-ts-period-btn-active {
background: var(--fclk-green);
color: white;
border-color: var(--fclk-green);
text-decoration: none;
}
.fclk-ts-summary {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 24px;
}
.fclk-ts-summary-card {
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
border-radius: 12px;
padding: 16px;
text-align: center;
}
.fclk-ts-summary-value {
color: var(--fclk-text);
font-size: 24px;
font-weight: 700;
}
.fclk-ts-summary-label {
color: var(--fclk-text-muted);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-top: 4px;
}
.fclk-ts-table {
width: 100%;
border-collapse: separate;
border-spacing: 0 6px;
}
.fclk-ts-table th {
color: var(--fclk-text-muted);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 8px 12px;
text-align: left;
font-weight: 500;
}
.fclk-ts-table td {
background: var(--fclk-card);
color: var(--fclk-text);
font-size: 13px;
padding: 12px;
}
.fclk-ts-table tr td:first-child {
border-radius: 8px 0 0 8px;
}
.fclk-ts-table tr td:last-child {
border-radius: 0 8px 8px 0;
}
.fclk-ts-badge-auto {
background: rgba(239, 68, 68, 0.15);
color: var(--fclk-red);
font-size: 10px;
padding: 2px 6px;
border-radius: 4px;
}
/* Responsive timesheet entries — stacked cards instead of a cramped table.
Reads cleanly at any phone/tablet width; no horizontal overflow. */
.fclk-ts-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.fclk-ts-card {
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
border-radius: 12px;
padding: 14px 16px;
}
.fclk-ts-card-top {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 8px;
}
.fclk-ts-card-date {
color: var(--fclk-text);
font-size: 14px;
font-weight: 700;
}
.fclk-ts-card-date span {
color: var(--fclk-text-dim);
font-weight: 400;
margin-left: 6px;
}
.fclk-ts-card-net {
color: var(--fclk-green);
font-weight: 700;
font-size: 15px;
white-space: nowrap;
}
.fclk-ts-card-times {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
color: var(--fclk-text);
font-size: 14px;
}
.fclk-ts-arrow {
color: var(--fclk-text-dim);
}
.fclk-ts-k {
color: var(--fclk-text-dim);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.4px;
margin-right: 4px;
}
.fclk-ts-card-meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
color: var(--fclk-text-muted);
font-size: 12px;
}
.fclk-ts-dot {
color: var(--fclk-text-dim);
}
.fclk-ts-correct {
margin-left: auto;
color: var(--fclk-text-muted);
font-size: 12px;
text-decoration: none;
}
.fclk-ts-correct:hover {
color: var(--fclk-green);
text-decoration: underline;
}
/* Leave request: From / To date-range row.
Grid (not flex) so it stays two columns on every width — iOS date inputs
have a large intrinsic min-width that can break a flex row; grid 1fr 1fr +
min-width:0 forces them to share the row and shrink. */
.fclk-leave-daterange {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.fclk-leave-daterange-col {
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.fclk-leave-daterange-col .fclk-wizard-input {
width: 100%;
min-width: 0;
box-sizing: border-box;
}
.fclk-leave-daterange-cap {
font-size: 11px;
color: var(--fclk-text-muted);
text-transform: uppercase;
letter-spacing: 0.4px;
}
.fclk-leave-daterange-cap small {
text-transform: none;
letter-spacing: 0;
color: var(--fclk-text-dim);
}
/* ---- Reports Page ---- */
.fclk-reports-container {
max-width: 600px;
margin: 0 auto;
padding: 24px 20px 100px;
}
.fclk-report-item {
display: flex;
align-items: center;
justify-content: space-between;
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
border-radius: 12px;
padding: 16px;
margin-bottom: 8px;
transition: background 0.2s ease;
}
.fclk-report-item:hover {
background: var(--fclk-hover-bg);
}
.fclk-report-info h4 {
color: var(--fclk-text);
font-size: 14px;
font-weight: 600;
margin: 0 0 4px;
}
.fclk-report-info p {
color: var(--fclk-text-muted);
font-size: 12px;
margin: 0;
}
.fclk-report-download {
background: var(--fclk-green);
color: white;
border: none;
border-radius: 8px;
padding: 8px 16px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
text-decoration: none;
transition: opacity 0.2s;
}
.fclk-report-download:hover {
opacity: 0.85;
color: white;
text-decoration: none;
}
.fclk-empty-state {
text-align: center;
color: var(--fclk-text-dim);
padding: 48px 24px;
font-size: 14px;
}
.fclk-empty-state svg {
margin-bottom: 12px;
opacity: 0.4;
}
/* ============================================================
Portal FAB - Floating clock-in/out button on all portal pages
Matches the backend FAB design (gradient, ripple, theme-aware)
============================================================ */
/* ---- Theme tokens (light default) ---- */
#fclk-portal-fab {
--pfab-panel-bg: #ffffff;
--pfab-panel-border: #e5e7eb;
--pfab-panel-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
--pfab-text: #1f2937;
--pfab-muted: #6b7280;
--pfab-divider: #e5e7eb;
--pfab-loc-bg: rgba(16, 185, 129, 0.08);
--pfab-error-bg: rgba(239, 68, 68, 0.06);
--pfab-arrow-bg: #ffffff;
--pfab-green: #10B981;
--pfab-red: #ef4444;
--pfab-teal: #0d9488;
--pfab-blue: #2563eb;
}
/* ---- Dark theme (OS preference) ---- */
@media (prefers-color-scheme: dark) {
#fclk-portal-fab {
--pfab-panel-bg: #1e2028;
--pfab-panel-border: #3a3d48;
--pfab-panel-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
--pfab-text: #f1f1f4;
--pfab-muted: #9ca3af;
--pfab-divider: #3a3d48;
--pfab-loc-bg: rgba(16, 185, 129, 0.1);
--pfab-error-bg: rgba(239, 68, 68, 0.1);
--pfab-arrow-bg: #1e2028;
}
}
/* ---- Dark theme (Odoo class) ---- */
html.o_dark #fclk-portal-fab {
--pfab-panel-bg: #1e2028;
--pfab-panel-border: #3a3d48;
--pfab-panel-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
--pfab-text: #f1f1f4;
--pfab-muted: #9ca3af;
--pfab-divider: #3a3d48;
--pfab-loc-bg: rgba(16, 185, 129, 0.1);
--pfab-error-bg: rgba(239, 68, 68, 0.1);
--pfab-arrow-bg: #1e2028;
}
/* ---- Wrapper: bottom-left, like the backend FAB ---- */
#fclk-portal-fab {
position: fixed;
bottom: 24px;
left: 24px;
z-index: 1050;
display: flex;
flex-direction: column-reverse;
align-items: flex-start;
gap: 12px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
/* ---- FAB Button ---- */
.fclk-pfab-btn {
position: relative;
width: 54px;
height: 54px;
border-radius: 50%;
border: none;
background: linear-gradient(135deg, var(--pfab-teal) 0%, var(--pfab-blue) 100%);
color: #fff;
font-size: 21px;
cursor: pointer;
box-shadow: 0 4px 20px rgba(13, 148, 136, 0.35);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
align-items: center;
justify-content: center;
outline: none;
overflow: visible;
-webkit-tap-highlight-color: transparent;
}
.fclk-pfab-btn:hover {
transform: scale(1.1);
box-shadow: 0 6px 28px rgba(13, 148, 136, 0.45);
}
.fclk-pfab-btn:active {
transform: scale(0.93);
}
/* Active (clocked in): green-teal gradient */
.fclk-pfab-btn--active {
background: linear-gradient(135deg, var(--pfab-green) 0%, var(--pfab-teal) 100%);
box-shadow: 0 4px 20px rgba(16, 185, 129, 0.4);
}
.fclk-pfab-btn--active:hover {
box-shadow: 0 6px 28px rgba(16, 185, 129, 0.5);
}
.fclk-pfab-icon {
position: relative;
z-index: 2;
line-height: 1;
}
/* ---- Ripple rings (only visible when clocked in) ---- */
.fclk-pfab-ripple-ring {
position: absolute;
top: 50%;
left: 50%;
width: 54px;
height: 54px;
border-radius: 50%;
border: 2px solid rgba(16, 185, 129, 0.5);
transform: translate(-50%, -50%) scale(1);
pointer-events: none;
z-index: 1;
opacity: 0;
}
/* Only animate ripples when clocked in */
.fclk-pfab--active .fclk-pfab-ripple-ring--1 {
animation: fclk-pfab-ripple-out 2.4s ease-out infinite;
}
.fclk-pfab--active .fclk-pfab-ripple-ring--2 {
animation: fclk-pfab-ripple-out 2.4s ease-out 0.8s infinite;
}
.fclk-pfab--active .fclk-pfab-ripple-ring--3 {
animation: fclk-pfab-ripple-out 2.4s ease-out 1.6s infinite;
}
@keyframes fclk-pfab-ripple-out {
0% {
transform: translate(-50%, -50%) scale(1);
opacity: 0.55;
}
100% {
transform: translate(-50%, -50%) scale(2.6);
opacity: 0;
}
}
/* ---- Mini timer badge ---- */
.fclk-pfab-badge {
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
background: #111827;
color: var(--pfab-green);
font-size: 9px;
font-weight: 700;
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
padding: 2px 7px;
border-radius: 10px;
white-space: nowrap;
letter-spacing: 0.5px;
border: 1px solid rgba(16, 185, 129, 0.35);
pointer-events: none;
z-index: 3;
animation: fclk-pfab-badge-in 0.3s ease;
}
@keyframes fclk-pfab-badge-in {
from { opacity: 0; transform: translateX(-50%) translateY(4px); }
to { opacity: 1; transform: translateX(-50%) translateY(0); }
}
/* ---- Expandable Panel ---- */
.fclk-pfab-panel {
width: 280px;
background: var(--pfab-panel-bg);
border: 1px solid var(--pfab-panel-border);
border-radius: 16px;
padding: 18px;
box-shadow: var(--pfab-panel-shadow);
position: relative;
animation: fclk-pfab-panel-in 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}
.fclk-pfab-panel--open {
display: block;
}
@keyframes fclk-pfab-panel-in {
from { opacity: 0; transform: translateY(12px) scale(0.96); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
/* Arrow pointing down toward the FAB */
.fclk-pfab-panel-arrow {
position: absolute;
bottom: -6px;
left: 22px;
width: 12px;
height: 12px;
background: var(--pfab-arrow-bg);
border-right: 1px solid var(--pfab-panel-border);
border-bottom: 1px solid var(--pfab-panel-border);
transform: rotate(45deg);
}
/* ---- Panel Header ---- */
.fclk-pfab-panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 14px;
}
.fclk-pfab-panel-title {
display: flex;
align-items: center;
gap: 8px;
color: var(--pfab-text);
font-size: 13px;
font-weight: 600;
letter-spacing: 0.3px;
}
.fclk-pfab-status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: #9ca3af;
flex-shrink: 0;
display: inline-block;
}
.fclk-pfab-status-dot--active {
background: var(--pfab-green);
box-shadow: 0 0 6px rgba(16, 185, 129, 0.5);
}
.fclk-pfab-status-text {
color: var(--pfab-text);
}
.fclk-pfab-open-link {
color: var(--pfab-muted);
font-size: 13px;
text-decoration: none;
transition: color 0.2s;
}
.fclk-pfab-open-link:hover {
color: var(--pfab-blue);
text-decoration: none;
}
/* ---- Location chip ---- */
.fclk-pfab-location {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--pfab-green);
background: var(--pfab-loc-bg);
border-radius: 8px;
padding: 6px 10px;
margin-bottom: 12px;
}
.fclk-pfab-location .fa {
font-size: 12px;
}
/* ---- Timer ---- */
.fclk-pfab-timer {
text-align: center;
color: var(--pfab-text);
font-size: 28px;
font-weight: 300;
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
letter-spacing: 2px;
font-variant-numeric: tabular-nums;
margin-bottom: 14px;
line-height: 1;
}
/* ---- Stats row ---- */
.fclk-pfab-stats {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
margin-bottom: 14px;
}
.fclk-pfab-stat {
text-align: center;
}
.fclk-pfab-stat-val {
display: block;
color: var(--pfab-text);
font-size: 16px;
font-weight: 700;
}
.fclk-pfab-stat-lbl {
display: block;
color: var(--pfab-muted);
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.6px;
margin-top: 2px;
}
.fclk-pfab-stat-divider {
width: 1px;
height: 28px;
background: var(--pfab-divider);
}
/* ---- Action button ---- */
.fclk-pfab-action {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
border: none;
border-radius: 12px;
padding: 11px 0;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
letter-spacing: 0.3px;
}
.fclk-pfab-action .fa {
font-size: 15px;
}
.fclk-pfab-action--in {
background: linear-gradient(135deg, var(--pfab-teal) 0%, var(--pfab-blue) 100%);
color: #fff;
}
.fclk-pfab-action--in:hover:not(:disabled) {
box-shadow: 0 4px 16px rgba(13, 148, 136, 0.4);
}
.fclk-pfab-action--out {
background: var(--pfab-red);
color: #fff;
}
.fclk-pfab-action--out:hover:not(:disabled) {
box-shadow: 0 4px 16px rgba(239, 68, 68, 0.35);
}
.fclk-pfab-action:disabled {
opacity: 0.55;
cursor: not-allowed;
}
/* ---- Error ---- */
.fclk-pfab-error {
display: flex;
align-items: flex-start;
gap: 6px;
color: var(--pfab-red);
font-size: 11px;
background: var(--pfab-error-bg);
border-radius: 8px;
padding: 8px 10px;
margin-top: 10px;
line-height: 1.4;
}
.fclk-pfab-error .fa {
font-size: 12px;
margin-top: 1px;
flex-shrink: 0;
}
/* ---- Responsive: smaller screens ---- */
@media (max-width: 380px) {
#fclk-portal-fab {
bottom: 16px;
left: 16px;
}
.fclk-pfab-panel {
width: 260px;
}
}
/* ============================================================
Employee portal — Payslips, 4-item nav, sign out
(uses the --fclk-* palette above, so light/dark just works)
============================================================ */
/* Keep 4 nav items comfortable on narrow phones */
.fclk-nav-bar .fclk-nav-item { min-width: 64px; }
/* Sign out (clock header, top-right) */
.fclk-header { position: relative; }
.fclk-signout {
position: absolute;
top: 0;
right: 0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
border-radius: 10px;
color: var(--fclk-text-muted);
background: var(--fclk-card);
border: 1px solid var(--fclk-card-border);
text-decoration: none;
}
.fclk-signout:hover { color: var(--fclk-text); }
/* Payslip list rows (extend .fclk-report-item) */
.fclk-payslip-item { text-decoration: none; color: inherit; cursor: pointer; }
.fclk-payslip-status {
font-size: 12px;
font-weight: 600;
padding: 3px 10px;
border-radius: 999px;
white-space: nowrap;
}
.fclk-payslip-status--paid { background: var(--fclk-green-glow); color: var(--fclk-green); }
.fclk-payslip-status--done { background: var(--fclk-hover-bg); color: var(--fclk-text-muted); }
/* Payslip detail (inline paystub) */
.fclk-payslip-detail-header .fclk-payslip-back {
display: inline-block;
font-size: 13px;
color: var(--fclk-green);
text-decoration: none;
margin-bottom: 6px;
}
.fclk-payslip-net {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.fclk-payslip-net-label { font-size: 13px; color: var(--fclk-text-muted); }
.fclk-payslip-net-value { font-size: 26px; font-weight: 700; color: var(--fclk-green); }
.fclk-payslip-section { margin-bottom: 16px; }
.fclk-payslip-section-title {
font-size: 13px;
text-transform: uppercase;
letter-spacing: .04em;
color: var(--fclk-text-muted);
margin: 0 0 10px;
}
.fclk-payslip-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 0;
font-size: 14px;
color: var(--fclk-text);
border-bottom: 1px solid var(--fclk-card-border);
}
.fclk-payslip-row:last-child { border-bottom: none; }
.fclk-payslip-row--total { font-weight: 700; }
.fclk-payslip-pdf-btn {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 14px;
margin-bottom: 90px; /* clear the fixed bottom nav */
border-radius: 12px;
background: var(--fclk-green);
color: #fff;
font-weight: 600;
text-decoration: none;
}