Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 205 additions & 4 deletions static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ h1, h2 {
}

label {
display: block;
display: flex;
align-items: center;
margin-bottom: 5px;
font-weight: bold;
color: var(--text-tertiary);
Expand Down Expand Up @@ -164,7 +165,6 @@ button.loading:hover {
padding: 20px;
background: var(--section-bg);
border-radius: 10px;
border-left: 5px solid #667eea;
transition: background 0.3s ease;
}

Expand Down Expand Up @@ -761,7 +761,7 @@ input[readonly] {
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.95);
border: 3px solid #764ba2;
border: 1px solid #764ba2;
border-radius: 50%;
width: 50px;
height: 50px;
Expand All @@ -779,7 +779,7 @@ input[readonly] {

.dark-mode #darkModeToggle {
background: rgba(39, 39, 42, 0.95);
border: 3px solid #ffffff;
border: 1px solid #ffffff;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(255, 255, 255, 0.2);
}

Expand Down Expand Up @@ -848,3 +848,204 @@ input[readonly] {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}

/* Modal Styles */
.modal-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
justify-content: center;
align-items: center;
backdrop-filter: blur(5px);
animation: fadeIn 0.2s ease;
}

.modal-overlay.active {
display: flex;
}

.modal-content {
background: var(--container-bg);
border-radius: 15px;
padding: 30px;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
position: relative;
animation: slideIn 0.3s ease;
}

@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes slideIn {
from { transform: translateY(-30px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}

.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}

.modal-header h3 {
margin: 0;
color: var(--text-secondary);
}

.modal-close {
background: transparent;
border: 2px solid var(--text-tertiary);
font-size: 28px;
cursor: pointer;
color: var(--text-tertiary);
padding: 0;
width: 44px;
height: 44px;
Comment on lines +910 to +915
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The modal close button has a fixed size of 44x44 pixels with a 28px font size. Similar to the help icon button, these fixed pixel dimensions don't scale with user font-size preferences. Consider using relative units like 'em' or 'rem' to better support accessibility and user text-size preferences.

Suggested change
font-size: 28px;
cursor: pointer;
color: var(--text-tertiary);
padding: 0;
width: 44px;
height: 44px;
font-size: 1.75rem;
cursor: pointer;
color: var(--text-tertiary);
padding: 0;
width: 2.75rem;
height: 2.75rem;

Copilot uses AI. Check for mistakes.
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.modal-close:hover {
background: rgba(102, 126, 234, 0.1);
color: var(--text-tertiary);
border-color: #667eea;
}

.modal-body {
color: var(--text-color);
}

.modal-body p {
margin: 15px 0;
}

.modal-body ul {
margin: 10px 0;
padding-left: 20px;
}

.modal-body li {
margin: 8px 0;
}

.help-icon-btn {
background: transparent;
border: 2px solid var(--border-color);
border-radius: 50%;
width: 44px;
height: 44px;
min-width: 44px;
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: var(--text-secondary);
font-size: 24px; /* Ensures the icon remains visually 24px */
font-weight: bold;
font-family: Arial, sans-serif;
margin-left: 8px;
padding: 0;
line-height: 1;
Comment on lines +946 to +964
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The help icon button has a fixed size of 44x44 pixels with a 24px font size. However, the font-size comment states it "ensures the icon remains visually 24px", but this doesn't scale with user font-size preferences (e.g., for users who need larger text). Consider using relative units like 'em' or 'rem' for the button dimensions and font-size to better support accessibility and user preferences.

Copilot uses AI. Check for mistakes.
transition: all 0.2s ease;
flex-shrink: 0;
}

.help-icon-btn:hover {
background: #667eea;
border-color: #667eea;
color: white;
transform: scale(1.1);
}

/* Toggleable Section Styles */
.toggleable-section {
position: relative;
padding: 0;
}

.section-header {
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
padding: 15px;
margin: 0;
border-radius: 8px;
transition: all 0.2s ease;
width: 100%;
border: 1px solid var(--border-color);
background: var(--section-bg);
text-align: left;
box-shadow: none;
}

.section-header:hover {
background: rgba(102, 126, 234, 0.05);
border-color: #667eea;
box-shadow: none;
}

.dark-mode .section-header:hover {
background: rgba(102, 126, 234, 0.1);
border-color: #667eea;
box-shadow: none;
}

Comment on lines +1001 to +1009
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'box-shadow: none' property is set and then explicitly set again in the hover state. This is redundant since the base state already has no box-shadow. Consider removing the explicit 'box-shadow: none' from lines 995, 1001, and 1007 to reduce redundancy.

Suggested change
box-shadow: none;
}
.dark-mode .section-header:hover {
background: rgba(102, 126, 234, 0.1);
border-color: #667eea;
box-shadow: none;
}
}
.dark-mode .section-header:hover {
background: rgba(102, 126, 234, 0.1);
border-color: #667eea;
}

Copilot uses AI. Check for mistakes.
.section-title {
margin: 0;
flex: 1;
pointer-events: none;
font-size: 1.5em;
font-weight: bold;
color: var(--text-secondary);
text-align: center;
}

