diff --git a/css/styles.css b/css/styles.css index 7f081fe..76f285d 100644 --- a/css/styles.css +++ b/css/styles.css @@ -286,130 +286,89 @@ html, body { } /* ========================================================================== - SCREEN 1: START / HERO VIEW + SCREEN 1: START / HERO VIEW WITH DEDICATED HQ HOME SCREEN GRAPHIC ========================================================================== */ -.intro-card { - max-width: 820px; - margin: 30px auto; - background: linear-gradient(180deg, #131d2e 0%, #0d1524 100%); - border: 1px solid var(--panel-border); - border-radius: var(--radius-lg); - padding: 40px; - text-align: center; - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5); +#screen-start { + position: relative; + width: 100%; + flex: 1; display: flex; - flex-direction: column; align-items: center; + justify-content: center; + padding: 4px 0; } -.hero-graphic { - margin-bottom: 20px; - position: relative; -} - -.safety-shield-animated { - width: 80px; - height: 80px; +.home-screen-backdrop { position: relative; + width: 100%; + max-width: 1440px; + margin: 0 auto; + aspect-ratio: 1024 / 458; + border-radius: var(--radius-lg); + overflow: hidden; + border: 1px solid var(--panel-border-light); + box-shadow: 0 16px 50px rgba(0, 0, 0, 0.7); + background: #070d18; display: flex; align-items: center; justify-content: center; } -.shield-svg { +.home-bg-image { width: 100%; height: 100%; + object-fit: contain; + object-position: center; + display: block; + user-select: none; + pointer-events: none; + image-rendering: -webkit-optimize-contrast; + image-rendering: high-quality; + transform: translateZ(0); } -.shield-outline { - fill: #1e293b; - stroke: var(--safety-yellow); - stroke-width: 4; -} - -.shield-plus { - stroke: var(--safety-yellow); - stroke-width: 6; - stroke-linecap: round; -} - -.hero-title { - font-family: var(--font-display); - font-size: 2.2rem; - font-weight: 700; - letter-spacing: 1px; - color: #fff; - margin-bottom: 4px; -} - -.hero-subtitle { - font-size: 1.1rem; - font-weight: 700; - color: var(--safety-yellow); - letter-spacing: 3px; - margin-bottom: 24px; - text-transform: uppercase; -} - -.mission-briefing { - background: rgba(0, 0, 0, 0.35); - border: 1px solid var(--panel-border); - border-radius: var(--radius-md); - padding: 24px; - margin-bottom: 28px; - text-align: center; - max-width: 700px; -} - -.mission-briefing h3 { - font-size: 1.1rem; - font-weight: 700; - color: #fff; - margin-bottom: 8px; -} - -.mission-briefing p { - color: var(--text-muted); - font-size: 0.92rem; - margin-bottom: 18px; - line-height: 1.5; -} - -.learning-flow-banner { +.btn-start-hero-overlay { + position: absolute; + top: 78.5%; + left: 50.8%; + transform: translate(-50%, -50%); + width: 24.8%; + height: 9.8%; + min-width: 180px; + min-height: 40px; + background: transparent; + border: none; + cursor: pointer; + border-radius: 14px; + transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1); + z-index: 10; display: flex; align-items: center; justify-content: center; - flex-wrap: wrap; - gap: 10px; - background: #090f19; - padding: 12px 18px; - border-radius: var(--radius-md); - border: 1px solid var(--panel-border); + text-decoration: none; + outline: none; } -.flow-item { - font-family: var(--font-mono); - font-size: 0.78rem; - font-weight: 700; - color: var(--safety-yellow); +.btn-hero-hover-text { + opacity: 0; + font-family: var(--font-display); + font-size: 1.15rem; + font-weight: 900; + color: #000; + letter-spacing: 1px; + transition: opacity 0.2s ease; + user-select: none; } -.flow-arrow { - color: var(--text-dim); - font-size: 0.85rem; +.btn-start-hero-overlay:hover { + background: rgba(250, 204, 21, 0.12); + box-shadow: 0 0 40px rgba(250, 204, 21, 0.55); + transform: translate(-50%, -50%) scale(1.015); } -.start-controls { - display: flex; - flex-direction: column; - align-items: center; - gap: 16px; -} - -.controls-hint { - font-size: 0.8rem; - color: var(--text-dim); +.btn-start-hero-overlay:active { + transform: translate(-50%, -50%) scale(0.985); } /* Common Button Styles */ @@ -714,16 +673,109 @@ html, body { pointer-events: none; } +.scene-img-backdrop { + position: relative; + width: 100%; + height: 100%; +} + +.env-stage-photo { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center 60%; + display: block; + filter: contrast(1.03) brightness(0.98); + image-rendering: -webkit-optimize-contrast; + image-rendering: high-quality; + transform: translateZ(0); + backface-visibility: hidden; + transition: object-position 0.3s ease; +} + +.stage-chemical .env-stage-photo { + object-position: center 48%; +} + +.stage-grinding .env-stage-photo { + object-position: center 58%; +} + +.env-stage-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: radial-gradient(circle at 50% 55%, rgba(0, 0, 0, 0.0) 0%, rgba(0, 0, 0, 0.35) 100%), linear-gradient(180deg, rgba(0, 0, 0, 0.2) 0%, transparent 35%, rgba(0, 0, 0, 0.35) 100%); + pointer-events: none; +} + .avatar-wrapper { position: relative; z-index: 10; - width: 280px; + width: 290px; height: 440px; display: flex; align-items: center; justify-content: center; } +.worker-svg { + width: 100%; + height: 100%; + overflow: visible; + animation: characterAuraPulse 3s ease-in-out infinite; + transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1); +} + +.worker-svg image { + transition: opacity 0.2s ease-in-out; +} + +@keyframes characterAuraPulse { + 0% { + filter: drop-shadow(0 0 5px rgba(56, 189, 248, 0.45)) + drop-shadow(0 0 12px rgba(56, 189, 248, 0.2)) + drop-shadow(0 14px 24px rgba(0, 0, 0, 0.6)); + } + 50% { + filter: drop-shadow(0 0 10px rgba(56, 189, 248, 0.8)) + drop-shadow(0 0 22px rgba(56, 189, 248, 0.45)) + drop-shadow(0 0 34px rgba(56, 189, 248, 0.25)) + drop-shadow(0 14px 24px rgba(0, 0, 0, 0.65)); + } + 100% { + filter: drop-shadow(0 0 5px rgba(56, 189, 248, 0.45)) + drop-shadow(0 0 12px rgba(56, 189, 248, 0.2)) + drop-shadow(0 14px 24px rgba(0, 0, 0, 0.6)); + } +} + +/* Specific Equipped PPE Item Electric Cyan/Blue Glow Pop */ +.gear-pop-glow { + animation: ppeGearPopGlow 0.55s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +@keyframes ppeGearPopGlow { + 0% { + filter: drop-shadow(0 0 16px #38bdf8) drop-shadow(0 0 32px #0284c7); + opacity: 0.2; + transform: scale(1.04); + transform-origin: center center; + } + 40% { + filter: drop-shadow(0 0 20px #38bdf8) drop-shadow(0 0 36px #38bdf8); + opacity: 1; + transform: scale(1.02); + } + 100% { + filter: url(#gearShadow); + opacity: 1; + transform: scale(1); + } +} + .avatar-step-container { position: relative; width: 100%; @@ -1048,13 +1100,34 @@ html, body { color: var(--safety-cyan); } -/* 3x3 Grid Layout (Square Cards) */ +/* 3-Column Grid Layout for 10 Locker Items */ .ppe-locker-grid { display: grid; grid-template-columns: repeat(3, 1fr); - gap: 10px; + gap: 8px; width: 100%; margin-bottom: auto; + max-height: calc(100vh - 265px); + overflow-y: auto; + padding-right: 2px; +} + +.ppe-locker-grid::-webkit-scrollbar { + width: 5px; +} + +.ppe-locker-grid::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.25); + border-radius: 4px; +} + +.ppe-locker-grid::-webkit-scrollbar-thumb { + background: #1e3556; + border-radius: 4px; +} + +.ppe-locker-grid::-webkit-scrollbar-thumb:hover { + background: var(--safety-cyan); } .ppe-item-card { @@ -1066,11 +1139,11 @@ html, body { background: #09121f; border: 1.5px solid #162740; border-radius: var(--radius-md); - padding: 8px 4px 6px 4px; + padding: 6px 3px 5px 3px; cursor: pointer; transition: var(--transition-normal); position: relative; - aspect-ratio: 1 / 1.18; + aspect-ratio: 1 / 1.15; width: 100%; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35); } diff --git a/grinding_ppe_icons_png/07_fire_resistant_apron.png b/grinding_ppe_icons_png/07_fire_resistant_apron.png new file mode 100644 index 0000000..5bdef4f Binary files /dev/null and b/grinding_ppe_icons_png/07_fire_resistant_apron.png differ diff --git a/grinding_ppe_icons_png/07_reflective_vest.png b/grinding_ppe_icons_png/07_reflective_vest.png index 0e27863..991e7b6 100644 Binary files a/grinding_ppe_icons_png/07_reflective_vest.png and b/grinding_ppe_icons_png/07_reflective_vest.png differ diff --git a/grinding_ppe_icons_png/09_reflective_vest.png b/grinding_ppe_icons_png/09_reflective_vest.png new file mode 100644 index 0000000..991e7b6 Binary files /dev/null and b/grinding_ppe_icons_png/09_reflective_vest.png differ diff --git a/images/background_chemical.jpg b/images/background_chemical.jpg new file mode 100644 index 0000000..8be04b7 Binary files /dev/null and b/images/background_chemical.jpg differ diff --git a/images/background_grinding.jpg b/images/background_grinding.jpg new file mode 100644 index 0000000..67fe9d5 Binary files /dev/null and b/images/background_grinding.jpg differ diff --git a/images/crop_glasses.png b/images/crop_glasses.png new file mode 100644 index 0000000..53b7f9f Binary files /dev/null and b/images/crop_glasses.png differ diff --git a/images/crop_head_full.png b/images/crop_head_full.png new file mode 100644 index 0000000..9b608ae Binary files /dev/null and b/images/crop_head_full.png differ diff --git a/images/crop_helmet_dome.png b/images/crop_helmet_dome.png new file mode 100644 index 0000000..4317d38 Binary files /dev/null and b/images/crop_helmet_dome.png differ diff --git a/images/crop_helmet_earmuffs.png b/images/crop_helmet_earmuffs.png new file mode 100644 index 0000000..1b89836 Binary files /dev/null and b/images/crop_helmet_earmuffs.png differ diff --git a/images/crop_left_boot.png b/images/crop_left_boot.png new file mode 100644 index 0000000..f106618 Binary files /dev/null and b/images/crop_left_boot.png differ diff --git a/images/crop_left_glove.png b/images/crop_left_glove.png new file mode 100644 index 0000000..3aa31bc Binary files /dev/null and b/images/crop_left_glove.png differ diff --git a/images/crop_right_boot.png b/images/crop_right_boot.png new file mode 100644 index 0000000..6addfe1 Binary files /dev/null and b/images/crop_right_boot.png differ diff --git a/images/crop_right_glove.png b/images/crop_right_glove.png new file mode 100644 index 0000000..33364f6 Binary files /dev/null and b/images/crop_right_glove.png differ diff --git a/images/crop_vest.png b/images/crop_vest.png new file mode 100644 index 0000000..bd61238 Binary files /dev/null and b/images/crop_vest.png differ diff --git a/images/gear/gear_3d_boot_left.png b/images/gear/gear_3d_boot_left.png new file mode 100644 index 0000000..bdc4a39 Binary files /dev/null and b/images/gear/gear_3d_boot_left.png differ diff --git a/images/gear/gear_3d_boot_right.png b/images/gear/gear_3d_boot_right.png new file mode 100644 index 0000000..6ae25a4 Binary files /dev/null and b/images/gear/gear_3d_boot_right.png differ diff --git a/images/gear/gear_3d_chem_apron.png b/images/gear/gear_3d_chem_apron.png new file mode 100644 index 0000000..9555a07 Binary files /dev/null and b/images/gear/gear_3d_chem_apron.png differ diff --git a/images/gear/gear_3d_chem_gloves.png b/images/gear/gear_3d_chem_gloves.png new file mode 100644 index 0000000..fe45c45 Binary files /dev/null and b/images/gear/gear_3d_chem_gloves.png differ diff --git a/images/gear/gear_3d_chem_goggles.png b/images/gear/gear_3d_chem_goggles.png new file mode 100644 index 0000000..fa73d7e Binary files /dev/null and b/images/gear/gear_3d_chem_goggles.png differ diff --git a/images/gear/gear_3d_chem_splash_goggles.png b/images/gear/gear_3d_chem_splash_goggles.png new file mode 100644 index 0000000..fa73d7e Binary files /dev/null and b/images/gear/gear_3d_chem_splash_goggles.png differ diff --git a/images/gear/gear_3d_cotton_coat.png b/images/gear/gear_3d_cotton_coat.png new file mode 100644 index 0000000..9392f5f Binary files /dev/null and b/images/gear/gear_3d_cotton_coat.png differ diff --git a/images/gear/gear_3d_dust_mask.png b/images/gear/gear_3d_dust_mask.png new file mode 100644 index 0000000..7a41e5f Binary files /dev/null and b/images/gear/gear_3d_dust_mask.png differ diff --git a/images/gear/gear_3d_earmuffs.png b/images/gear/gear_3d_earmuffs.png new file mode 100644 index 0000000..edad1ba Binary files /dev/null and b/images/gear/gear_3d_earmuffs.png differ diff --git a/images/gear/gear_3d_face_shield.png b/images/gear/gear_3d_face_shield.png new file mode 100644 index 0000000..4218d01 Binary files /dev/null and b/images/gear/gear_3d_face_shield.png differ diff --git a/images/gear/gear_3d_fire_resistant_apron.png b/images/gear/gear_3d_fire_resistant_apron.png new file mode 100644 index 0000000..c71dedb Binary files /dev/null and b/images/gear/gear_3d_fire_resistant_apron.png differ diff --git a/images/gear/gear_3d_glove_left.png b/images/gear/gear_3d_glove_left.png new file mode 100644 index 0000000..9082243 Binary files /dev/null and b/images/gear/gear_3d_glove_left.png differ diff --git a/images/gear/gear_3d_glove_right.png b/images/gear/gear_3d_glove_right.png new file mode 100644 index 0000000..e2abe99 Binary files /dev/null and b/images/gear/gear_3d_glove_right.png differ diff --git a/images/gear/gear_3d_helmet_dome_only.png b/images/gear/gear_3d_helmet_dome_only.png new file mode 100644 index 0000000..5f9c897 Binary files /dev/null and b/images/gear/gear_3d_helmet_dome_only.png differ diff --git a/images/gear/gear_3d_leather_gloves.png b/images/gear/gear_3d_leather_gloves.png new file mode 100644 index 0000000..30ed6b9 Binary files /dev/null and b/images/gear/gear_3d_leather_gloves.png differ diff --git a/images/gear/gear_3d_reflective_vest.png b/images/gear/gear_3d_reflective_vest.png new file mode 100644 index 0000000..6681dc9 Binary files /dev/null and b/images/gear/gear_3d_reflective_vest.png differ diff --git a/images/gear/gear_3d_safety_boots.png b/images/gear/gear_3d_safety_boots.png new file mode 100644 index 0000000..e297184 Binary files /dev/null and b/images/gear/gear_3d_safety_boots.png differ diff --git a/images/gear/gear_3d_safety_glasses.png b/images/gear/gear_3d_safety_glasses.png new file mode 100644 index 0000000..fa73d7e Binary files /dev/null and b/images/gear/gear_3d_safety_glasses.png differ diff --git a/images/gear/gear_3d_safety_helmet.png b/images/gear/gear_3d_safety_helmet.png new file mode 100644 index 0000000..f8f3baa Binary files /dev/null and b/images/gear/gear_3d_safety_helmet.png differ diff --git a/images/gear/gear_3d_work_gloves.png b/images/gear/gear_3d_work_gloves.png new file mode 100644 index 0000000..25d9cc8 Binary files /dev/null and b/images/gear/gear_3d_work_gloves.png differ diff --git a/images/gear_3d_boots.png b/images/gear_3d_boots.png new file mode 100644 index 0000000..96d92cc Binary files /dev/null and b/images/gear_3d_boots.png differ diff --git a/images/gear_3d_earmuffs.png b/images/gear_3d_earmuffs.png new file mode 100644 index 0000000..76a0020 Binary files /dev/null and b/images/gear_3d_earmuffs.png differ diff --git a/images/gear_3d_glasses.png b/images/gear_3d_glasses.png new file mode 100644 index 0000000..bfb798b Binary files /dev/null and b/images/gear_3d_glasses.png differ diff --git a/images/gear_3d_gloves.png b/images/gear_3d_gloves.png new file mode 100644 index 0000000..6974c5f Binary files /dev/null and b/images/gear_3d_gloves.png differ diff --git a/images/gear_3d_helmet_earmuffs.png b/images/gear_3d_helmet_earmuffs.png new file mode 100644 index 0000000..f1a6dd2 Binary files /dev/null and b/images/gear_3d_helmet_earmuffs.png differ diff --git a/images/gear_3d_vest.png b/images/gear_3d_vest.png new file mode 100644 index 0000000..3de37b8 Binary files /dev/null and b/images/gear_3d_vest.png differ diff --git a/images/home_page_bg.png b/images/home_page_bg.png new file mode 100644 index 0000000..b762dab Binary files /dev/null and b/images/home_page_bg.png differ diff --git a/images/icons/icon_fire_resistant_apron.png b/images/icons/icon_fire_resistant_apron.png new file mode 100644 index 0000000..5bdef4f Binary files /dev/null and b/images/icons/icon_fire_resistant_apron.png differ diff --git a/images/test_all_gear_fitted.png b/images/test_all_gear_fitted.png new file mode 100644 index 0000000..6da04e0 Binary files /dev/null and b/images/test_all_gear_fitted.png differ diff --git a/images/test_apron_fit.png b/images/test_apron_fit.png new file mode 100644 index 0000000..d426223 Binary files /dev/null and b/images/test_apron_fit.png differ diff --git a/images/test_boots.png b/images/test_boots.png new file mode 100644 index 0000000..96d92cc Binary files /dev/null and b/images/test_boots.png differ diff --git a/images/test_boots_fit.png b/images/test_boots_fit.png new file mode 100644 index 0000000..c43abc6 Binary files /dev/null and b/images/test_boots_fit.png differ diff --git a/images/test_button_smooth_crop.png b/images/test_button_smooth_crop.png new file mode 100644 index 0000000..f33dd8e Binary files /dev/null and b/images/test_button_smooth_crop.png differ diff --git a/images/test_button_smooth_crop2.png b/images/test_button_smooth_crop2.png new file mode 100644 index 0000000..b248961 Binary files /dev/null and b/images/test_button_smooth_crop2.png differ diff --git a/images/test_chem_apron_fit.png b/images/test_chem_apron_fit.png new file mode 100644 index 0000000..134a30b Binary files /dev/null and b/images/test_chem_apron_fit.png differ diff --git a/images/test_chem_full_fit.png b/images/test_chem_full_fit.png new file mode 100644 index 0000000..914db6c Binary files /dev/null and b/images/test_chem_full_fit.png differ diff --git a/images/test_chem_goggles_clean.png b/images/test_chem_goggles_clean.png new file mode 100644 index 0000000..825d2fd Binary files /dev/null and b/images/test_chem_goggles_clean.png differ diff --git a/images/test_earmuffs.png b/images/test_earmuffs.png new file mode 100644 index 0000000..b65eea2 Binary files /dev/null and b/images/test_earmuffs.png differ diff --git a/images/test_earmuffs_alone_fit.png b/images/test_earmuffs_alone_fit.png new file mode 100644 index 0000000..1cf2b0e Binary files /dev/null and b/images/test_earmuffs_alone_fit.png differ diff --git a/images/test_earmuffs_and_helmet_fit.png b/images/test_earmuffs_and_helmet_fit.png new file mode 100644 index 0000000..a3c69d4 Binary files /dev/null and b/images/test_earmuffs_and_helmet_fit.png differ diff --git a/images/test_face_shield_fit.png b/images/test_face_shield_fit.png new file mode 100644 index 0000000..fce9e68 Binary files /dev/null and b/images/test_face_shield_fit.png differ diff --git a/images/test_full_dressed_character.png b/images/test_full_dressed_character.png new file mode 100644 index 0000000..2f53783 Binary files /dev/null and b/images/test_full_dressed_character.png differ diff --git a/images/test_glasses.png b/images/test_glasses.png new file mode 100644 index 0000000..bfb798b Binary files /dev/null and b/images/test_glasses.png differ diff --git a/images/test_glasses_fit.png b/images/test_glasses_fit.png new file mode 100644 index 0000000..b2b5ae1 Binary files /dev/null and b/images/test_glasses_fit.png differ diff --git a/images/test_gloves.png b/images/test_gloves.png new file mode 100644 index 0000000..9f31fba Binary files /dev/null and b/images/test_gloves.png differ diff --git a/images/test_gloves_fit.png b/images/test_gloves_fit.png new file mode 100644 index 0000000..103a919 Binary files /dev/null and b/images/test_gloves_fit.png differ diff --git a/images/test_gloves_under_coat.png b/images/test_gloves_under_coat.png new file mode 100644 index 0000000..d1022eb Binary files /dev/null and b/images/test_gloves_under_coat.png differ diff --git a/images/test_helmet.png b/images/test_helmet.png new file mode 100644 index 0000000..2626a01 Binary files /dev/null and b/images/test_helmet.png differ diff --git a/images/test_helmet_fit.png b/images/test_helmet_fit.png new file mode 100644 index 0000000..638ac2a Binary files /dev/null and b/images/test_helmet_fit.png differ diff --git a/images/test_helmet_full.png b/images/test_helmet_full.png new file mode 100644 index 0000000..cce9030 Binary files /dev/null and b/images/test_helmet_full.png differ diff --git a/images/test_leather_gloves_fit.png b/images/test_leather_gloves_fit.png new file mode 100644 index 0000000..c9a5b48 Binary files /dev/null and b/images/test_leather_gloves_fit.png differ diff --git a/images/test_mask_fit.png b/images/test_mask_fit.png new file mode 100644 index 0000000..5364dd9 Binary files /dev/null and b/images/test_mask_fit.png differ diff --git a/images/test_new_boots_fit.png b/images/test_new_boots_fit.png new file mode 100644 index 0000000..8ec209a Binary files /dev/null and b/images/test_new_boots_fit.png differ diff --git a/images/test_new_glasses_fit2.png b/images/test_new_glasses_fit2.png new file mode 100644 index 0000000..825d2fd Binary files /dev/null and b/images/test_new_glasses_fit2.png differ diff --git a/images/test_new_gloves_fit.png b/images/test_new_gloves_fit.png new file mode 100644 index 0000000..54c0c2f Binary files /dev/null and b/images/test_new_gloves_fit.png differ diff --git a/images/test_new_shield_fit2.png b/images/test_new_shield_fit2.png new file mode 100644 index 0000000..f2b0987 Binary files /dev/null and b/images/test_new_shield_fit2.png differ diff --git a/images/test_vest.png b/images/test_vest.png new file mode 100644 index 0000000..75007e8 Binary files /dev/null and b/images/test_vest.png differ diff --git a/images/test_vest_fit.png b/images/test_vest_fit.png new file mode 100644 index 0000000..04967de Binary files /dev/null and b/images/test_vest_fit.png differ diff --git a/images/test_work_gloves_fit.png b/images/test_work_gloves_fit.png new file mode 100644 index 0000000..54c0c2f Binary files /dev/null and b/images/test_work_gloves_fit.png differ diff --git a/images/worker_base.png b/images/worker_base.png index 857c382..bc927a4 100644 Binary files a/images/worker_base.png and b/images/worker_base.png differ diff --git a/images/worker_realistic_base.png b/images/worker_realistic_base.png new file mode 100644 index 0000000..bc927a4 Binary files /dev/null and b/images/worker_realistic_base.png differ diff --git a/images/worker_with_new_gear_test.png b/images/worker_with_new_gear_test.png new file mode 100644 index 0000000..a8330a6 Binary files /dev/null and b/images/worker_with_new_gear_test.png differ diff --git a/index.html b/index.html index 168d42f..2e2e840 100644 --- a/index.html +++ b/index.html @@ -8,6 +8,10 @@ + + + + @@ -69,44 +73,11 @@
-
-
-
- - - - -
-
-
- -

PPE SAFETY CHALLENGE

-

β€œChoose Right. Work Safe.”

- -
-

Interactive Workplace Safety Mission

-

Select the correct Personal Protective Equipment (PPE) for critical industrial tasks. Learn how proper gear prevents severe workplace injuries before starting dangerous operations.

- -
- πŸ” SEE SITUATION - βž” - ⚠️ IDENTIFY HAZARD - βž” - 🦺 SELECT PPE - βž” - βš™οΈ START TASK -
-
- -
- -
- ⌨️ Mouse Click or Number Keys (1-9) to equip β€’ Spacebar to Start Task -
-
+
+ PPE Safety Challenge +
@@ -207,9 +178,16 @@ - +
- +
+ Angle Grinding Workshop +
+
+
diff --git a/js/audio.js b/js/audio.js index 1cba43f..86c47e6 100644 --- a/js/audio.js +++ b/js/audio.js @@ -77,10 +77,19 @@ class SoundEngine { // VOLUME & MUTE CONTROLS // ========================================== + get isMasterMuted() { + return this.isMuted; + } + + toggleMasterMute() { + this.setMasterMute(!this.isMuted); + return this.isMuted; + } + setMasterMute(muted) { - this.isMuted = muted; + this.isMuted = !!muted; if (this.masterGain && this.ctx) { - this.masterGain.gain.setTargetAtTime(muted ? 0 : 1.0, this.ctx.currentTime, 0.05); + this.masterGain.gain.setTargetAtTime(this.isMuted ? 0 : 1.0, this.ctx.currentTime, 0.05); } } @@ -173,7 +182,7 @@ class SoundEngine { ]; let step = 0; - this.musicInterval = setInterval(() => { + const playStep = () => { if (!this.isMusicPlaying || !this.ctx || this.isMuted) return; const current = progression[step % progression.length]; @@ -247,7 +256,27 @@ class SoundEngine { }); step++; - }, 1800); + }; + + // Play step 0 immediately with zero delay + playStep(); + this.musicInterval = setInterval(playStep, 1800); + } + + enableAudioOnFirstGesture() { + const unlockAndPlay = () => { + this.ensureContext(); + if (!this.isMusicPlaying) { + this.startBackgroundMusic(); + } + ['click', 'keydown', 'pointerdown', 'touchstart'].forEach(evt => { + window.removeEventListener(evt, unlockAndPlay); + }); + }; + + ['click', 'keydown', 'pointerdown', 'touchstart'].forEach(evt => { + window.addEventListener(evt, unlockAndPlay, { once: false, passive: true }); + }); } stopBackgroundMusic() { @@ -265,6 +294,7 @@ class SoundEngine { setAmbientScenario(scenarioId) { this.ensureContext(); this.stopAmbient(); + this.stopAllTaskSFX(); if (!this.ctx || !this.isAmbientEnabled) return; if (scenarioId === 'grinding') { @@ -609,19 +639,37 @@ class SoundEngine { stopGrinderSFX() { if (this.activeGrinderNode && this.ctx) { - const t = this.ctx.currentTime; + const node = this.activeGrinderNode; + this.activeGrinderNode = null; try { - this.activeGrinderNode.gain.gain.linearRampToValueAtTime(0.001, t + 0.1); - this.activeGrinderNode.noiseGain.gain.linearRampToValueAtTime(0.001, t + 0.1); + const t = this.ctx.currentTime; + if (node.gain && node.gain.gain) { + node.gain.gain.cancelScheduledValues(t); + node.gain.gain.setValueAtTime(node.gain.gain.value, t); + node.gain.gain.linearRampToValueAtTime(0.0001, t + 0.05); + } + if (node.noiseGain && node.noiseGain.gain) { + node.noiseGain.gain.cancelScheduledValues(t); + node.noiseGain.gain.setValueAtTime(node.noiseGain.gain.value, t); + node.noiseGain.gain.linearRampToValueAtTime(0.0001, t + 0.05); + } setTimeout(() => { try { - this.activeGrinderNode.osc.stop(); - this.activeGrinderNode.noise.stop(); + if (node.osc) { + node.osc.stop(); + node.osc.disconnect(); + } + if (node.noise) { + node.noise.stop(); + node.noise.disconnect(); + } } catch (e) {} - this.activeGrinderNode = null; - }, 120); + }, 60); } catch (e) { - this.activeGrinderNode = null; + try { + if (node.osc) { node.osc.stop(); node.osc.disconnect(); } + if (node.noise) { node.noise.stop(); node.noise.disconnect(); } + } catch (err) {} } } } @@ -662,21 +710,36 @@ class SoundEngine { stopLiquidTransferSFX() { if (this.activeLiquidNode && this.ctx) { - const t = this.ctx.currentTime; + const node = this.activeLiquidNode; + this.activeLiquidNode = null; try { - this.activeLiquidNode.gain.gain.linearRampToValueAtTime(0.001, t + 0.1); + const t = this.ctx.currentTime; + if (node.gain && node.gain.gain) { + node.gain.gain.cancelScheduledValues(t); + node.gain.gain.setValueAtTime(node.gain.gain.value, t); + node.gain.gain.linearRampToValueAtTime(0.0001, t + 0.05); + } setTimeout(() => { try { - this.activeLiquidNode.noise.stop(); + if (node.noise) { + node.noise.stop(); + node.noise.disconnect(); + } } catch (e) {} - this.activeLiquidNode = null; - }, 120); + }, 60); } catch (e) { - this.activeLiquidNode = null; + try { + if (node.noise) { node.noise.stop(); node.noise.disconnect(); } + } catch (err) {} } } } + stopAllTaskSFX() { + this.stopGrinderSFX(); + this.stopLiquidTransferSFX(); + } + playChemicalSplashSFX() { this.ensureContext(); if (!this.ctx || this.isMuted) return; diff --git a/js/avatar.js b/js/avatar.js index e74d188..f02a21a 100644 --- a/js/avatar.js +++ b/js/avatar.js @@ -1,7 +1,7 @@ /** - * PPE SAFETY CHALLENGE - DYNAMIC 2D WORKER AVATAR SYSTEM - * Uses the step-by-step transparent character images from ppe_character_steps_transparent_png! - * Dynamically switches character states as safety gear is equipped. + * PPE SAFETY CHALLENGE - REALISTIC 3D WORKER AVATAR SYSTEM + * Uses the exact 3D realistic character image provided by the user (images/worker_base.png), + * with precision anatomical PPE overlays for both Grinding and Chemical scenarios. */ class WorkerAvatar { @@ -18,7 +18,7 @@ class WorkerAvatar { equip(itemId) { this.equippedItems.add(itemId); - this.updateLayers(); + this.updateLayers(itemId); } unequip(itemId) { @@ -40,107 +40,165 @@ class WorkerAvatar { x: relX, y: relY, eyeX: relX, - eyeY: relY - 130, - handX: relX + 55, - handY: relY + 30 + eyeY: relY - 135, + handX: relX + 65, + handY: relY + 25 }; } - getStepImagePath() { - const hasHelmet = this.equippedItems.has('safety_helmet_grind') || this.equippedItems.has('safety_helmet_chem'); - const hasGoggles = this.equippedItems.has('chem_splash_goggles') || this.equippedItems.has('safety_goggles_grind'); - const hasFaceShield = this.equippedItems.has('face_shield_grind') || this.equippedItems.has('face_shield_chem'); - const hasHearing = this.equippedItems.has('hearing_protection_grind') || this.equippedItems.has('hearing_protection_chem'); - const hasLeatherGloves = this.equippedItems.has('leather_grinding_gloves'); - const hasWorkGloves = this.equippedItems.has('work_gloves_grind') || this.equippedItems.has('standard_work_gloves'); - const hasChemGloves = this.equippedItems.has('chem_resistant_gloves'); - const hasVest = this.equippedItems.has('reflective_vest_grind'); - const hasApron = this.equippedItems.has('chem_resistant_apron'); - const hasCottonCoat = this.equippedItems.has('cotton_coat_chem'); - const hasBoots = this.equippedItems.has('steel_toe_boots_grind') || this.equippedItems.has('chem_safety_shoes'); - const hasMask = this.equippedItems.has('dust_mask_grind'); - - // 10. Full PPE with Mask - if (hasMask) { - return 'images/steps/10_full_ppe_with_mask.png'; - } - - // 9. Full Grinding Set with Boots - if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing && (hasLeatherGloves || hasChemGloves) && (hasVest || hasApron || hasBoots)) { - return 'images/steps/09_helmet_goggles_hearing_leather_gloves_vest_boots.png'; - } - - // 8. With Vest / Body Wear - if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing && (hasLeatherGloves || hasChemGloves) && (hasVest || hasApron || hasCottonCoat)) { - return 'images/steps/08_helmet_goggles_hearing_leather_gloves_vest.png'; - } - - // 7. With Leather / Chem Gloves - if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing && (hasLeatherGloves || hasChemGloves)) { - return 'images/steps/07_helmet_goggles_hearing_leather_gloves.png'; - } - - // 6. With Work Gloves - if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing && hasWorkGloves) { - return 'images/steps/06_helmet_goggles_hearing_work_gloves.png'; - } - - // 5. With Earmuffs / Hearing Protection - if (hasHelmet && (hasGoggles || hasFaceShield) && hasHearing) { - return 'images/steps/05_helmet_goggles_hearing.png'; - } - - // 4. With Face Shield - if (hasFaceShield || (hasHelmet && hasFaceShield)) { - return 'images/steps/04_helmet_goggles_face_shield.png'; - } - - // 3. With Goggles & Helmet - if (hasHelmet && hasGoggles) { - return 'images/steps/03_helmet_goggles.png'; - } - - // 2. Helmet Only - if (hasHelmet) { - return 'images/steps/02_helmet.png'; - } - - // Individual items - if (hasFaceShield) return 'images/steps/04_helmet_goggles_face_shield.png'; - if (hasGoggles) return 'images/steps/03_helmet_goggles.png'; - if (hasHearing) return 'images/steps/05_helmet_goggles_hearing.png'; - if (hasLeatherGloves) return 'images/steps/07_helmet_goggles_hearing_leather_gloves.png'; - if (hasWorkGloves) return 'images/steps/06_helmet_goggles_hearing_work_gloves.png'; - - // 1. Base Worker (No PPE) - return 'images/steps/01_no_ppe.png'; - } - render() { if (!this.container) return; this.container.innerHTML = ` -
- -
- - - Worker Character -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + `; this.updateLayers(); } - updateLayers() { + updateLayers(newlyEquippedId = null) { if (!this.container) return; - const imgEl = document.getElementById('avatar-step-img'); - if (imgEl) { - const targetSrc = this.getStepImagePath(); - if (imgEl.getAttribute('src') !== targetSrc) { - imgEl.src = targetSrc; + const allGearIds = [ + 'safety_helmet_chem', 'safety_helmet_grind', + 'chem_splash_goggles', 'safety_goggles_grind', 'face_shield_chem', 'face_shield_grind', 'tinted_sunglasses', 'dust_mask_grind', + 'hearing_protection_chem', 'hearing_protection_grind', + 'chem_resistant_gloves', 'standard_work_gloves', 'work_gloves_grind', 'leather_grinding_gloves', 'disposable_latex_gloves', + 'chem_resistant_apron', 'cotton_coat_chem', 'flame_resistant_jacket', 'reflective_vest_grind', 'fire_resistant_apron_grind', 'fire_resistant_apron', + 'chem_safety_shoes', 'steel_toe_boots_grind', 'running_sneakers' + ]; + + allGearIds.forEach(gearId => { + const el = document.getElementById(`layer-gear-${gearId}`); + if (el) { + const isEquipped = this.equippedItems.has(gearId); + el.style.display = isEquipped ? 'block' : 'none'; + if (isEquipped && gearId === newlyEquippedId) { + el.classList.remove('gear-pop-glow'); + void el.offsetWidth; + el.classList.add('gear-pop-glow'); + } } - } + }); } } diff --git a/js/game.js b/js/game.js index 8f6187c..af70dae 100644 --- a/js/game.js +++ b/js/game.js @@ -94,6 +94,9 @@ class SafetyGame { this.bindEvents(); this.updateHUD(); + // Enable background music starting from home page on first gesture + window.soundEngine.enableAudioOnFirstGesture(); + // Listen for freeze frame event from particle collision window.addEventListener('hazard-freeze-triggered', (e) => { this.handleFreezeFrameAlert(e.detail.projectile); @@ -101,17 +104,14 @@ class SafetyGame { } bindEvents() { - // Start Game from Intro + // Start Game from Intro (Instantaneous Transition, Zero Delay) if (this.dom.btnStartGame) { this.dom.btnStartGame.addEventListener('click', () => { window.soundEngine.ensureContext(); window.soundEngine.playStartIntroSting(); + window.soundEngine.startBackgroundMusic(); + this.loadScenario(0); this.switchScreen('game'); - - setTimeout(() => { - window.soundEngine.startBackgroundMusic(); - this.loadScenario(0); - }, 800); }); } @@ -141,6 +141,7 @@ class SafetyGame { if (this.dom.btnNextScenario) { this.dom.btnNextScenario.addEventListener('click', () => { window.soundEngine.playButtonClick(); + window.soundEngine.stopAllTaskSFX(); this.dom.successOverlay.classList.remove('active'); this.particles.stopMode(); @@ -175,13 +176,13 @@ class SafetyGame { }); } - // Audio & Settings Header Controls + // Audio & Settings Header Controls (Toggle Mute / Unmute continuously) if (this.dom.btnSoundToggle) { this.dom.btnSoundToggle.addEventListener('click', () => { window.soundEngine.ensureContext(); - const isMuted = !window.soundEngine.isMasterMuted; - window.soundEngine.setMasterMute(isMuted); - this.dom.btnSoundToggle.querySelector('span').textContent = isMuted ? 'πŸ”‡' : 'πŸ”Š'; + const isMuted = window.soundEngine.toggleMasterMute(); + const iconSpan = this.dom.btnSoundToggle.querySelector('span') || this.dom.btnSoundToggle; + iconSpan.textContent = isMuted ? 'πŸ”‡' : 'πŸ”Š'; if (this.dom.toggleMasterMute) this.dom.toggleMasterMute.checked = !isMuted; }); } @@ -192,7 +193,8 @@ class SafetyGame { const currentVol = window.soundEngine.musicVolume; const newVol = currentVol > 0.01 ? 0 : 0.25; window.soundEngine.setMusicVolume(newVol); - this.dom.btnMusicToggle.querySelector('span').textContent = newVol > 0 ? '🎡' : 'πŸ”‡'; + const iconSpan = this.dom.btnMusicToggle.querySelector('span') || this.dom.btnMusicToggle; + iconSpan.textContent = newVol > 0 ? '🎡' : 'πŸ”‡'; if (this.dom.sliderMusicVol) this.dom.sliderMusicVol.value = Math.round(newVol * 100); if (this.dom.musicVolNum) this.dom.musicVolNum.textContent = `${Math.round(newVol * 100)}%`; }); @@ -247,16 +249,23 @@ class SafetyGame { }); } - // Keyboard Shortcuts (1-9 for Locker, Space for Start Task, Esc to close modals) + // Keyboard Shortcuts (1-9 and 0 for 10 Locker items, Space for Start Task, Esc to close modals) window.addEventListener('keydown', (e) => { const num = parseInt(e.key); - if (!isNaN(num) && num >= 1 && num <= 9) { + if (!isNaN(num)) { const cards = document.querySelectorAll('.ppe-item-card'); - if (cards[num - 1]) { - cards[num - 1].click(); + const cardIndex = num === 0 ? 9 : num - 1; + if (cards[cardIndex]) { + cards[cardIndex].click(); } } + if ((e.code === 'Space' || e.code === 'Enter') && this.dom.screenStart.classList.contains('active')) { + e.preventDefault(); + if (this.dom.btnStartGame) this.dom.btnStartGame.click(); + return; + } + if (e.code === 'Space' && this.dom.screenGame.classList.contains('active')) { if (!this.dom.consequenceOverlay.classList.contains('active') && !this.dom.successOverlay.classList.contains('active')) { e.preventDefault(); @@ -285,6 +294,7 @@ class SafetyGame { } loadScenario(index) { + window.soundEngine.stopAllTaskSFX(); this.currentScenarioIndex = index; this.currentScenario = SCENARIO_DATA[index]; this.scenarioFirstAttempt = true; @@ -305,6 +315,11 @@ class SafetyGame { if (this.dom.sceneDesc) this.dom.sceneDesc.textContent = this.currentScenario.environmentDesc; if (this.dom.lockerGuideText) this.dom.lockerGuideText.textContent = this.currentScenario.guideTip; + // Apply stage scene class for background framing + if (this.dom.sceneStage) { + this.dom.sceneStage.className = 'scene-environment-container ' + (this.currentScenario.sceneClass || ''); + } + // Render Scenario Background this.renderScenarioEnvironment(this.currentScenario.id); @@ -326,298 +341,16 @@ class SafetyGame { } renderScenarioEnvironment(scenarioId) { - if (!this.dom.sceneBackdrop) return; - - if (scenarioId === 'grinding') { - // SCENARIO 1: Vector Industrial Fabrication & Grinding Workshop (Matching Reference Image) - this.dom.sceneBackdrop.innerHTML = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WEAR - PPE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - `; - } else { - // SCENARIO 2: Vector Industrial Chemical Transfer Station - this.dom.sceneBackdrop.innerHTML = ` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - EYE - WASH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - πŸ§ͺ - - - - - `; + const bgGrind = document.getElementById('backdrop-grinding'); + const bgChem = document.getElementById('backdrop-chemical'); + if (bgGrind && bgChem) { + if (scenarioId === 'grinding') { + bgGrind.style.display = 'block'; + bgChem.style.display = 'none'; + } else { + bgGrind.style.display = 'none'; + bgChem.style.display = 'block'; + } } } @@ -888,6 +621,9 @@ class SafetyGame { // Play Success Chime & Star sound after short operational run setTimeout(() => { + window.soundEngine.stopAllTaskSFX(); + this.particles.stopMode(); + window.soundEngine.playSuccessChime(); window.soundEngine.playSafetyStarSound(); @@ -1054,6 +790,7 @@ class SafetyGame { } dismissConsequenceModal() { + window.soundEngine.stopAllTaskSFX(); if (this.dom.consequenceOverlay) this.dom.consequenceOverlay.classList.remove('active'); this.particles.clear(); this.particles.setFrozen(false); @@ -1071,6 +808,7 @@ class SafetyGame { // ========================================================================= showFinalSummary() { + window.soundEngine.stopAllTaskSFX(); this.switchScreen('summary'); window.soundEngine.playVictorySting(); this.particles.spawnConfetti(); diff --git a/js/scenarios.js b/js/scenarios.js index cfd7493..97e7248 100644 --- a/js/scenarios.js +++ b/js/scenarios.js @@ -28,6 +28,7 @@ const SCENARIO_DATA = [ "safety_helmet_grind", "face_shield_grind", "hearing_protection_grind", + "fire_resistant_apron_grind", "leather_grinding_gloves", "steel_toe_boots_grind" ], @@ -47,6 +48,13 @@ const SCENARIO_DATA = [ consequenceDesc: "Angle grinding routinely exceeds 100 dB. Operating without acoustic earmuffs causes irreversible inner-ear cochlear damage and permanent tinnitus.", missingLabel: "Hearing Protection (NRR 30 Earmuffs)" }, + { + slot: "body", + requiredItem: "fire_resistant_apron_grind", + consequenceTitle: "Thermal Spark Clothing Burn Hazard", + consequenceDesc: "Without a heavy split cowhide flame-resistant apron, 1000Β°C angle grinder spark streams ignite clothing and cause severe upper-body thermal burns.", + missingLabel: "Fire Resistant Leather Apron" + }, { slot: "hands", requiredItem: "leather_grinding_gloves", @@ -128,14 +136,13 @@ const SCENARIO_DATA = [ spec: "Heavy split cowhide heat/spark resistant" }, { - id: "reflective_vest_grind", - name: "Reflective Vest", + id: "fire_resistant_apron_grind", + name: "Leather Apron", slot: "body", iconType: "leather_jacket", - iconImg: "grinding_ppe_icons_png/07_reflective_vest.png", - isCorrect: false, - rejectReason: "Synthetic high-visibility vests melt or ignite from dense grinder spark streams.", - spec: "Synthetic mesh hi-vis vest" + iconImg: "grinding_ppe_icons_png/07_fire_resistant_apron.png", + isCorrect: true, + spec: "Heavy split cowhide heat/spark & flame resistant apron" }, { id: "steel_toe_boots_grind", @@ -149,12 +156,22 @@ const SCENARIO_DATA = [ { id: "dust_mask_grind", name: "Dust Mask", - slot: "body", - iconType: "cotton_coat", + slot: "mouth", + iconType: "respirator", iconImg: "grinding_ppe_icons_png/09_dust_mask.png", isCorrect: false, - rejectReason: "A simple dust mask does not protect the face or body from high-speed thermal sparks.", - spec: "N95 particulate filter mask" + rejectReason: "A basic nuisance dust mask provides no impact, eye, or face protection against high-speed 1000Β°C abrasive sparks and disc shattering debris.", + spec: "Particulate nuisance dust mask (Non-impact)" + }, + { + id: "reflective_vest_grind", + name: "Reflective Vest", + slot: "body", + iconType: "leather_jacket", + iconImg: "grinding_ppe_icons_png/09_reflective_vest.png", + isCorrect: false, + rejectReason: "Synthetic high-visibility vests melt and catch fire instantly from dense 1000Β°C angle grinder spark streams. Heavy leather apron is required.", + spec: "Synthetic mesh hi-vis vest (Non-FR)" } ], diff --git a/ppe_icons_png/02_chemical_splash_goggles.png b/ppe_icons_png/02_chemical_splash_goggles.png index bb86609..b122532 100644 Binary files a/ppe_icons_png/02_chemical_splash_goggles.png and b/ppe_icons_png/02_chemical_splash_goggles.png differ diff --git a/ppe_icons_png/05_chemical_resistant_gloves.png b/ppe_icons_png/05_chemical_resistant_gloves.png index f14d4ec..0797fa0 100644 Binary files a/ppe_icons_png/05_chemical_resistant_gloves.png and b/ppe_icons_png/05_chemical_resistant_gloves.png differ diff --git a/ppe_icons_png/07_chemical_resistant_apron.png b/ppe_icons_png/07_chemical_resistant_apron.png index 77feed4..6718315 100644 Binary files a/ppe_icons_png/07_chemical_resistant_apron.png and b/ppe_icons_png/07_chemical_resistant_apron.png differ diff --git a/ppe_icons_png/09_cotton_coat.png b/ppe_icons_png/09_cotton_coat.png index 5cc2f88..faf3091 100644 Binary files a/ppe_icons_png/09_cotton_coat.png and b/ppe_icons_png/09_cotton_coat.png differ