Files
Odoo-Modules/fusion_clock/static/src/css/portal_clock.css
gsinghpal a479052b72 fix(fusion_clock): portal white-border + responsive timesheet entries
- White border on every portal page: the .fclk-app full-bleed relied on exact
  negative margins to cancel the portal layout's container padding; when it
  didn't match, the white page chrome showed through. Match the PAGE background
  to the app (light #f3f4f6 / dark #0f1117, via body:has(.fclk-app)) so the
  gutter is invisible, and clip horizontal overflow.
- Timesheets not responsive: the 6-column table crammed/wrapped on phones.
  Replaced the table with stacked cards (date + net up top, in -> out, then
  break / location / Correct) that read cleanly at any width. Correction-link
  data attributes preserved; the xpath-inherited .fclk-nav-bar untouched.

Live on entech 19.0.3.12.2 (both rules verified in the served frontend bundle).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 22:20:56 -04:00

1850 lines
40 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: -16px -15px;
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;
}
.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;
}
/* ---- 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;
}