.toggle-btn {
background: transparent;
border: none;
font-size: 16px;
cursor: pointer;
padding: 0;
margin-right: 10px;
color: var(--text-secondary);
transition: color 0.2s ease;
line-height: 1;
flex-shrink: 0;
width: 16px;
height: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
font-family: monospace;
}

.section-content {
max-height: none;
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The transition uses 'max-height' animation which requires knowing the content height. Setting 'max-height: none' for the expanded state and then transitioning to 'max-height: 0' won't animate smoothly. This approach typically needs a fixed max-height value (e.g., '5000px') for the expanded state to allow the transition to work properly. Consider using a specific large value or implementing a JavaScript-based height calculation for smoother animations.

Suggested change
max-height: none;
max-height: 1000px;

Copilot uses AI. Check for mistakes.
overflow: hidden;
transition: max-height 0.5s ease, opacity 0.3s ease, padding 0.3s ease;
opacity: 1;
padding: 20px;
}

.section-content.collapsed {
max-height: 0;
opacity: 0;
padding: 0;
}
72 changes: 72 additions & 0 deletions static/js/modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Modal functionality for help dialogs
*/

function openModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('active');
document.body.style.overflow = 'hidden'; // Prevent background scrolling
}
}

function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.remove('active');
document.body.style.overflow = ''; // Restore scrolling
}
}

// Initialize modal listeners when DOM is ready
function initializeModals() {
// Open location help modal when button is clicked
const locationHelpBtn = document.getElementById('locationHelpBtn');
if (locationHelpBtn) {
locationHelpBtn.addEventListener('click', function() {
openModal('locationHelpModal');
});
}

// Close modal when clicking on overlay (outside modal content)
const overlays = document.querySelectorAll('.modal-overlay');
overlays.forEach(overlay => {
overlay.addEventListener('click', function(e) {
if (e.target === this) {
closeModal(this.id);
}
});
});

// Close modal when clicking close button
const closeButtons = document.querySelectorAll('.modal-close');
closeButtons.forEach(button => {
button.addEventListener('click', function() {
const modal = this.closest('.modal-overlay');
if (modal) {
closeModal(modal.id);
}
});
});

// Close modal on Escape key (remove old listener first to prevent duplicates)
document.removeEventListener('keydown', handleEscapeKey);
document.addEventListener('keydown', handleEscapeKey);
Comment on lines +52 to +54
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pattern of removing and then immediately re-adding the event listener for Escape key handling on every modal initialization could lead to issues. If initializeModals() is called multiple times (e.g., in a dynamic page context), this pattern may not prevent duplicate listeners as intended since the listener is added back immediately. Consider using a flag to track whether the listener has been added, or better yet, use the 'once' option or ensure initializeModals() is only called once.

Copilot uses AI. Check for mistakes.
}

// Named function for Escape key handling to allow proper cleanup
function handleEscapeKey(e) {
if (e.key === 'Escape') {
const activeModal = document.querySelector('.modal-overlay.active');
if (activeModal) {
closeModal(activeModal.id);
}
}
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeModals);
} else {
initializeModals();
}
79 changes: 79 additions & 0 deletions static/js/section-toggle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Toggle functionality for collapsible sections
*/

/**
* Update the UI state of a section (expanded or collapsed)
* @param {HTMLElement} content - The content element to toggle
* @param {HTMLElement} toggleIcon - The icon element to update
* @param {HTMLElement} header - The header element to update
* @param {boolean} expanded - Whether the section should be expanded
*/
function updateSectionState(content, toggleIcon, header, expanded) {
if (expanded) {
content.classList.remove('collapsed');
toggleIcon.classList.remove('collapsed');
toggleIcon.textContent = '▼';
header.setAttribute('aria-expanded', 'true');
} else {
content.classList.add('collapsed');
toggleIcon.classList.add('collapsed');
toggleIcon.textContent = '▶';
header.setAttribute('aria-expanded', 'false');
}
}

function initializeToggleSections() {
const headers = document.querySelectorAll('.section-header');

headers.forEach(header => {
header.addEventListener('click', function(e) {
e.preventDefault();

const sectionId = this.getAttribute('data-section');
const content = document.getElementById(sectionId);
const toggleIcon = this.querySelector('.toggle-btn');

// Check if required elements exist before proceeding
if (!content || !toggleIcon) {
console.warn(`Missing elements for section: ${sectionId}`);
return;
}

const isCollapsed = content.classList.contains('collapsed');
const shouldExpand = isCollapsed;

// Update UI state
updateSectionState(content, toggleIcon, this, shouldExpand);

// Save state to localStorage
localStorage.setItem(`section-${sectionId}`, shouldExpand ? 'expanded' : 'collapsed');
});
});

// Restore saved states from localStorage
restoreSectionStates();
}

function restoreSectionStates() {
const headers = document.querySelectorAll('.section-header');

headers.forEach(header => {
const sectionId = header.getAttribute('data-section');
const savedState = localStorage.getItem(`section-${sectionId}`);
const content = document.getElementById(sectionId);
const toggleIcon = header.querySelector('.toggle-btn');

if (savedState === 'expanded' && content && toggleIcon) {
updateSectionState(content, toggleIcon, header, true);
}
// Default is already collapsed, no need to do anything
});
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeToggleSections);
} else {
initializeToggleSections();
}
Loading