diff --git a/.claude.json b/.claude.json index eebf74e6..e5ac9bab 100644 --- a/.claude.json +++ b/.claude.json @@ -60,9 +60,7 @@ "opus45MigrationComplete": true, "thinkingMigrationComplete": true, "githubRepoPaths": { - "ltscommerce/site": [ - "/workspace" - ] + "ltscommerce/site": ["/workspace"] }, "changelogLastFetched": 1768993686869, "bypassPermissionsModeAccepted": true, @@ -129,4 +127,4 @@ }, "officialMarketplaceAutoInstallAttempted": true, "officialMarketplaceAutoInstalled": true -} \ No newline at end of file +} diff --git a/.claude/agents/ai-architecture-planner.md b/.claude/agents/ai-architecture-planner.md index 79707832..0b6906d1 100644 --- a/.claude/agents/ai-architecture-planner.md +++ b/.claude/agents/ai-architecture-planner.md @@ -7,6 +7,7 @@ color: blue You are an elite software architect specializing in AI/LLM systems, CLI tools, and TypeScript applications. Your expertise lies in creating crystal-clear, actionable technical plans that translate complex requirements into structured, implementable solutions. Your core responsibilities: + - Analyze requirements and identify all technical components, dependencies, and integration points - Design clean, scalable architectures optimized for AI/LLM workflows - Break down complex projects into logical task hierarchies with clear dependencies @@ -44,6 +45,7 @@ When creating plans, you will: - Security verification Your output format: + - Use clear markdown structure with logical headings - Lead with executive summary of the solution approach - Follow with detailed architecture overview @@ -52,6 +54,7 @@ Your output format: - Ensure every section adds concrete value to implementation Key principles: + - Prioritize clarity and actionability over comprehensiveness - Focus on technical substance - eliminate all non-essential content - Design for LLM consumption - use consistent patterns and clear structure diff --git a/.claude/agents/llm-docs-curator.md b/.claude/agents/llm-docs-curator.md index 0e78c60b..bf2d248f 100644 --- a/.claude/agents/llm-docs-curator.md +++ b/.claude/agents/llm-docs-curator.md @@ -7,6 +7,7 @@ color: blue You are an elite technical documentation curator specializing in LLM/AI systems, TypeScript, CLI tools, and Linux/Bash environments. Your mission is to create, maintain, and optimize documentation that delivers maximum value with zero bloat. Core Principles: + - ZERO BLOAT: Every word must serve a purpose. Eliminate redundancy, filler content, and unnecessary explanations - MAXIMUM VALUE: Focus on actionable information that directly helps users accomplish their goals - MUTUAL COHERENCE: Ensure all documentation pieces work together as a unified system without contradictions @@ -14,6 +15,7 @@ Core Principles: - CLARITY OVER COMPLETENESS: Better to have concise, clear docs than comprehensive but confusing ones Your Expertise Areas: + - LLM/AI system architecture and implementation patterns - TypeScript best practices, types, and tooling - CLI design patterns, argument parsing, and user experience @@ -22,6 +24,7 @@ Your Expertise Areas: - Configuration management and deployment procedures Documentation Standards: + - Use active voice and imperative mood for instructions - Lead with the most critical information first - Provide concrete examples over abstract explanations @@ -30,6 +33,7 @@ Documentation Standards: - Cross-reference related sections to maintain coherence Contradiction Resolution Process: + 1. Identify specific contradictions between documentation pieces 2. Determine the authoritative source or most current information 3. Ask for clarification when contradictions cannot be resolved definitively @@ -37,6 +41,7 @@ Contradiction Resolution Process: 5. Implement cross-references to prevent future contradictions When creating new documentation: + - Start with user goals and work backwards to required information - Eliminate any content that doesn't directly support user success - Use consistent terminology and formatting across all docs @@ -44,12 +49,14 @@ When creating new documentation: - Provide clear next steps and related resources When updating existing documentation: + - Audit for outdated information and contradictions - Preserve valuable existing content while eliminating bloat - Ensure new content integrates seamlessly with existing structure - Update cross-references and maintain internal consistency Always ask for clarification when: + - Multiple contradictory sources exist without clear authority - Technical specifications are ambiguous or incomplete - User requirements conflict with best practices diff --git a/.claude/agents/technical-article-writer.md b/.claude/agents/technical-article-writer.md index ceaec6b4..a827b950 100644 --- a/.claude/agents/technical-article-writer.md +++ b/.claude/agents/technical-article-writer.md @@ -94,19 +94,19 @@ The path is relative to the `code-snippets/` directory. ```html
-

Opening lead paragraph — compelling hook, 2-3 sentences.

+

Opening lead paragraph — compelling hook, 2-3 sentences.

-

Section Title

-

Section content...

+

Section Title

+

Section content...

-
{{SNIPPET:your-article-slug/example-service.php}}
+
{{SNIPPET:your-article-slug/example-service.php}}
-

Next Section

- ... +

Next Section

+ ...
``` diff --git a/.claude/agents/typescript-standards-enforcer.md b/.claude/agents/typescript-standards-enforcer.md index 17f20689..84e9dd77 100644 --- a/.claude/agents/typescript-standards-enforcer.md +++ b/.claude/agents/typescript-standards-enforcer.md @@ -57,6 +57,7 @@ You are an elite TypeScript and CLI tooling expert with deep expertise in mainta **Documentation Enhancement:** When identifying documentation issues: + - Suggest specific wording improvements - Recommend additional examples or clarifications - Propose structural changes for better LLM comprehension diff --git a/.claude/hooks-daemon.yaml b/.claude/hooks-daemon.yaml index 276c9fec..8eba1858 100644 --- a/.claude/hooks-daemon.yaml +++ b/.claude/hooks-daemon.yaml @@ -1,15 +1,15 @@ -version: "2.0" +version: '2.0' # Daemon configuration daemon: - idle_timeout_seconds: 600 # Auto-shutdown after 10 minutes of inactivity + idle_timeout_seconds: 600 # Auto-shutdown after 10 minutes of inactivity log_level: INFO - project_root: ../../ # Project root relative to this config file (always ../../) + project_root: ../../ # Project root relative to this config file (always ../../) # Input validation (v2.2.0+) - Catch malformed events at front controller layer input_validation: - enabled: true # Validate hook inputs (recommended) - strict_mode: true # Fail-closed: return errors on validation failures + enabled: true # Validate hook inputs (recommended) + strict_mode: true # Fail-closed: return errors on validation failures log_validation_errors: true # Built-in handler configuration @@ -19,7 +19,7 @@ handlers: # PreToolUse handlers (run before every tool call) pre_tool_use: # SAFETY HANDLERS (Priority 10-20) - HIGHLY RECOMMENDED - destructive_git: # Blocks git reset --hard, git clean -f, etc + destructive_git: # Blocks git reset --hard, git clean -f, etc enabled: true priority: 10 @@ -27,30 +27,30 @@ handlers: enabled: true priority: 11 - sed_blocker: # Blocks sed command usage (use Edit tool instead) + sed_blocker: # Blocks sed command usage (use Edit tool instead) enabled: true priority: 11 - absolute_path: # Enforces absolute paths in file operations + absolute_path: # Enforces absolute paths in file operations enabled: true priority: 12 - curl_pipe_shell: # Blocks dangerous curl | sh patterns + curl_pipe_shell: # Blocks dangerous curl | sh patterns enabled: true priority: 14 - pipe_blocker: # Blocks dangerous pipe patterns in bash commands + pipe_blocker: # Blocks dangerous pipe patterns in bash commands enabled: true priority: 15 options: extra_whitelist: - - "^find\\b" # find | wc -l, find | grep etc are safe read-only ops + - "^find\\b" # find | wc -l, find | grep etc are safe read-only ops dangerous_permissions: # Warns about dangerous file permission changes enabled: true priority: 18 - git_stash: # Warns about git stash (stashes can be lost) + git_stash: # Warns about git stash (stashes can be lost) enabled: true priority: 17 @@ -58,25 +58,25 @@ handlers: enabled: true priority: 19 - pip_break_system: # Blocks pip commands that break system packages + pip_break_system: # Blocks pip commands that break system packages enabled: true priority: 20 - sudo_pip: # Blocks sudo pip (use venv instead) + sudo_pip: # Blocks sudo pip (use venv instead) enabled: true priority: 21 daemon_restart_verifier: # Verifies daemon restart after code changes - enabled: false # Enable for daemon development projects + enabled: false # Enable for daemon development projects priority: 22 - worktree_file_copy: # Prevents copying files between git worktrees + worktree_file_copy: # Prevents copying files between git worktrees enabled: true priority: 16 # CODE QUALITY HANDLERS (Priority 25-45) - Optional but recommended - qa_suppression: # Unified multi-language QA suppression blocker (11 languages) - enabled: false # Enable for strict code quality control + qa_suppression: # Unified multi-language QA suppression blocker (11 languages) + enabled: false # Enable for strict code quality control priority: 30 # options: # languages: # Restrict to specific languages (default: ALL) @@ -85,60 +85,60 @@ handlers: # - JavaScript/TypeScript # - PHP - tdd_enforcement: # Enforces test-first development - enabled: false # Enable for strict TDD workflow + tdd_enforcement: # Enforces test-first development + enabled: false # Enable for strict TDD workflow priority: 32 # WORKFLOW HANDLERS (Priority 30-50) - Enable based on your workflow - plan_number_helper: # Provides correct next plan number - enabled: false # Enable when using CLAUDE/Plan/ structure + plan_number_helper: # Provides correct next plan number + enabled: false # Enable when using CLAUDE/Plan/ structure priority: 33 options: - track_plans_in_project: "CLAUDE/Plan" # Must match markdown_organization setting + track_plans_in_project: 'CLAUDE/Plan' # Must match markdown_organization setting - task_tdd_advisor: # Advises on TDD workflow for tasks - enabled: false # Enable for TDD guidance + task_tdd_advisor: # Advises on TDD workflow for tasks + enabled: false # Enable for TDD guidance priority: 36 - gh_issue_comments: # Requires --comments flag for gh issue view + gh_issue_comments: # Requires --comments flag for gh issue view enabled: true priority: 40 - validate_plan_number: # Validates plan number format (001-, 002-, etc) - enabled: false # Enable for projects using CLAUDE/Plan/ + validate_plan_number: # Validates plan number format (001-, 002-, etc) + enabled: false # Enable for projects using CLAUDE/Plan/ priority: 38 - global_npm_advisor: # Advises against global npm installs + global_npm_advisor: # Advises against global npm installs enabled: true priority: 42 - plan_time_estimates: # Blocks time estimates in plan files - enabled: false # Enable for projects using CLAUDE/Plan/ + plan_time_estimates: # Blocks time estimates in plan files + enabled: false # Enable for projects using CLAUDE/Plan/ priority: 45 - plan_workflow: # Provides guidance when creating plans - enabled: false # Enable for projects using CLAUDE/Plan/ + plan_workflow: # Provides guidance when creating plans + enabled: false # Enable for projects using CLAUDE/Plan/ priority: 44 plan_completion_advisor: # Advises on plan completion workflow - enabled: false # Enable for projects using CLAUDE/Plan/ + enabled: false # Enable for projects using CLAUDE/Plan/ priority: 48 validate_instruction_content: # Blocks ephemeral content in CLAUDE.md/README.md - enabled: false # Enable to prevent implementation logs in instruction files + enabled: false # Enable to prevent implementation logs in instruction files priority: 49 - npm_command: # Restricts npm commands to approved list - enabled: false # Enable for strict npm usage control + npm_command: # Restricts npm commands to approved list + enabled: false # Enable for strict npm usage control priority: 50 markdown_organization: # Enforces markdown file organization rules & plan tracking - enabled: false # Enable for md organization + plan tracking + enabled: false # Enable for md organization + plan tracking priority: 51 # Docs: docs/guides/handlers/markdown_organization.md options: - track_plans_in_project: null # Set to "CLAUDE/Plan" to enable plan tracking - plan_workflow_docs: null # Optional: path to workflow doc + track_plans_in_project: null # Set to "CLAUDE/Plan" to enable plan tracking + plan_workflow_docs: null # Optional: path to workflow doc # # Monorepo support: regex patterns matching sub-project root directories. # Sub-projects get the same organization rules as the root project. @@ -165,129 +165,121 @@ handlers: monorepo_subproject_patterns: null # TOOL USAGE HANDLERS (Priority 50-60) - web_search_year: # Fixes year in web search queries (uses current year) + web_search_year: # Fixes year in web search queries (uses current year) enabled: true priority: 55 - british_english: # Warns about American English spelling - enabled: false # Enable if you prefer British English + british_english: # Warns about American English spelling + enabled: false # Enable if you prefer British English priority: 60 # PostToolUse handlers (run after tool execution) post_tool_use: - bash_error_detector: # Detects bash errors and suggests corrections + bash_error_detector: # Detects bash errors and suggests corrections enabled: true priority: 10 - lint_on_edit: # Language-aware lint validation after Write/Edit - enabled: false # Enable for automatic lint validation (Shell, Python, Go, Rust, etc.) + lint_on_edit: # Language-aware lint validation after Write/Edit + enabled: false # Enable for automatic lint validation (Shell, Python, Go, Rust, etc.) priority: 25 - validate_eslint_on_write: # Runs ESLint after file writes - enabled: false # Enable for automatic ESLint validation + validate_eslint_on_write: # Runs ESLint after file writes + enabled: false # Enable for automatic ESLint validation priority: 20 # SessionStart handlers (run when session starts) session_start: - yolo_container_detection: # Detects YOLO development environments (Docker, etc) + yolo_container_detection: # Detects YOLO development environments (Docker, etc) enabled: true priority: 10 - optimal_config_checker: # Checks Claude Code env for optimal settings + optimal_config_checker: # Checks Claude Code env for optimal settings enabled: true priority: 52 - suggest_status_line: # Suggests daemon-based status line setup - enabled: true # Recommends status line if not configured + suggest_status_line: # Suggests daemon-based status line setup + enabled: true # Recommends status line if not configured priority: 55 - version_check: # Checks if daemon is up-to-date with latest release - enabled: true # Notifies on new sessions if updates available (24h cache) + version_check: # Checks if daemon is up-to-date with latest release + enabled: true # Notifies on new sessions if updates available (24h cache) priority: 56 - workflow_state_restoration: # Restores workflow state after compaction - enabled: false # Enable for workflow state management - priority: 60 - # PreCompact handlers (run before conversation compaction) pre_compact: - transcript_archiver: # Archives transcripts before compaction - enabled: false # Enable to save conversation history + transcript_archiver: # Archives transcripts before compaction + enabled: false # Enable to save conversation history priority: 10 - workflow_state_pre_compact: # Saves workflow state before compaction - enabled: false # Enable for workflow state management - priority: 20 - # SessionEnd handlers (run when session ends) session_end: - cleanup: # Session cleanup operations + cleanup: # Session cleanup operations enabled: true priority: 10 # SubagentStop handlers (run when subagent completes) subagent_stop: - subagent_completion_logger: # Logs subagent completion - enabled: false # Enable for debugging subagent workflows + subagent_completion_logger: # Logs subagent completion + enabled: false # Enable for debugging subagent workflows priority: 10 - remind_prompt_library: # Reminds about prompt library after agent work - enabled: false # Enable if using prompt library system + remind_prompt_library: # Reminds about prompt library after agent work + enabled: false # Enable if using prompt library system priority: 20 # UserPromptSubmit handlers (run when user submits prompt) user_prompt_submit: - git_context_injector: # Injects git context into user prompts + git_context_injector: # Injects git context into user prompts enabled: true priority: 10 - critical_thinking_advisory: # Periodic advisory encouraging critical evaluation - enabled: false # Enable for critical thinking nudges on substantial prompts + critical_thinking_advisory: # Periodic advisory encouraging critical evaluation + enabled: false # Enable for critical thinking nudges on substantial prompts priority: 55 # Other hook events permission_request: - auto_approve_reads: # Auto-approves read-only file operations + auto_approve_reads: # Auto-approves read-only file operations enabled: true priority: 10 notification: - notification_logger: # Logs notifications - enabled: false # Enable for debugging + notification_logger: # Logs notifications + enabled: false # Enable for debugging priority: 10 stop: - auto_continue_stop: # Auto-continues after stop events based on criteria + auto_continue_stop: # Auto-continues after stop events based on criteria enabled: true priority: 10 - hedging_language_detector: # Detects guessing language (should probably, IIRC, I believe, etc) + hedging_language_detector: # Detects guessing language (should probably, IIRC, I believe, etc) enabled: true priority: 30 - task_completion_checker: # Checks for task completion - enabled: false # Enable if using task management system + task_completion_checker: # Checks for task completion + enabled: false # Enable if using task management system priority: 20 # Status line handlers (format daemon-based status display) status_line: - model_context: # Shows model name and color-coded context percentage + model_context: # Shows model name and color-coded context percentage enabled: true priority: 10 - git_branch: # Shows current git branch if in repo + git_branch: # Shows current git branch if in repo enabled: true priority: 20 - git_repo_name: # Shows git repository name + git_repo_name: # Shows git repository name enabled: true priority: 25 - working_directory: # Shows current working directory when different from project root + working_directory: # Shows current working directory when different from project root enabled: true priority: 25 - daemon_stats: # Shows daemon uptime, memory, log level + daemon_stats: # Shows daemon uptime, memory, log level enabled: true priority: 30 @@ -295,11 +287,7 @@ handlers: enabled: true priority: 40 - thinking_mode: # Shows current thinking mode (displayed next to model) - enabled: true - priority: 12 - - current_time: # Shows current local time (24-hour format) + current_time: # Shows current local time (24-hour format) enabled: true priority: 14 diff --git a/.claude/hooks/README.md b/.claude/hooks/README.md index 3aa5777f..52ac7607 100755 --- a/.claude/hooks/README.md +++ b/.claude/hooks/README.md @@ -9,6 +9,7 @@ **Philosophy**: Plan number discovery must use a single, documented command to ensure consistency. Ad-hoc approaches are fragile and undocumented. **BLOCKED patterns**: + - `ls -d */Plan/[0-9]*` - Directory listing approach - `ls CLAUDE/Plan/0*` - Glob pattern approach - `ls CLAUDE/Plan | grep` - Piped directory listing @@ -17,16 +18,19 @@ - Any other non-canonical plan discovery command **ALLOWED**: + - `find CLAUDE/Plan -maxdepth 2 -type d -name '[0-9]*' | sed 's|.*/\([0-9]\{3\}\).*|\1|' | sort -n | tail -1` - (The exact command from CLAUDE/Plan/CLAUDE.md) **What the official command does**: + - Finds all plan directories with 3-digit prefixes (NNN-*) - Extracts just the plan number (NNN) - Sorts numerically (ensuring 9 < 10 < 100) - Returns the highest number (next plan = highest + 1) **Benefits**: + - Ensures consistent plan discovery workflow - Directs users to official documentation - Prevents fragile ad-hoc approaches @@ -39,6 +43,7 @@ **Purpose**: Blocks WebSearch queries containing outdated year references (like "2024" when it's 2025). **How it works**: + 1. Intercepts WebSearch tool calls before execution 2. Scans query for 4-digit year patterns (1900-2099) 3. Compares found years against current year @@ -50,16 +55,19 @@ **Philosophy**: Search results stay relevant when queries use current years. Outdated year references return stale information. **BLOCKED patterns**: + - `"latest React features 2024"` when it's 2025 - `"PHP 8.3 2024 release"` when it's 2025 - Any query with years that don't match current year **ALLOWED patterns**: + - `"latest React features 2025"` (current year) - `"latest React features"` (no year, search engine handles recency) - Historical queries are still blocked (needs refinement for legitimate historical research) **Example blocked query**: + ``` Query: "TypeScript best practices 2024" Status: BLOCKED @@ -68,6 +76,7 @@ Suggestion: Remove year or use 2025 ``` **Benefits**: + - Prevents searching with outdated year references - Ensures Claude searches for current information - Catches copy-paste queries from old conversations @@ -80,6 +89,7 @@ Suggestion: Remove year or use 2025 **Purpose**: Enforces use of LLM-optimized npm commands. Blocks ALL npm commands except llm: prefixed ones and a tiny whitelist. **How it works**: + 1. Intercepts Bash tool calls containing `npm run` commands 2. BLOCKS ALL commands by default (strict deny-by-default policy) 3. Only allows llm: prefixed commands or tiny whitelist (currently just `clean`) @@ -89,12 +99,14 @@ Suggestion: Remove year or use 2025 **Trigger**: PreToolUse (Bash tool only) **Philosophy**: Claude should ALWAYS use llm: prefixed commands which provide: + - Minimal stdout (summary only) - Verbose JSON logging to files (./var/qa/) - Machine-readable output for parsing - Caching system for performance **BLOCKED (nearly everything)**: + - `npm run build` → Must use `npm run llm:build` - `npm run lint` → Must use `npm run llm:lint` - `npm run type-check` → Must use `npm run llm:type-check` @@ -114,10 +126,12 @@ Suggestion: Remove year or use 2025 - `npx playwright` → Must use `npm run llm:test` **ALLOWED (tiny whitelist)**: + - All `npm run llm:*` commands (the whole point!) - `npm run clean` (cleanup operation, no llm: version needed) **Why llm: commands**: + - Minimal stdout (summary only, not verbose output) - Verbose JSON logging to ./var/qa/ files - Machine-readable output (no parsing needed) @@ -126,6 +140,7 @@ Suggestion: Remove year or use 2025 - No need for grep/awk/sed post-processing **Benefits**: + - Forces use of optimized LLM workflows - Prevents brittle text-parsing with grep/awk/sed - Ensures consistent machine-readable output @@ -133,6 +148,7 @@ Suggestion: Remove year or use 2025 - Dramatically reduces token usage (summary only, not full output) **Example blocked command**: + ```bash npm run build Status: BLOCKED @@ -140,6 +156,7 @@ Suggestion: npm run llm:build ``` **Example allowed commands**: + ```bash npm run llm:build Status: ALLOWED @@ -164,6 +181,7 @@ Stdout: Summary only (minimal) **Purpose**: Prevents ad-hoc script execution (via `npx tsx scripts/...`) when npm command wrappers exist. **How it works**: + 1. Intercepts Bash tool calls that invoke scripts via `npx tsx`, `tsx`, or `node` 2. Checks if the script has a corresponding npm command wrapper 3. BLOCKS ad-hoc execution if npm command exists @@ -173,12 +191,14 @@ Stdout: Summary only (minimal) **Trigger**: PreToolUse (Bash tool only) **Philosophy**: All scripts with npm command wrappers must be run via npm to ensure: + - Standardised execution environment - Proper error handling and logging - Consistent caching behaviour - Integration with build pipeline **BLOCKED patterns** (scripts with npm commands): + - `npx tsx scripts/llm-lint.ts` → Must use `npm run llm:lint` - `tsx scripts/qa.ts` → Must use `npm run qa` - `node scripts/generate-screenshots.ts` → Must use `npm run screenshots` @@ -187,18 +207,21 @@ Stdout: Summary only (minimal) - `npx tsx /workspace/scripts/find-unused-exports.ts` → Must use `npm run llm:unused-exports` **Protected scripts** (27 scripts with npm wrappers): + - All `llm-*.ts` scripts (llm-lint, llm-type-check, llm-format, etc.) - Build pipeline scripts (extract-seo-metadata, prerender-pages, verify-build, etc.) - QA scripts (qa.ts, generate-screenshots.ts, lighthouse-audit.ts, etc.) - Utility scripts with wrappers (find-unused-exports, sort-lazy-imports, etc.) **ALLOWED ad-hoc patterns** (scripts WITHOUT npm commands): + - `npx tsx scripts/debug-page.ts` ✅ No npm wrapper - `tsx scripts/eslint-wrapper.ts` ✅ No npm wrapper - `npx tsx scripts/generate-page-folders.ts` ✅ No npm wrapper - `tsx scripts/test-playwright-403.ts` ✅ No npm wrapper **Benefits**: + - Enforces use of npm commands for consistency - Prevents confusion about which execution method to use - Ensures proper logging and caching @@ -206,6 +229,7 @@ Stdout: Summary only (minimal) - Clear separation between production scripts (npm) and ad-hoc utilities **Example blocked command**: + ```bash npx tsx scripts/llm-lint.ts Status: BLOCKED @@ -214,6 +238,7 @@ Reason: This script has an npm wrapper - use it for standardised execution ``` **Example allowed commands**: + ```bash npm run llm:lint Status: ALLOWED (correct way to run script) @@ -229,6 +254,7 @@ Status: ALLOWED (no npm wrapper exists) **Purpose**: Enables automatic continuation of multi-step tasks without confirmation prompts. **How it works**: + 1. Detects when Claude/agents ask confirmation questions like "Would you like me to continue?" 2. When user responds with minimal answers ("yes", "y", "continue", "proceed", "go ahead"), enhances the prompt with STRONG auto-continue instructions 3. Adds explicit direction to proceed through ALL remaining batches/phases/steps without further approval @@ -239,6 +265,7 @@ Status: ALLOWED (no npm wrapper exists) **Configuration**: Registered in `.claude/settings.local.json` **Common patterns detected**: + - "would you like me to continue/proceed/start" - "should I continue/proceed" - "shall I continue" @@ -250,6 +277,7 @@ Status: ALLOWED (no npm wrapper exists) **Enhancement applied**: When minimal response detected, the hook adds: + ``` [AUTO-CONTINUE MODE: YES, continue with ALL remaining work. Do NOT ask for confirmation again. @@ -259,6 +287,7 @@ Execute the full plan without further approval requests.] ``` **Minimal responses recognized**: + - yes, y, yep, yeah - ok, okay - continue, proceed @@ -266,6 +295,7 @@ Execute the full plan without further approval requests.] - do it, yes please **Benefits**: + - Eliminates need to repeatedly approve continuation - Ensures agents complete full multi-batch workflows - Particularly useful for orchestration agents (page-orchestration, qa, eslint, sitemap) @@ -280,6 +310,7 @@ Execute the full plan without further approval requests.] **Purpose**: Warns when American English spellings are detected in content files. **How it works**: + 1. Checks `.md`, `.ejs`, `.html`, and `.txt` files 2. Scans for American spellings (color, organize, center, etc.) 3. Suggests British alternatives (colour, organise, centre, etc.) @@ -294,6 +325,7 @@ Execute the full plan without further approval requests.] **Purpose**: Blocks all ESLint suppression comments in TypeScript/JavaScript files. **How it works**: + 1. Checks `.ts`, `.tsx`, `.js`, `.jsx` files 2. Scans for suppression patterns: `eslint-disable`, `@ts-ignore`, `@ts-expect-error` 3. Blocks the write operation if any suppression is detected @@ -304,6 +336,7 @@ Execute the full plan without further approval requests.] **Philosophy**: "Fix the problem, don't hide it" **Blocked patterns**: + - `eslint-disable` - `eslint-disable-line` - `eslint-disable-next-line` @@ -317,6 +350,7 @@ Execute the full plan without further approval requests.] **Purpose**: Runs ESLint validation on all TypeScript/TSX file writes to ensure code quality. **How it works**: + 1. Intercepts Write and Edit operations on `.ts` and `.tsx` files 2. Runs ESLint validation on the file before allowing the write 3. For Write operations: creates temp file, validates, then allows write if passed @@ -326,6 +360,7 @@ Execute the full plan without further approval requests.] **Trigger**: PreToolUse (Write and Edit tools) **What it catches**: + - Ad-hoc HTML/JSX without proper components - Invalid Tailwind classes (text-create, text-support, etc.) - Card-like divs that should use ServiceGrid @@ -334,6 +369,7 @@ Execute the full plan without further approval requests.] - All ESLint rule violations **Benefits**: + - Prevents bad code from being written in the first place - Catches issues immediately, not at commit time - Forces agents to write clean, compliant code @@ -346,6 +382,7 @@ Execute the full plan without further approval requests.] **Purpose**: Validates that CLAUDE.md and README.md files contain only useful instructions, NOT logs, research, or LLM output. **How it works**: + 1. Intercepts Write and Edit operations on CLAUDE.md and README.md files 2. Scans for blocked content patterns (logs, research, summaries, status indicators) 3. Skips detection of patterns inside markdown code blocks (between ``` markers) @@ -357,6 +394,7 @@ Execute the full plan without further approval requests.] **Philosophy**: These files should contain actionable instructions for humans or LLMs, not documentation of what was already done. **ALLOWED content**: + - Clear, actionable instructions for LLMs/humans - Context about directory/module purpose - Guidelines, conventions, patterns to follow @@ -365,6 +403,7 @@ Execute the full plan without further approval requests.] - Examples showing proper usage **BLOCKED patterns** (all case variations): + - Implementation logs ("Created X", "Modified Y", "Added Z", "Step 1", "Phase 2") - Research findings, analysis output - Test results, QA output @@ -376,11 +415,13 @@ Execute the full plan without further approval requests.] - Completion indicators ("all done", "that should fix it") **Exceptions** (not flagged): + - Code examples containing blocked patterns - Markdown code blocks with ``` delimiters - Quoted examples explaining what NOT to do **Benefits**: + - Ensures CLAUDE.md/README.md stay focused on instructions - Prevents bloat from implementation notes - Keeps documentation useful long-term @@ -390,6 +431,7 @@ Execute the full plan without further approval requests.] **Examples**: Blocked - implementation log: + ``` CLAUDE.md contains: "Created hook for validating content" Status: BLOCKED @@ -397,6 +439,7 @@ Suggestion: Move to untracked/implementation-notes.md ``` Blocked - LLM summary: + ``` README.md contains: "## Summary Here's what I did..." @@ -405,6 +448,7 @@ Suggestion: Remove summary, focus on instructions for using the directory ``` Allowed - instructional content: + ``` CLAUDE.md contains: "## How to use hooks @@ -421,6 +465,7 @@ Status: ALLOWED **Purpose**: BLOCKS git stash usage (dangerous workflow pattern) with escape hatch for confirmed necessary cases. **How it works**: + 1. Detects when Claude attempts to run `git stash` commands 2. Distinguishes between safe operations (viewing/recovering stashes) and dangerous ones (creating stashes) 3. BLOCKS dangerous stash operations by default (exit code 2) @@ -430,6 +475,7 @@ Status: ALLOWED **Trigger**: PreToolUse (Bash tool only) **Why git stash is dangerous**: + - Stashes can be forgotten and lost - `git stash drop` and `git stash clear` permanently destroy work - Stashes are not part of the git graph (can become orphaned) @@ -437,12 +483,14 @@ Status: ALLOWED - Usually a bodge to avoid solving the real git issue **What it BLOCKS** (dangerous stash operations): + - `git stash` (creating stash) - `git stash push` - `git stash save "message"` - `git stash -u` (stash untracked files) **What it ALLOWS without warning** (safe read/recovery operations): + - `git stash list` (viewing stashes) - `git stash show` (viewing stash contents) - `git stash apply` (recovering stashed work) @@ -450,16 +498,19 @@ Status: ALLOWED **Escape Hatch**: Include this EXACT phrase in your command to bypass the block: + ``` "I HAVE ABSOLUTELY CONFIRMED THAT STASH IS THE ONLY OPTION" ``` Example usage: + ```bash git stash # I HAVE ABSOLUTELY CONFIRMED THAT STASH IS THE ONLY OPTION ``` **Suggested alternatives** (use these instead of stash): + - `git commit -m "WIP: description"` - Proper version control - `git checkout -b experiment/feature-name` - New branch for experimental work - `git worktree add ../worktree-name` - Use worktrees for parallel work @@ -472,6 +523,7 @@ git stash # I HAVE ABSOLUTELY CONFIRMED THAT STASH IS THE ONLY OPTION **Purpose**: Prevents time estimates and completion dates from being written to CLAUDE/Plan/*.md files. **How it works**: + 1. Intercepts Write and Edit operations on files in CLAUDE/Plan/ directories 2. Scans content for time estimate patterns (hours, days, weeks, target completion dates) 3. Blocks the operation if any time estimates are detected @@ -482,6 +534,7 @@ git stash # I HAVE ABSOLUTELY CONFIRMED THAT STASH IS THE ONLY OPTION **Philosophy**: Time estimates are context bloat. Plans should focus on WHAT needs to be done and HOW, not WHEN or HOW LONG. **What it BLOCKS** (all case variations): + - `Estimated Effort: X hours/minutes/days/weeks` - `Time estimated: ...` or `Estimated time: ...` - `Total Estimated Time: ...` @@ -491,22 +544,26 @@ git stash # I HAVE ABSOLUTELY CONFIRMED THAT STASH IS THE ONLY OPTION - Timeline sections containing time durations **Plans should focus on**: + - What needs to be done - Why it's needed - How to implement it **Plans should NEVER include**: + - When work will be completed - How long tasks will take - Timeline estimates **Escape Hatch**: If a pattern match is a false positive (genuinely not a time estimate), add this comment: + ```html ``` Example: + ```markdown The reference documentation talks about "2 hours" of battery life. @@ -519,6 +576,7 @@ The reference documentation talks about "2 hours" of battery life. **Purpose**: Enforces strict organization rules for markdown documentation files. Prevents markdown files from littering the filesystem. **How it works**: + 1. Intercepts Write and Edit operations on `*.md` files 2. Validates file path against allowed location patterns 3. Auto-detects current plan being worked on @@ -565,6 +623,7 @@ The reference documentation talks about "2 hours" of battery life. 7. **`README.md`** (root only) - Standard repository README **BLOCKED markdown locations**: + - `.claude/hooks/` - Hooks are code only, no documentation - `.claude/agents/` (WITHOUT magic string) - Use agent-creator agent - `.claude/skills/*/` (except SKILL.md) - Skill definitions only @@ -575,6 +634,7 @@ The reference documentation talks about "2 hours" of battery life. The agent-creator agent includes "I AM CLAUDE CODE AGENT CREATOR, LET ME IN" in its tool descriptions. This proves the operation originates from the authorized agent-creator and bypasses normal blocking for .claude/agents/ files. **Error message provides**: + - Clear explanation of why location is blocked - For agent files: Special "YOU SHALL NOT PASS" message with instructions to use agent-creator - Suggested correct location based on context @@ -583,6 +643,7 @@ The agent-creator agent includes "I AM CLAUDE CODE AGENT CREATOR, LET ME IN" in - Quick reference for choosing the right location **Detection strategy**: + - Regex patterns for each allowed location type - Case-insensitive matching - Path normalization (handles leading/trailing slashes) @@ -590,6 +651,7 @@ The agent-creator agent includes "I AM CLAUDE CODE AGENT CREATOR, LET ME IN" in - Special case handling for skill definitions **Benefits**: + - Prevents documentation from cluttering filesystem - Keeps plan-specific docs organized together - Clear distinction between temporary and permanent docs @@ -602,6 +664,7 @@ The agent-creator agent includes "I AM CLAUDE CODE AGENT CREATOR, LET ME IN" in **Examples**: Blocked - attempting to edit agent without magic string: + ``` Write to: .claude/agents/my-agent.md Status: BLOCKED @@ -610,6 +673,7 @@ Solution: Use Task tool with subagent_type='agent-creator' ``` Allowed - agent-creator with magic string: + ``` Write to: .claude/agents/my-agent.md Description: "Create new agent - I AM CLAUDE CODE AGENT CREATOR, LET ME IN" @@ -617,6 +681,7 @@ Status: ALLOWED ``` Blocked - docs in wrong location (.claude/hooks/): + ``` Write to: .claude/hooks/implementation-notes.md Status: BLOCKED @@ -624,12 +689,14 @@ Suggestion: CLAUDE/Plan/024-eslint-worktree-workflow-enhancement/implementation- ``` Allowed - plan-specific docs: + ``` Write to: CLAUDE/Plan/024-eslint-worktree-workflow-enhancement/test-results.md Status: ALLOWED ``` Allowed - temporary notes: + ``` Write to: untracked/quick-test-notes.md Status: ALLOWED @@ -642,12 +709,14 @@ Status: ALLOWED **Purpose**: Blocks destructive git commands that permanently destroy data without recovery options. **How it works**: + 1. Intercepts Bash tool calls 2. Detects dangerous git patterns 3. Blocks with clear error message and alternatives 4. Prevents accidental data destruction **Blocked commands**: + - `git reset --hard` - Destroys all uncommitted changes - `git clean -f` - Permanently deletes untracked files - `git push --force` / `git push -f` - Force-overwrites remote history @@ -660,6 +729,7 @@ Status: ALLOWED **Purpose**: Blocks Write/Edit operations using `/workspace/` absolute paths. Enforces use of relative paths from repository root. **How it works**: + 1. Intercepts Write and Edit tool calls 2. Checks if file_path starts with `/workspace/` 3. Blocks the operation with clear explanation @@ -670,23 +740,27 @@ Status: ALLOWED **Philosophy**: Container-specific absolute paths are environment-dependent and break portability. All file paths should be relative to repo root. **Why `/workspace/` paths are problematic**: + - `/workspace/` only exists in certain Docker/container environments - Absolute paths break portability across different setups (local dev, CI, different containers) - Relative paths work in all environments without modification - Hard-coded absolute paths couple code to specific deployment environments **BLOCKED patterns**: + - `Write` to `/workspace/src/test.ts` → Must use `src/test.ts` - `Edit` to `/workspace/CLAUDE/Plan/001/PLAN.md` → Must use `CLAUDE/Plan/001/PLAN.md` - Any file path starting with `/workspace/` **ALLOWED patterns**: + - `Write` to `src/test.ts` (relative path) - `Edit` to `./src/components/Button.tsx` (dot-relative path) - `Write` to `CLAUDE/documentation.md` (relative path) - Other absolute paths like `/home/user/file.ts` (not environment-specific) **Benefits**: + - Ensures code portability across all environments - Prevents container-specific path coupling - Makes file operations work in local dev, CI, and production @@ -699,17 +773,20 @@ Status: ALLOWED **Purpose**: Prevents copying files between worktrees (dangerous cross-worktree workflow pattern). **How it works**: + 1. Detects cp/copy commands that copy FROM one worktree TO another 2. Blocks the operation 3. Suggests proper git-based alternatives **Why this is dangerous**: + - Worktrees should be independent branches - Copying files breaks git history tracking - Can cause merge conflicts and data loss - Violates worktree isolation principles **Safe alternatives**: + - Use git commits/branches - Use shared configuration files - Use git merge (proper workflow) @@ -721,6 +798,7 @@ Status: ALLOWED **Purpose**: Reminds to validate sitemap files after editing them. **How it works**: + 1. Triggers on Edit/Write operations to `CLAUDE/Sitemap/**/*.md` files 2. Allows the write operation (non-blocking) 3. Adds reminder context to run sitemap-validator agent after editing @@ -729,17 +807,20 @@ Status: ALLOWED **Trigger**: PostToolUse (Edit/Write tools on sitemap files) **Philosophy**: Allow modifications but remind to validate afterward. Validation ensures: + - No content (statistics, prose, descriptions) - No hallucinated components (must exist in src/components/CLAUDE.md) - No implementation details (props, code, styling) - Correct notation (CSI enums, arrow syntax) **What it checks**: + - Files in CLAUDE/Sitemap/ directory - Markdown files only (.md extension) - Ignores CLAUDE/Sitemap/CLAUDE.md (the rules file itself) **Reminder includes**: + - How to run sitemap-validator agent - What the validator checks - Note that sitemap skill runs validation automatically @@ -751,6 +832,7 @@ Status: ALLOWED **Purpose**: Generic hook to remind running validator agents after builder/modifier agents complete. **How it works**: + 1. Triggers on SubagentStop event (when any subagent completes) 2. Parses conversation transcript to identify which agent completed 3. Checks if completed agent has a corresponding validator in the mapping @@ -762,6 +844,7 @@ Status: ALLOWED **Philosophy**: Automate reminders for validation after modifications. Builder agents should always be followed by validator agents to complete the build→check loop. **Currently mapped workflows**: + - `sitemap-modifier` → `sitemap-validator` - Validate sitemap notation and components - `page-implementer` → `page-technical-reviewer` - Review page implementation for code quality - `page-content-updater` → `page-humanizer` - Humanize content and remove LLM tells @@ -771,6 +854,7 @@ Status: ALLOWED - `typescript-specialist` → `qa-runner` - Run full QA suite (ESLint + TypeScript + tests) on new code **Extensible**: Add more builder→validator mappings in `BUILDER_TO_VALIDATOR` dict: + ```python BUILDER_TO_VALIDATOR = { "sitemap-modifier": { @@ -784,12 +868,14 @@ BUILDER_TO_VALIDATOR = { ``` **How it detects agent**: + 1. Reads conversation transcript (JSONL format) 2. Parses in reverse to find most recent Task tool call 3. Extracts `subagent_type` parameter 4. Looks up validator in mapping **Reminder includes**: + - Confirmation that builder agent completed - Which validator to run - How to invoke the validator @@ -802,6 +888,7 @@ BUILDER_TO_VALIDATOR = { **Purpose**: Validates plan folder numbers when creating new plans in CLAUDE/Plan/. Ensures sequential numbering by checking BOTH Plan/ and Plan/Completed/ directories. **How it works**: + 1. Detects when a new plan folder is about to be created (pattern: CLAUDE/Plan/NNN-*) 2. Finds highest existing plan number from both Plan/ and Plan/Completed/ 3. Validates new plan number is highest + 1 @@ -812,18 +899,21 @@ BUILDER_TO_VALIDATOR = { **Why PreToolUse (not PostToolUse)**: The handler was moved from PostToolUse to PreToolUse to fix a timing bug: + - PostToolUse ran AFTER directory creation - Handler saw the just-created directory as "existing" - Creating 061 when highest was 060 incorrectly warned "use 062" - PreToolUse validates BEFORE creation, so 060 is correctly seen as highest **Why check both directories**: + - Active plans in CLAUDE/Plan/ - Completed plans in CLAUDE/Plan/Completed/ - Numbering must be sequential across both - Prevents number reuse and collisions **Command to find correct number**: + ```bash find CLAUDE/Plan -maxdepth 2 -type d -name '[0-9]*' | sed 's|.*/\([0-9]\{3\}\).*|\1|' | sort -n | tail -1 ``` @@ -831,12 +921,14 @@ find CLAUDE/Plan -maxdepth 2 -type d -name '[0-9]*' | sed 's|.*/\([0-9]\{3\}\).* **Philosophy**: Plans must be numbered sequentially for easy reference and tracking. **What happens on incorrect number**: + - Hook allows operation (non-blocking) - Provides exact `mv` command to fix - Reminds to update references in plan files - Points to CLAUDE/Plan/CLAUDE.md for instructions **Example**: + ``` Highest existing: 028 (in Completed/) Agent creates: CLAUDE/Plan/028-new-feature/ diff --git a/.claude/init.sh b/.claude/init.sh index 94bb6524..06a590f7 100755 --- a/.claude/init.sh +++ b/.claude/init.sh @@ -284,29 +284,38 @@ _resolve_python_cmd() { # # _get_hostname_suffix() - Get hostname-based suffix for runtime files # -# Uses HOSTNAME environment variable directly to isolate daemon runtime -# files across different environments (containers, machines). +# Resolves a STABLE hostname (in series): $HOSTNAME, then the `hostname` +# command (the OS hostname), then a constant. This MUST agree with the Python +# side (daemon/paths.py:_resolve_hostname_from_env, which uses +# socket.gethostname()) so the bash forwarder and the Python daemon compute the +# SAME socket/PID suffix. +# +# NEVER use a time-based hash here: $HOSTNAME is unset on macOS (zsh) and many +# minimal containers, and a time hash changes on every call — so start/status/ +# stop would each look for a different socket (the macOS daemon-unmanageable +# bug, Plan 00122 BUG 1). # # Returns: -# "-{sanitized-hostname}" or "-{time-hash}" if no hostname +# "-{sanitized-hostname}" # # Example: # HOSTNAME="laptop" -> "-laptop" # HOSTNAME="506355bfbc76" -> "-506355bfbc76" # HOSTNAME="My-Server" -> "-my-server" -# No HOSTNAME -> "-a1b2c3d4" (MD5 of timestamp) +# No HOSTNAME -> "-{os-hostname}" (e.g. "-work.local"), or "-localhost" # _get_hostname_suffix() { local hostname="${HOSTNAME:-}" - # No hostname? Use MD5 of current time for uniqueness + # No $HOSTNAME (macOS/zsh, minimal containers)? Use the OS hostname — the + # same value Python's socket.gethostname() returns — so both sides agree. + if [[ -z "$hostname" ]] && command -v hostname > /dev/null; then + hostname="$(hostname)" + fi + + # Last resort: a stable constant (matches paths.py _HOSTNAME_FALLBACK). if [[ -z "$hostname" ]]; then - local timestamp - timestamp=$(date +%s.%N) - local hash - hash=$(echo -n "$timestamp" | md5sum | cut -c1-8) - echo "-${hash}" - return 0 + hostname="localhost" fi # Sanitize hostname for filesystem safety: lowercase, no spaces @@ -377,7 +386,6 @@ _exec_bit_selfheal() { # Pattern: {project}/.claude/hooks-daemon/untracked/daemon.{sock|pid} # Container: {project}/.claude/hooks-daemon/untracked/daemon-{hash}.{sock|pid} # Must match Python paths module: claude_code_hooks_daemon.daemon.paths -_abs_project_path=$(realpath "$PROJECT_PATH") # Determine untracked directory path # Must match ProjectContext.daemon_untracked_dir() logic @@ -540,8 +548,13 @@ start_daemon() { return 1 fi - # Remove stale socket file - rm -f "$SOCKET_PATH" + # NOTE (Plan 00127): do NOT `rm -f "$SOCKET_PATH"` here. On the + # host+container shared-untracked path the socket may be owned by a LIVE + # incumbent daemon, and unconditionally deleting it would steal the socket + # before the python layer's liveness gate ever runs. Stale-socket cleanup is + # now the single responsibility of the python server, which probes socket + # liveness before unlinking (reuse on live, unlink on stale). We already + # short-circuit via is_daemon_running() above for the healthy-incumbent case. # Start daemon using CLI (proper daemonization) # CRITICAL: Pass --project-root and export env vars so the CLI uses the diff --git a/.claude/project-handlers/session_start/ts_qa_ci_handler.py b/.claude/project-handlers/session_start/ts_qa_ci_handler.py new file mode 100644 index 00000000..cedf0dfc --- /dev/null +++ b/.claude/project-handlers/session_start/ts_qa_ci_handler.py @@ -0,0 +1,39 @@ +""" +ts-qa-ci project handler, deployed by `ts-qa deploy-skills`. +Regenerate via that command rather than hand-editing - manual edits will +be overwritten on the next deploy. +""" + +from typing import Any + +from claude_code_hooks_daemon.core import AcceptanceTest, Handler, HookResult +from claude_code_hooks_daemon.core.hook_result import Decision + + +class TsQaCiHandler(Handler): + """Advisory handler surfacing ts-qa-ci pipeline status to Claude Code sessions.""" + + def __init__(self) -> None: + super().__init__( + handler_id="ts-qa-ci", + priority=55, + terminal=False, + tags=["project", "qa", "ts-qa-ci"], + ) + + def matches(self, hook_input: dict[str, Any]) -> bool: + return True + + def handle(self, hook_input: dict[str, Any]) -> HookResult: + return HookResult(decision=Decision.ALLOW) + + def get_claude_md(self) -> str | None: + return ( + "## ts-qa-ci\n\n" + "This project uses `ts-qa-ci` for QA/CI. Run `npx ts-qa` for the full " + "pipeline, or `npx ts-qa -t ` to run a single tool. See " + "`node_modules/@longtermsupport/ts-qa-ci/docs/` for the full docs set.\n" + ) + + def get_acceptance_tests(self) -> list[AcceptanceTest]: + return [] diff --git a/.claude/skills/hooks-daemon/SKILL.md b/.claude/skills/hooks-daemon/SKILL.md index 2e6a495f..98d49ff1 100644 --- a/.claude/skills/hooks-daemon/SKILL.md +++ b/.claude/skills/hooks-daemon/SKILL.md @@ -1,7 +1,7 @@ --- name: hooks-daemon description: Manage Claude Code Hooks Daemon - install, upgrade, check health, restart, and develop project-level handlers -argument-hint: "[install|upgrade|health|restart|dev-handlers|logs] [args...]" +argument-hint: '[install|upgrade|health|restart|check|dev-handlers|regen-docs|logs|release-notes] [args...]' disable-model-invocation: false user-invocable: true allowed-tools: Bash, Read, Write, Edit @@ -14,7 +14,9 @@ Manage your Claude Code Hooks Daemon installation with these commands. ## Available Commands ### Install Daemon + Install the hooks daemon on a fresh clone (daemon not yet present): + ```bash /hooks-daemon install # Install daemon from GitHub /hooks-daemon install --force # Force reinstall over existing @@ -23,7 +25,9 @@ Install the hooks daemon on a fresh clone (daemon not yet present): See [install.md](install.md) for detailed install documentation. ### Upgrade Daemon + Update to a new version of the hooks daemon: + ```bash /hooks-daemon upgrade # Auto-detect and upgrade to latest version /hooks-daemon upgrade 2.14.0 # Upgrade to specific version @@ -33,7 +37,9 @@ Update to a new version of the hooks daemon: See [upgrade.md](upgrade.md) for detailed upgrade documentation. ### Restart Daemon + **Required after editing `.claude/hooks-daemon.yaml` or project handlers:** + ```bash /hooks-daemon restart ``` @@ -42,8 +48,30 @@ The daemon caches config at startup — restart picks up any config or handler c See [restart.md](restart.md) for details. +### Regenerate Generated Docs + +Force-regenerate the daemon's generated documentation **without restarting**: + +```bash +/hooks-daemon regen-docs +``` + +Rewrites both generated artifacts to their canonical form in one shot: + +- `.claude/HOOKS-DAEMON.md` — the active-handler summary. +- The `` guidance block inside your project `CLAUDE.md`. + +This is the explicit way to recover both files after a **git merge/rebase conflict** +left them stale or conflict-marked — run it, then stage the clean result. (A normal +`restart` also refreshes these, but `regen-docs` does it as a one-shot with no daemon +bounce.) + +See [regen-docs.md](regen-docs.md) for details. + ### Check Health & Status + Verify daemon is running correctly: + ```bash /hooks-daemon health # Quick health check /hooks-daemon logs # View last 50 lines of logs @@ -52,8 +80,26 @@ Verify daemon is running correctly: See [health.md](health.md) for health check details. +### Check Environment & Configuration + +Run a verbose, on-demand audit of the Claude Code environment: + +```bash +/hooks-daemon check +``` + +Reports Claude Code optimal-config settings (max output tokens, bash working +directory, effort level, extended thinking, agent teams, auto-memory) with +fix instructions, plus the container runtime, git `core.fileMode`, and +hook-registration drift. This is the detail that SessionStart deliberately +keeps quiet — SessionStart now only speaks when something needs action. + +See [check.md](check.md) for details. + ### Develop Project Handlers + Scaffold new project-level handlers: + ```bash /hooks-daemon dev-handlers # Interactive handler scaffolding ``` @@ -61,7 +107,9 @@ Scaffold new project-level handlers: See [dev-handlers.md](dev-handlers.md) for handler development guide. ### Investigate an Issue + Generate a detailed investigation report with timeline, evidence, and analysis: + ```bash /hooks-daemon report "daemon stopped responding during edits" ``` @@ -70,15 +118,35 @@ The report is saved to `./untracked/hooks-daemon-{description}.md` for sharing w See [report.md](report.md) for details. +### Read Release Notes + +Show the daemon's release notes without leaving the terminal. With no flag it +shows the notes for the version you currently have installed: + +```bash +/hooks-daemon release-notes # installed version's notes +/hooks-daemon release-notes --latest # newest available version +/hooks-daemon release-notes --version 3.27.0 # a specific version +/hooks-daemon release-notes --from 3.20.0 --to 3.27.0 # everything you gained upgrading +/hooks-daemon release-notes --list # list available versions +/hooks-daemon release-notes --version 3.27.0 --format json +``` + +Notes are read from the per-version `RELEASES/vX.Y.Z.md` files that ship with +the install — no network access required. `--from` is exclusive and `--to` is +inclusive, matching the upgrade semantics (the notes for everything you gained). + ## Quick Start After editing `.claude/hooks-daemon.yaml`: + ```bash /hooks-daemon restart # Apply config changes /hooks-daemon health # Verify it's running ``` If you're experiencing issues: + ```bash # 1. Check daemon status /hooks-daemon health @@ -135,7 +203,12 @@ case "$SUBCOMMAND" in cat "$SKILL_DIR/report.md" | sed "s/\$ARGUMENTS/$*/" ;; - logs|status|restart|handlers|validate-config|bug-report) + regen-docs|regenerate-docs) + # User-facing alias regen-docs maps to the CLI command regenerate-docs. + bash "$SKILL_DIR/scripts/daemon-cli.sh" regenerate-docs "$@" + ;; + + logs|status|restart|handlers|validate-config|bug-report|check|release-notes) # Forward to daemon CLI wrapper bash "$SKILL_DIR/scripts/daemon-cli.sh" "$SUBCOMMAND" "$@" ;; @@ -147,14 +220,17 @@ case "$SUBCOMMAND" in echo "Available commands:" echo " install [--force] Install daemon (fresh clone)" echo " restart Restart daemon (required after config changes)" + echo " regen-docs Force-regenerate HOOKS-DAEMON.md + CLAUDE.md block" echo " health Check daemon health and status" echo " upgrade [VERSION] Upgrade daemon to new version" echo " dev-handlers Scaffold new project handlers" echo " logs [--follow] View daemon logs" echo " status Show daemon status" echo " handlers List loaded handlers" + echo " check Verbose environment & configuration audit" echo " bug-report DESC Generate bug report with diagnostics" echo " report DESC Investigate an issue and generate a detailed report" + echo " release-notes [opts] Show release notes (installed version by default)" echo "" echo "After editing .claude/hooks-daemon.yaml, always run: /hooks-daemon restart" echo "" diff --git a/.claude/skills/hooks-daemon/check.md b/.claude/skills/hooks-daemon/check.md new file mode 100644 index 00000000..17bc7c76 --- /dev/null +++ b/.claude/skills/hooks-daemon/check.md @@ -0,0 +1,66 @@ +# Hooks Daemon Environment Check + +Run a verbose, on-demand audit of the Claude Code environment and daemon +configuration. + +## Usage + +```bash +/hooks-daemon check +``` + +## What It Reports + +SessionStart is deliberately quiet — it only surfaces things that need action +(a daemon update, a missing `.gitignore` entry). `check` is where you get the +full picture on demand: + +- **Claude Code configuration** — each optimal setting with its current value, + and for anything sub-optimal: why it matters, how to fix it, where to set it, + and a docs link. Covers: + - Agent Teams (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`) + - Effort Level (`effortLevel` / `CLAUDE_CODE_EFFORT_LEVEL`) + - Extended Thinking (`alwaysThinkingEnabled`) + - Max Output Tokens (`CLAUDE_CODE_MAX_OUTPUT_TOKENS`) + - Auto Memory (`CLAUDE_CODE_DISABLE_AUTO_MEMORY`) + - Bash Working Directory (`CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR`) +- **Container runtime** — docker / podman / lxc / generic, or desktop/host. + (Also shown continuously by the status-line environment icon.) +- **Git `core.fileMode`** — warns when `false` (hooks can lose their exec bit). +- **Hook registration** — drift between `settings.json` and + `settings.local.json`. + +## Example Output + +``` +Hooks Daemon — Environment Check + +Claude Code configuration: 4/6 optimal + [OK ] Agent Teams: CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS='1' + [OK ] Effort Level: effortLevel='high' + [OK ] Extended Thinking: alwaysThinkingEnabled=True + [MISS] Max Output Tokens: Not set (default: 32000) + Why: ... + Fix: Set environment variable: export CLAUDE_CODE_MAX_OUTPUT_TOKENS="64000" + Where: ~/.bashrc, ~/.zshrc, or settings.json env section + Docs: https://code.claude.com/docs/en/settings + [OK ] Auto Memory: Enabled (default) + [MISS] Bash Working Directory: Not set + ... + +Container runtime: + In a podman container + +Git core.fileMode: + true (OK) + +Hook registration: + OK — all hooks registered correctly in settings.json +``` + +## Notes + +- The audit is **advisory** — it always exits `0` and never blocks. +- It reuses the SessionStart handlers' own check logic, so the report and the + (quiet) session-start advisories stay in sync — a single source of truth. +- Equivalent direct call: `$PYTHON -m claude_code_hooks_daemon.daemon.cli check`. diff --git a/.claude/skills/hooks-daemon/dev-handlers.md b/.claude/skills/hooks-daemon/dev-handlers.md index e5fbee19..93286e5c 100644 --- a/.claude/skills/hooks-daemon/dev-handlers.md +++ b/.claude/skills/hooks-daemon/dev-handlers.md @@ -138,7 +138,7 @@ project_handlers: enabled: true path: .claude/project-handlers handlers: - {event_type}: + { event_type }: my_custom: enabled: true priority: 50 diff --git a/.claude/skills/hooks-daemon/references/troubleshooting.md b/.claude/skills/hooks-daemon/references/troubleshooting.md index 1b8a0832..d6ce532f 100644 --- a/.claude/skills/hooks-daemon/references/troubleshooting.md +++ b/.claude/skills/hooks-daemon/references/troubleshooting.md @@ -114,13 +114,13 @@ Fix `.claude/hooks-daemon.yaml`: handlers: pre_tool_use: my_handler: - priority: "50" # String + priority: '50' # String # RIGHT handlers: pre_tool_use: my_handler: - priority: 50 # Integer + priority: 50 # Integer ``` ## Upgrade Failures @@ -261,7 +261,7 @@ handlers: pre_tool_use: my_handler: enabled: true - priority: 50 # REQUIRED + priority: 50 # REQUIRED ``` ## Performance Issues diff --git a/.claude/skills/hooks-daemon/regen-docs.md b/.claude/skills/hooks-daemon/regen-docs.md new file mode 100644 index 00000000..d86e0d36 --- /dev/null +++ b/.claude/skills/hooks-daemon/regen-docs.md @@ -0,0 +1,60 @@ +# Regenerate Generated Docs + +Force-regenerate the daemon's generated documentation to its canonical form, +**without restarting the daemon**. + +## Usage + +```bash +/hooks-daemon regen-docs +``` + +## What It Regenerates + +Two artifacts are rewritten from live config + handler metadata in one shot: + +1. **`.claude/HOOKS-DAEMON.md`** — the active-handler summary (priorities, behaviours, + per-event handler tables). Same output as `generate-docs`. +2. **The `` block in your project `CLAUDE.md`** — the active-handler + guidance the daemon injects at startup. Surrounding user content is preserved; + only the `...` section is replaced. + +Both are written in the daemon's canonical (formatter-aligned) form, so a later +edit will not produce churn diffs. + +## When to Use + +- **Resolving a git merge/rebase conflict** that left conflict markers or stale + content inside `.claude/HOOKS-DAEMON.md` or the `CLAUDE.md` `` block. + Run `regen-docs`, then stage the clean result — the regenerated content reflects + your merged `.claude/hooks-daemon.yaml`, not either conflicting side. +- After hand-editing `.claude/hooks-daemon.yaml` when you want the generated docs + refreshed but do **not** want to bounce the running daemon. +- Any time the generated docs have drifted from the live config. + +## regen-docs vs restart + +| Action | Refreshes generated docs | Restarts daemon | Use when | +| ------------ | ------------------------ | --------------- | --------------------------------------------- | +| `regen-docs` | Yes | No | Fixing/refreshing docs only (e.g. merge fix) | +| `restart` | Yes (on startup) | Yes | Applying config/handler changes to the daemon | + +A `restart` already regenerates both artifacts on startup. `regen-docs` is the +explicit one-shot when you want the doc regeneration **without** the restart. + +## Options + +```bash +/hooks-daemon regen-docs --include-disabled # list disabled handlers in HOOKS-DAEMON.md +/hooks-daemon regen-docs --output PATH # alternate HOOKS-DAEMON.md output path +``` + +## Verify + +```bash +git diff -- .claude/HOOKS-DAEMON.md CLAUDE.md +``` + +The diff should show only the regenerated, conflict-free content. If `CLAUDE.md` +is in a git repo and was changed, the daemon auto-commits the `` +block on regeneration (only `CLAUDE.md`, no other files are touched). diff --git a/.claude/skills/hooks-daemon/report.md b/.claude/skills/hooks-daemon/report.md index f0ab48ae..e6042738 100644 --- a/.claude/skills/hooks-daemon/report.md +++ b/.claude/skills/hooks-daemon/report.md @@ -109,9 +109,9 @@ Write a single markdown file with this structure: ## Timeline -| Time | Event | Source | -|------|-------|--------| -| ... | ... | daemon log / transcript / git | +| Time | Event | Source | +| ---- | ----- | ----------------------------- | +| ... | ... | daemon log / transcript / git | {Chronological table of events leading up to, during, and after the issue.} @@ -148,6 +148,7 @@ Write a single markdown file with this structure: ## Analysis {Detailed analysis of what went wrong: + - What was the immediate cause? - What was the root cause? - Were there contributing factors? diff --git a/.claude/skills/hooks-daemon/scripts/health-check.sh b/.claude/skills/hooks-daemon/scripts/health-check.sh index 2675a892..91daf46e 100755 --- a/.claude/skills/hooks-daemon/scripts/health-check.sh +++ b/.claude/skills/hooks-daemon/scripts/health-check.sh @@ -112,6 +112,21 @@ else fi # === SELF-BOOTSTRAP END === +# Plan 00122 BUG 4: make any non-zero exit honest. Under `set -euo pipefail` an +# unguarded failure (e.g. sourcing _resolve-venv.sh when venv resolution dies) +# would otherwise terminate the script with NO output, leaving the operator +# nothing to act on (observed on macOS). This trap prints the script, exit +# code, and failing line on a non-zero exit; it stays silent on success. +_health_check_exit_trap() { + local rc=$? + if [ "$rc" -ne 0 ]; then + echo "" >&2 + echo "❌ health-check.sh aborted (exit $rc) at line ${BASH_LINENO[0]:-unknown}." >&2 + echo " Re-run with 'bash -x \"$0\"' for a full trace, or check the daemon logs." >&2 + fi +} +trap _health_check_exit_trap EXIT + # Detect project root PROJECT_ROOT="$(pwd)" while [ "$PROJECT_ROOT" != "/" ]; do diff --git a/.claude/skills/hooks-daemon/scripts/install.sh b/.claude/skills/hooks-daemon/scripts/install.sh index c9a64dee..7044cf4f 100755 --- a/.claude/skills/hooks-daemon/scripts/install.sh +++ b/.claude/skills/hooks-daemon/scripts/install.sh @@ -96,16 +96,48 @@ export HOOKS_DAEMON_PYTHON="$FOUND_PY" echo "Using Python: $FOUND_PY" echo "" +# Plan 00122 BUG 3: an install counts as "already installed" only if it is +# HEALTHY — a venv python exists AND the daemon package imports. A bare +# directory check treated a broken/partial install (dir present, no working +# venv) as complete, so the documented `/hooks-daemon install` could not repair +# it (only `--force` could, which re-clones from scratch). Returns 0 when +# healthy, non-zero otherwise. Probe output goes to a temp file (never +# /dev/null) so failures stay inspectable. +_installation_is_healthy() { + local dir="$1" + local py probe_out + probe_out="$(mktemp)" + # canonical-resolver-exempt: this is the skill bootstrap, which runs + # standalone in a client project BEFORE the daemon source tree (and + # scripts/lib/resolve_venv.sh) exists. This is a lightweight health probe, + # not venv resolution for use, so it globs directly. + for py in "$dir"/untracked/venv-*/bin/python "$dir"/untracked/venv/bin/python; do + if [ -x "$py" ] && "$py" -c "import claude_code_hooks_daemon" > "$probe_out" 2> "$probe_out"; then + rm -f "$probe_out" + return 0 + fi + done + rm -f "$probe_out" + return 1 +} + # Check if already installed if [ -d "$DAEMON_DIR" ] && [ "$FORCE_FLAG" != "--force" ]; then - echo "Daemon is already installed at: $DAEMON_DIR" - echo "" - echo "To upgrade to a new version:" - echo " /hooks-daemon upgrade" + if _installation_is_healthy "$DAEMON_DIR"; then + echo "Daemon is already installed at: $DAEMON_DIR" + echo "" + echo "To upgrade to a new version:" + echo " /hooks-daemon upgrade" + echo "" + echo "To force reinstall:" + echo " /hooks-daemon install --force" + exit 0 + fi + echo "Daemon directory exists at: $DAEMON_DIR" + echo "but the installation looks broken (venv missing or package not importable)." + echo "Repairing now (equivalent to --force)..." echo "" - echo "To force reinstall:" - echo " /hooks-daemon install --force" - exit 0 + FORCE_FLAG="--force" fi # Download installer to temp file (never pipe curl to shell — we block that pattern) diff --git a/.claude/skills/hooks-daemon/scripts/upgrade.sh b/.claude/skills/hooks-daemon/scripts/upgrade.sh index 11db8200..2170ef8a 100755 --- a/.claude/skills/hooks-daemon/scripts/upgrade.sh +++ b/.claude/skills/hooks-daemon/scripts/upgrade.sh @@ -19,9 +19,9 @@ set -euo pipefail if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then - echo "Usage: upgrade.sh [VERSION]" - echo " VERSION Target git tag (default: latest). See HOOKS_DAEMON_UPGRADE_REF" - echo " and HOOKS_DAEMON_UPGRADE_BASE_URL for fetch overrides." + printf '%s\n' "Usage: upgrade.sh [VERSION]" \ + " VERSION Target git tag (default: latest). See HOOKS_DAEMON_UPGRADE_REF" \ + " and HOOKS_DAEMON_UPGRADE_BASE_URL for fetch overrides." exit 0 fi @@ -41,8 +41,11 @@ URL="$BASE_URL/$REF/scripts/upgrade.sh" TMP="$(mktemp)" if ! curl -fsSL --max-time 30 -o "$TMP" "$URL"; then - echo "Error: failed to fetch upgrade.sh from $URL" >&2 rm -f "$TMP" + # Plan 00114 F4: make the offline/network failure actionable instead of a + # dead end. The installed daemon already ships a working Layer 1 upgrade.sh. + # A single printf keeps the shim under its thin-shim line budget. + printf 'Error: failed to fetch upgrade.sh from %s\nRecovery: run the installed daemon Layer 1 directly:\n bash "%s/.claude/hooks-daemon/scripts/upgrade.sh" --project-root "%s"\nor pin a reachable ref: HOOKS_DAEMON_UPGRADE_REF=v3.16.0 bash "%s"\n' "$URL" "$PROJECT_ROOT" "$PROJECT_ROOT" "$0" >&2 exit 1 fi chmod +x "$TMP" diff --git a/.claude/skills/hooks-daemon/upgrade.md b/.claude/skills/hooks-daemon/upgrade.md index 8755d34e..deef2a93 100644 --- a/.claude/skills/hooks-daemon/upgrade.md +++ b/.claude/skills/hooks-daemon/upgrade.md @@ -24,7 +24,64 @@ Upgrade the Claude Code Hooks Daemon and commit the result atomically. $PYTHON -m claude_code_hooks_daemon.daemon.cli status ``` -4. **Stage daemon-owned paths ONLY** with explicit `git add` — other +4. **Reconcile project docs with truth-changes** (skip on `--force` + reinstall, where `from_version == to_version`). Some statements that were + true about working in this project may have changed across the upgrade. + Load the truth-changes for the range you just crossed: + + ```bash + $PYTHON -m claude_code_hooks_daemon.daemon.cli check-truth-changes \ + --from ${from_version} --to ${to_version} + ``` + + Exit code `0` means nothing to do — skip to the next step. Exit code `1` + means there are `was → now` entries to reconcile. For **each** entry: + + - **Semantically** search the PROJECT'S OWN docs for the `was` statement — + `CLAUDE/`, `docs/`, `README*`, `AGENTS*`, and any project instruction + files. It is a natural-language statement, not a literal string; match on + meaning. + - **NEVER** edit anything under `.claude/hooks-daemon/` — that is the + upstream daemon clone and is overwritten on upgrade. + - If `now` is present: update the project's doc to assert the `now` truth + instead. Minimal edits — change only the stale statement. + - If `now` is empty / "remove all reference": delete the stale guidance. Remove + only the specific statement; if it is embedded in a larger section, ask + before removing the whole section. + - If a doc does not assert the `was` truth, there is nothing to do for it + (the step is idempotent — re-running is a no-op). + + Stage and commit any project-doc edits **separately** from the daemon + upgrade commit below (they touch project files, not daemon-owned paths). You + can re-run `check-truth-changes` any time to re-reconcile. + +5. **Surface newly-available / recommended config options** (skip on `--force` + reinstall, where `from_version == to_version`). Some releases add opt-in + protections or flip a default; this step reports what is now available or + recommended for the range you crossed so a new feature never ships dormant: + + ```bash + $PYTHON -m claude_code_hooks_daemon.daemon.cli check-config-migrations \ + --from ${from_version} --to ${to_version} + ``` + + Exit code `0` means nothing to surface — skip to the next step. Exit code `1` + means there are suggestions. Read them: + + - Anything under **🆕 Recommended — enable these** is a feature the daemon + recommends turning on. The output shows the key, the recommended value, and + your current value. To adopt one, set that key/value in + `.claude/hooks-daemon.yaml`. + - If a recommendation carries a migration **Note** (e.g. "migrate existing + memory into tracked docs first"), perform that migration **before** + enabling — follow any referenced post-upgrade task. + - Items under **💡 New Options Available** are informational; adopt if useful. + + This is advisory — enabling is your choice; the daemon never edits your config + for you. Stage and commit any `.claude/hooks-daemon.yaml` edits separately + from the daemon upgrade commit below. + +6. **Stage daemon-owned paths ONLY** with explicit `git add` — other working-tree changes are not part of this commit. Never `git add .`: ```bash @@ -33,7 +90,7 @@ Upgrade the Claude Code Hooks Daemon and commit the result atomically. .claude/settings.json ``` -5. **Commit** with the metadata block in the body: +7. **Commit** with the metadata block in the body: ``` hooks daemon upgrade: ${from_version} → ${to_version} diff --git a/.claude/skills/planning/SKILL.md b/.claude/skills/planning/SKILL.md index 2206fe0d..6ee1c857 100644 --- a/.claude/skills/planning/SKILL.md +++ b/.claude/skills/planning/SKILL.md @@ -33,6 +33,7 @@ find CLAUDE/Plan -maxdepth 2 -type d -name '[0-9]*' | grep -oP '/\K\d{3}(?=-)' | See @CLAUDE/PlanWorkflow.md for full template. Key sections: + - Overview - Goals / Non-Goals - Tasks (with checkboxes and status icons) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc24ab37..0c3e758d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '22' cache: 'npm' - name: Install dependencies @@ -35,6 +35,18 @@ jobs: env: VITE_CONTACT_FORM_URL: ${{ vars.VITE_CONTACT_FORM_URL }} + # QA runs after build, not before: ts-qa's Playwright phase serves the real + # dist/ output (via `npm run preview`) rather than a mocked/dev server, so the + # build has to exist first. Everything else in the pipeline (lint/type-check/ + # unit tests) doesn't depend on dist/ but there's no cost to running it after - + # GITHUB_ACTIONS=true puts ts-qa in read-only mode automatically (check-only, + # never auto-fixes/commits in CI). + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium + + - name: Run QA pipeline + run: npx ts-qa + - name: Upload Pages artifact if: github.ref == 'refs/heads/main' uses: actions/upload-pages-artifact@v3 @@ -54,4 +66,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 \ No newline at end of file + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index e94cdb5c..ba917b15 100644 --- a/.gitignore +++ b/.gitignore @@ -33,9 +33,21 @@ npm-debug.log* # Claude Code Hooks Daemon .claude/hooks-daemon/ +# Git worktrees (isolated agent branches) +.claude/worktrees + +# Claude Code Hooks Daemon - scheduled task lock file +.claude/scheduled_tasks.lock + # ClaudeMdInjector backup (session artifact, never commit) .CLAUDE.md.pre-inject # Python bytecode caches (project-handlers) __pycache__/ *.pyc + +# Playwright +test-results/ +playwright-report/ +blob-report/ +playwright/.cache/ diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..2a15d2fd --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +dist +dist-server +node_modules +code-snippets +untracked +ARCHIVE +var +.claude +cloudflare-workers +package-lock.json +src/data/snippets.ts diff --git a/ARCHIVE/README.md b/ARCHIVE/README.md index aec96c11..1b10b430 100644 --- a/ARCHIVE/README.md +++ b/ARCHIVE/README.md @@ -37,6 +37,7 @@ This directory contains the complete legacy build system for the LTS Commerce st ## Why Archived? The site is being migrated to React/TypeScript for: + - **Type safety**: Zero runtime type errors - **Component reusability**: Reduce duplication - **Better DX**: TypeScript + ESLint + modern tooling @@ -45,6 +46,7 @@ The site is being migrated to React/TypeScript for: ## Technology Comparison ### Legacy (Archived) + ``` EJS Templates → Custom Preprocessing → Vite → Static HTML - ✅ Simple, no framework overhead @@ -54,6 +56,7 @@ EJS Templates → Custom Preprocessing → Vite → Static HTML ``` ### New (React) + ``` React Components → TypeScript → Vite → Static HTML (SSG) - ✅ Type-safe throughout @@ -69,6 +72,7 @@ All articles and content from `private_html/articles/` are being migrated to the ## Reference This archive remains for: + - **Content reference**: When migrating articles - **Design reference**: Reviewing layout decisions - **Build reference**: Understanding previous approach @@ -82,18 +86,21 @@ This archive remains for: ## How to Use This Archive ### Extract Article Content + ```bash # Articles are in ARCHIVE/private_html/articles/ ls ARCHIVE/private_html/articles/ ``` ### View Original Styles + ```bash # CSS files in ARCHIVE/private_html/css/ cat ARCHIVE/private_html/css/main.css ``` ### Review Build Scripts + ```bash # Build utilities in ARCHIVE/scripts/ cat ARCHIVE/scripts/process-ejs.js @@ -104,6 +111,7 @@ cat ARCHIVE/scripts/process-ejs.js ⚠️ **This system is archived and should NOT be used for new development.** All new work follows: + - React/TypeScript architecture - react-site-skeleton patterns - PlanWorkflow system diff --git a/ARCHIVE/private_html/articles/CLAUDE.md b/ARCHIVE/private_html/articles/CLAUDE.md index 72a20a0f..7d22a9d1 100644 --- a/ARCHIVE/private_html/articles/CLAUDE.md +++ b/ARCHIVE/private_html/articles/CLAUDE.md @@ -18,6 +18,7 @@ This guide provides instructions for creating high-quality technical articles fo ### Research Requirements Before writing ANY technical content: + 1. **Check current version** - Visit npm, GitHub releases, official docs 2. **Verify features** - Confirm features exist in the current version 3. **Read recent updates** - Check blog posts from the last 3-6 months @@ -27,6 +28,7 @@ Before writing ANY technical content: ### Research Tools Use these tools for EVERY article: + - `WebSearch` - Find current documentation and recent articles - `WebFetch` - Read official docs, GitHub pages, and release notes - Cross-reference multiple sources @@ -45,6 +47,7 @@ cp private_html/articles/php-magic-constants-maintainable-logging.ejs private_ht ``` The `_TEMPLATE-ARTICLE.ejs` includes: + - Complete structure with comments explaining each section - Code snippet examples with proper directory path usage - Extensive linking examples @@ -73,6 +76,7 @@ The `_TEMPLATE-ARTICLE.ejs` includes: **ALWAYS use the snippet injection system for ALL code examples, no matter how small.** Embedding code directly in EJS templates causes catastrophic rendering failures: + - Unescaped HTML characters (`<`, `>`, `&`, `` tags on a new line **Examples of what to put in separate snippet files**: + - ✅ Complete class definitions - ✅ Function examples (even single functions) - ✅ Configuration file excerpts @@ -97,26 +102,33 @@ Embedding code directly in EJS templates causes catastrophic rendering failures: **Code Examples Requirements - Audience-Dependent**: ### Developer-Focused Technical Articles + **MANDATORY** - Include examples in ALL of these languages **IN THIS ORDER**: + 1. **Pseudocode** (`.txt` files) - Conceptual explanation first -2. **PHP** (`.php` files) - Current 8.4 syntax and best practices +2. **PHP** (`.php` files) - Current 8.4 syntax and best practices 3. **TypeScript** (`.ts` files) - Modern Node.js/TypeScript examples 4. **Ansible** (`.yml` files) - Infrastructure automation playbooks 5. **Bash** (`.sh` files) - Shell scripting with error handling ### Executive/Strategic Articles (C-level, Business Focus) + **OPTIONAL** - Code examples should be: + - **Minimal or none** - Focus on business concepts, ROI, strategic value - **High-level conceptual** - If code is needed, use simple pseudocode or architecture diagrams - **Business-relevant** - Only include code that directly supports business decision-making ### Mixed Technical/Business Articles + **SELECTIVE** - Include code examples that: + - **Support business points** - Code serves to illustrate business concepts - **1-3 languages maximum** - Choose most relevant to the business context - **Focus on outcomes** - Emphasize results rather than implementation details **Directory Structure Example**: + ``` code-snippets/ ├── defensive-programming-principles/ @@ -133,11 +145,13 @@ code-snippets/ ``` **Pseudocode File Extension Rule**: + - **ALWAYS use `.txt` extension** for pseudocode files to clearly indicate they are conceptual, not real Python code - Use `language-python` for syntax highlighting, but content must be language-agnostic pseudocode - Examples: `CLASS UserService`, `METHOD validate() -> boolean`, `IF condition THEN action` **Article Template Usage**: + ```html
{{SNIPPET:article-slug/concept-pseudocode.txt}}
@@ -153,11 +167,13 @@ code-snippets/
 ```
 
 **Critical Rules**:
+
 - **Closing tags MUST be on a new line**: If they're on the same line as the last line of code (especially comments), they'll be treated as part of the code
 - **Use full directory paths**: Always include the article directory name in snippet references
 - **Match directory names**: Code snippet directory should match article slug (kebab-case)
 
 **HTML Escaping**: The build process automatically escapes HTML entities in code snippets:
+
 - `<` becomes `<`
 - `>` becomes `>`
 - `&` becomes `&`
@@ -200,12 +216,14 @@ git push origin main # Triggers auto-deployment
 - **Current information** - Check that linked resources are up-to-date
 
 **CRITICAL**: Training data is often months or years out of date. For example:
+
 - Framework versions change frequently (e.g., oclif v4 released June 2024)
 - Best practices evolve rapidly
 - Tool features are added monthly
 - Security recommendations change
 
 Always use WebSearch and WebFetch to verify:
+
 - Current version numbers
 - Latest features and updates
 - Recent breaking changes
@@ -215,6 +233,7 @@ Always use WebSearch and WebFetch to verify:
 ### Research Process Example
 
 **BAD** (using training knowledge):
+
 ```
 "The latest version includes new features like..."
 "This framework recently added support for..."
@@ -222,6 +241,7 @@ Always use WebSearch and WebFetch to verify:
 ```
 
 **GOOD** (after research):
+
 ```
 "Version X.Y.Z (released [exact date from GitHub]) includes..."
 "According to npm, the current version published [days] ago is..."
@@ -231,73 +251,124 @@ Always use WebSearch and WebFetch to verify:
 Examples of well-linked content:
 
 **Basic linking**:
+
 ```html
 

- Oclif is an - open-source framework for building command-line interfaces in - Node.js and - TypeScript. + Oclif is an open-source framework + for building command-line interfaces in + Node.js and + TypeScript.

``` **Language feature linking (MANDATORY)**: + ```html

- PHP 8.4 introduces property hooks - and asymmetric visibility. - Final classes - prevent inheritance, encouraging composition over inheritance. + PHP 8.4 introduces + property hooks + and + asymmetric visibility. + Final classes + prevent inheritance, encouraging composition over inheritance.

- TypeScript's union types - and branded types - provide stronger type safety than traditional approaches. + TypeScript's + union types + and + branded types + provide stronger type safety than traditional approaches.

``` **Comprehensive linking**: + ```html

- PHP has converged around PSR-11 Container Interface, - with most frameworks implementing compatible containers. TypeScript? It's the Wild West. + PHP has converged around + PSR-11 Container Interface, with most frameworks implementing compatible containers. TypeScript? It's the Wild West.

-

InversifyJS

+

+ InversifyJS +

``` **Even inline code elements can be linked**: + ```html

- In PHP, you might use final to prevent inheritance + In PHP, you might use + final + to prevent inheritance

``` ### 2. Content Structure #### Opening Section -- **Lead paragraph** in `

` + +- **Lead paragraph** in `

` - Hook the reader with the problem being solved - Establish credibility and scope #### Body Sections + - Use `

` tags for major topics - Hierarchical headings: `

` for main sections, `

` for subsections - One idea per paragraph - Use lists for multiple related points #### Code Examples + - Always use appropriate language classes: `language-php`, `language-javascript`, etc. - Provide context before code blocks - Keep examples practical and runnable - Comment complex sections #### Conclusion + - Summarize key takeaways - Provide actionable next steps - Avoid generic endings @@ -305,12 +376,14 @@ Examples of well-linked content: ### 3. Writing Style #### Technical Accuracy + - **No fabrication** - Never invent case studies or metrics - **Practical focus** - Real-world applications over theory - **Honest assessment** - Include limitations and drawbacks - **Balanced perspective** - Pros AND cons for tools/approaches #### Tone and Voice + - **Professional** but approachable - **Confident** without being arrogant - **Instructive** rather than prescriptive @@ -325,6 +398,7 @@ Examples of well-linked content: - **NO "in conclusion"** - just conclude #### Formatting Standards + - **Links**: All external links must include `target="_blank" rel="noopener"` - **Emphasis**: Use `` for important terms, not just bold - **Lists**: Use `
    ` for unordered, `
      ` for sequential steps @@ -340,6 +414,7 @@ Examples of well-linked content: ### 5. Categories Available categories and their focus: + - **php** - PHP development, frameworks, best practices - **infrastructure** - DevOps, hosting, deployment, automation - **database** - MySQL, PostgreSQL, optimization, architecture @@ -349,6 +424,7 @@ Available categories and their focus: #### Adding a New Category 1. **Add category to `private_html/data/categories.json`**: + ```json "newcategory": { "label": "New Category", @@ -363,6 +439,7 @@ Available categories and their focus: 3. **Build the site** - Category styles are auto-generated and filters appear automatically! The system will: + - Validate categories during article registration - Generate CSS styles automatically - Create filter buttons dynamically @@ -375,7 +452,8 @@ Before publishing, verify: **Code Requirements (Audience-Dependent):** **For Developer-Focused Articles:** -- [ ] **PSEUDOCODE FIRST**: Every concept starts with language-agnostic pseudocode explanation + +- [ ] **PSEUDOCODE FIRST**: Every concept starts with language-agnostic pseudocode explanation - [ ] **PSEUDOCODE FILE EXTENSION**: All pseudocode files use `.txt` extension (not `.py`) - [ ] **PRESENTATION ORDER**: Pseudocode (.txt) → PHP → TypeScript → Ansible → Bash - [ ] **PHP examples** using current 8.4 syntax and best practices (shown after pseudocode) @@ -385,22 +463,26 @@ Before publishing, verify: - [ ] All examples demonstrate the same concepts across different implementation approaches **For Executive/Strategic Articles:** + - [ ] **Minimal or no code** - Focus on business value, ROI, strategic implications - [ ] **High-level concepts only** - Use architectural diagrams, flowcharts, or simple pseudocode if needed - [ ] **Business-relevant examples** - Only include technical details that support business decisions **For Mixed Technical/Business Articles:** + - [ ] **Selective code examples** - 1-3 languages maximum, chosen for business relevance - [ ] **Focus on outcomes** - Emphasize business results over implementation details - [ ] **Support business narrative** - Code serves to illustrate strategic points **Code Snippet System (CRITICAL):** + - [ ] **🚨 NO EMBEDDED CODE** - Zero code snippets embedded directly in EJS template - [ ] **ALL code in separate files** - Every single code example uses `{{SNIPPET:...}}` placeholder - [ ] **Snippet directory created** - `code-snippets/article-slug/` exists with all code files - [ ] **Closing tags on new lines** - All `

` tags are on their own line **Content Quality:** + - [ ] **Current research completed** - All version numbers and features verified through official sources - [ ] **No training data used** - Everything fact-checked with real-time research - [ ] **Dates are current** - No references to "recently" or "latest" without specific dates @@ -425,48 +507,63 @@ Before publishing, verify: ### 7. Example Article Sections #### Well-Researched Tool Comparison + ```html -

Commander.js

+

+ Commander.js +

The lightweight choice for simple CLIs:

    -
  • Minimal learning curve
  • -
  • Small footprint (only 12KB gzipped)
  • -
  • Great for basic scripts
  • -
  • Limited plugin support
  • +
  • Minimal learning curve
  • +
  • + Small footprint (only + 12KB gzipped) +
  • +
  • Great for basic scripts
  • +
  • Limited plugin support
``` #### Resource Section + ```html

Official Resources

``` ## Article Types ### 1. Technical Deep Dives + - Comprehensive exploration of a technology - Include architecture, best practices, examples - Compare with alternatives - Real-world use cases ### 2. Practical Guides + - Step-by-step implementation - Complete, working code examples - Common pitfalls and solutions - Performance considerations ### 3. Tool Comparisons + - Objective analysis of competing solutions - Benchmarks where applicable - Decision matrices - Specific use case recommendations ### 4. Best Practices + - Industry-standard approaches - Code organization patterns - Security considerations @@ -487,19 +584,22 @@ Before publishing, verify: ### Example: Writing About Any Technical Tool **❌ WRONG (Using Training Data)**: + ``` -"[Framework] recently released version 3 with [feature]. The latest update +"[Framework] recently released version 3 with [feature]. The latest update includes new features like [feature that might be outdated]..." ``` **✅ CORRECT (After Research)**: + ``` -"[Framework] version [X.Y.Z] (released [exact date]) is the current stable release. -Version [X], released in [month year], brought [specific feature] with [details]. +"[Framework] version [X.Y.Z] (released [exact date]) is the current stable release. +Version [X], released in [month year], brought [specific feature] with [details]. The latest updates include [feature] (v[X.Y.Z]) and [feature] (v[X.Y.Z])..." ``` The difference: + - Training data had outdated version info - Might miss entire major version releases - Vague timeline ("recent" vs specific dates) @@ -509,6 +609,7 @@ The difference: ## Final Notes The goal is to create articles that: + - Establish technical authority through accuracy and depth - Provide genuine value to experienced developers - Stand out through comprehensive research and practical insights @@ -518,4 +619,4 @@ Remember: Quality over quantity. One thoroughly researched, well-linked article --- -*Last Updated: 2025-07-22* \ No newline at end of file +_Last Updated: 2025-07-22_ diff --git a/ARCHIVE/private_html/css/about.css b/ARCHIVE/private_html/css/about.css index d4571659..6c5f98c1 100644 --- a/ARCHIVE/private_html/css/about.css +++ b/ARCHIVE/private_html/css/about.css @@ -1,165 +1,165 @@ /* About Page Specific Styles */ .about-hero { - padding: var(--spacing-xl) 0 var(--spacing-xxl); - background-color: var(--color-bg-secondary); + padding: var(--spacing-xl) 0 var(--spacing-xxl); + background-color: var(--color-bg-secondary); } .about-intro { - max-width: 800px; - margin: 0 auto; - text-align: center; + max-width: 800px; + margin: 0 auto; + text-align: center; } .lead-text { - font-size: 1.25rem; - line-height: 1.7; - color: var(--color-text-light); - margin-top: var(--spacing-md); + font-size: 1.25rem; + line-height: 1.7; + color: var(--color-text-light); + margin-top: var(--spacing-md); } .about-content { - padding: var(--spacing-xxl) 0; + padding: var(--spacing-xxl) 0; } .about-grid { - display: grid; - grid-template-columns: 1fr 350px; - gap: var(--spacing-xxl); - margin-bottom: var(--spacing-xxl); + display: grid; + grid-template-columns: 1fr 350px; + gap: var(--spacing-xxl); + margin-bottom: var(--spacing-xxl); } .about-main h2 { - font-size: 1.75rem; - margin-top: var(--spacing-xl); - margin-bottom: var(--spacing-md); - color: var(--color-text); + font-size: 1.75rem; + margin-top: var(--spacing-xl); + margin-bottom: var(--spacing-md); + color: var(--color-text); } .about-main h2:first-child { - margin-top: 0; + margin-top: 0; } .about-main p { - font-size: 1.0625rem; - line-height: 1.7; - color: var(--color-text-light); - margin-bottom: var(--spacing-md); + font-size: 1.0625rem; + line-height: 1.7; + color: var(--color-text-light); + margin-bottom: var(--spacing-md); } .expertise-list { - margin-top: var(--spacing-lg); + margin-top: var(--spacing-lg); } .expertise-item { - margin-bottom: var(--spacing-lg); - padding-left: var(--spacing-md); - border-left: 3px solid var(--color-primary); + margin-bottom: var(--spacing-lg); + padding-left: var(--spacing-md); + border-left: 3px solid var(--color-primary); } .expertise-item h3 { - font-size: 1.25rem; - margin-bottom: var(--spacing-xs); - color: var(--color-text); + font-size: 1.25rem; + margin-bottom: var(--spacing-xs); + color: var(--color-text); } .expertise-item p { - font-size: 1rem; - margin-bottom: 0; + font-size: 1rem; + margin-bottom: 0; } /* Sidebar */ .about-sidebar { - position: sticky; - top: calc(64px + var(--spacing-lg)); - height: fit-content; + position: sticky; + top: calc(64px + var(--spacing-lg)); + height: fit-content; } .skills-section, .certifications { - background-color: var(--color-bg-secondary); - padding: var(--spacing-lg); - border-radius: 8px; - margin-bottom: var(--spacing-lg); + background-color: var(--color-bg-secondary); + padding: var(--spacing-lg); + border-radius: 8px; + margin-bottom: var(--spacing-lg); } .skills-section h3, .certifications h3 { - font-size: 1.25rem; - margin-bottom: var(--spacing-md); - color: var(--color-text); + font-size: 1.25rem; + margin-bottom: var(--spacing-md); + color: var(--color-text); } .skills-grid { - display: grid; - gap: var(--spacing-md); + display: grid; + gap: var(--spacing-md); } .skill-category h4 { - font-size: 0.875rem; - font-weight: 600; - color: var(--color-primary); - text-transform: uppercase; - margin-bottom: var(--spacing-xs); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-primary); + text-transform: uppercase; + margin-bottom: var(--spacing-xs); } .skill-category ul, .certifications ul { - list-style: none; + list-style: none; } .skill-category li, .certifications li { - font-size: 0.875rem; - color: var(--color-text-light); - padding: 0.25rem 0; + font-size: 0.875rem; + color: var(--color-text-light); + padding: 0.25rem 0; } .certifications li { - padding: 0.5rem 0; - border-bottom: 1px solid var(--color-border); + padding: 0.5rem 0; + border-bottom: 1px solid var(--color-border); } .certifications li:last-child { - border-bottom: none; + border-bottom: none; } /* CTA Section */ .cta-section { - text-align: center; - padding: var(--spacing-xxl); - background-color: var(--color-bg-secondary); - border-radius: 8px; + text-align: center; + padding: var(--spacing-xxl); + background-color: var(--color-bg-secondary); + border-radius: 8px; } .cta-section h2 { - font-size: 2rem; - margin-bottom: var(--spacing-sm); + font-size: 2rem; + margin-bottom: var(--spacing-sm); } .cta-section p { - font-size: 1.125rem; - color: var(--color-text-light); - margin-bottom: var(--spacing-lg); + font-size: 1.125rem; + color: var(--color-text-light); + margin-bottom: var(--spacing-lg); } /* Responsive */ @media (max-width: 968px) { - .about-grid { - grid-template-columns: 1fr; - } - - .about-sidebar { - position: static; - } - - .skills-grid { - grid-template-columns: repeat(2, 1fr); - } + .about-grid { + grid-template-columns: 1fr; + } + + .about-sidebar { + position: static; + } + + .skills-grid { + grid-template-columns: repeat(2, 1fr); + } } @media (max-width: 480px) { - .skills-grid { - grid-template-columns: 1fr; - } -} \ No newline at end of file + .skills-grid { + grid-template-columns: 1fr; + } +} diff --git a/ARCHIVE/private_html/css/articles.css b/ARCHIVE/private_html/css/articles.css index d12001b3..b8df6e3e 100644 --- a/ARCHIVE/private_html/css/articles.css +++ b/ARCHIVE/private_html/css/articles.css @@ -5,1042 +5,1033 @@ /* Pagination Styles */ .pagination-nav { - margin: var(--spacing-xl) 0; - display: flex; - flex-direction: column; - align-items: center; - gap: var(--spacing-md); + margin: var(--spacing-xl) 0; + display: flex; + flex-direction: column; + align-items: center; + gap: var(--spacing-md); } .pagination-list { - display: flex; - align-items: center; - gap: var(--spacing-xs); - list-style: none; - margin: 0; - padding: 0; + display: flex; + align-items: center; + gap: var(--spacing-xs); + list-style: none; + margin: 0; + padding: 0; } .pagination-link { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 44px; - height: 44px; - padding: var(--spacing-sm) var(--spacing-md); - border: 1px solid var(--color-border); - border-radius: 8px; - background: var(--color-bg); - color: var(--color-text); - text-decoration: none; - font-weight: 500; - font-size: 0.9rem; - transition: all 0.2s ease; - cursor: pointer; - box-shadow: var(--shadow-dynamic-tier1); + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 44px; + height: 44px; + padding: var(--spacing-sm) var(--spacing-md); + border: 1px solid var(--color-border); + border-radius: 8px; + background: var(--color-bg); + color: var(--color-text); + text-decoration: none; + font-weight: 500; + font-size: 0.9rem; + transition: all 0.2s ease; + cursor: pointer; + box-shadow: var(--shadow-dynamic-tier1); } .pagination-link:hover { - border-color: var(--color-primary); - background: var(--color-primary-light); - color: var(--color-primary); + border-color: var(--color-primary); + background: var(--color-primary-light); + color: var(--color-primary); } .pagination-link.pagination-current { - background: var(--color-primary); - border-color: var(--color-primary); - color: white; - cursor: default; + background: var(--color-primary); + border-color: var(--color-primary); + color: white; + cursor: default; } .pagination-link.pagination-current:hover { - transform: none; + transform: none; } .pagination-ellipsis { - padding: var(--spacing-sm); - color: var(--color-text-muted); - font-weight: 500; + padding: var(--spacing-sm); + color: var(--color-text-muted); + font-weight: 500; } .pagination-prev, .pagination-next { - font-weight: 600; + font-weight: 600; } .pagination-info { - font-size: 0.9rem; - color: var(--color-text-muted); - text-align: center; + font-size: 0.9rem; + color: var(--color-text-muted); + text-align: center; } /* Dynamic pagination specific styles */ .dynamic-pagination { - border-top: 1px solid var(--color-border-light); - padding-top: var(--spacing-lg); + border-top: 1px solid var(--color-border-light); + padding-top: var(--spacing-lg); } .dynamic-pagination .pagination-nav { - margin: var(--spacing-lg) 0; + margin: var(--spacing-lg) 0; } .pagination-top { - border-bottom: 1px solid var(--color-border-light); - padding-bottom: var(--spacing-lg); - margin-bottom: var(--spacing-xl); + border-bottom: 1px solid var(--color-border-light); + padding-bottom: var(--spacing-lg); + margin-bottom: var(--spacing-xl); } .pagination-bottom { - border-top: 1px solid var(--color-border-light); - padding-top: var(--spacing-lg); - margin-top: var(--spacing-xl); + border-top: 1px solid var(--color-border-light); + padding-top: var(--spacing-lg); + margin-top: var(--spacing-xl); } /* Filter help text */ .filter-help { - font-size: 0.85rem; - color: var(--color-text-muted); - text-align: center; - margin-top: var(--spacing-sm); - font-style: italic; + font-size: 0.85rem; + color: var(--color-text-muted); + text-align: center; + margin-top: var(--spacing-sm); + font-style: italic; } /* Mobile pagination */ @media (max-width: 768px) { - .pagination-list { - gap: var(--spacing-xxs); - } - - .pagination-link { - min-width: 40px; - height: 40px; - padding: var(--spacing-xs) var(--spacing-sm); - font-size: 0.85rem; - } - - .pagination-ellipsis { - padding: var(--spacing-xs); - } - - /* Hide page numbers on very small screens, keep prev/next */ - @media (max-width: 480px) { - .pagination-link:not(.pagination-prev):not(.pagination-next):not(.pagination-current) { - display: none; - } + .pagination-list { + gap: var(--spacing-xxs); + } + + .pagination-link { + min-width: 40px; + height: 40px; + padding: var(--spacing-xs) var(--spacing-sm); + font-size: 0.85rem; + } + + .pagination-ellipsis { + padding: var(--spacing-xs); + } + + /* Hide page numbers on very small screens, keep prev/next */ + @media (max-width: 480px) { + .pagination-link:not(.pagination-prev):not(.pagination-next):not(.pagination-current) { + display: none; } + } } .articles-section { - padding: var(--spacing-xxl) 0; + padding: var(--spacing-xxl) 0; } /* Individual Article Styles with Mathematical Precision */ .article-container { - max-width: 800px; - margin: 0 auto; - padding: var(--spacing-xl) var(--spacing-md); - line-height: var(--golden-ratio); - position: relative; + max-width: 800px; + margin: 0 auto; + padding: var(--spacing-xl) var(--spacing-md); + line-height: var(--golden-ratio); + position: relative; } .article-header { - margin-bottom: var(--spacing-xl); - text-align: center; - border-bottom: 1px solid rgba(229, 231, 235, 0.6); - padding-bottom: var(--spacing-lg); - position: relative; - animation: fadeInUp 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; - opacity: 0; - transform: translateY(20px); + margin-bottom: var(--spacing-xl); + text-align: center; + border-bottom: 1px solid rgba(229, 231, 235, 0.6); + padding-bottom: var(--spacing-lg); + position: relative; + animation: fadeInUp 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; + opacity: 0; + transform: translateY(20px); } @keyframes fadeInUp { - to { - opacity: 1; - transform: translateY(0); - } + to { + opacity: 1; + transform: translateY(0); + } } .article-header::after { - content: ''; - position: absolute; - bottom: -1px; - left: 50%; - width: 60px; - height: 2px; - background: linear-gradient(90deg, - var(--color-primary), - var(--color-primary-light) - ); - transform: translateX(-50%); - border-radius: 1px; + content: ''; + position: absolute; + bottom: -1px; + left: 50%; + width: 60px; + height: 2px; + background: linear-gradient(90deg, var(--color-primary), var(--color-primary-light)); + transform: translateX(-50%); + border-radius: 1px; } .article-header h1 { - font-size: 2.5rem; - font-weight: 700; - line-height: var(--golden-line-height); - margin-bottom: var(--spacing-md); - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - /* Mouse-responsive drop shadow */ - filter: drop-shadow(var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.15)); - position: relative; - word-wrap: break-word; - overflow-wrap: break-word; - hyphens: none; + font-size: 2.5rem; + font-weight: 700; + line-height: var(--golden-line-height); + margin-bottom: var(--spacing-md); + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + /* Mouse-responsive drop shadow */ + filter: drop-shadow(var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.15)); + position: relative; + word-wrap: break-word; + overflow-wrap: break-word; + hyphens: none; } .article-meta { - display: flex; - justify-content: center; - align-items: center; - gap: var(--spacing-sm); - font-size: 0.875rem; - color: var(--color-text-light); + display: flex; + justify-content: center; + align-items: center; + gap: var(--spacing-sm); + font-size: 0.875rem; + color: var(--color-text-light); } .article-share { - margin: 0; - display: flex; - justify-content: center; - gap: var(--spacing-sm); - position: sticky; - top: 60px; - z-index: 100; - background: var(--color-bg); - padding: var(--spacing-sm) 0; + margin: 0; + display: flex; + justify-content: center; + gap: var(--spacing-sm); + position: sticky; + top: 60px; + z-index: 100; + background: var(--color-bg); + padding: var(--spacing-sm) 0; } .share-link { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 8px 16px; - border: 1px solid var(--color-border); - border-radius: 6px; - background: var(--color-bg); - color: var(--color-text); - text-decoration: none; - font-size: 0.875rem; - font-weight: 500; - transition: all 0.2s ease; - cursor: pointer; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 16px; + border: 1px solid var(--color-border); + border-radius: 6px; + background: var(--color-bg); + color: var(--color-text); + text-decoration: none; + font-size: 0.875rem; + font-weight: 500; + transition: all 0.2s ease; + cursor: pointer; } .share-link:hover { - border-color: var(--color-primary); - background: var(--color-primary-light); - color: var(--color-primary); - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border-color: var(--color-primary); + background: var(--color-primary-light); + color: var(--color-primary); + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } .share-link svg { - width: 16px; - height: 16px; + width: 16px; + height: 16px; } .hn-share:hover { - border-color: #ff6600; - background: rgba(255, 102, 0, 0.1); - color: #ff6600; + border-color: #ff6600; + background: rgba(255, 102, 0, 0.1); + color: #ff6600; } .reddit-share:hover { - border-color: #ff4500; - background: rgba(255, 69, 0, 0.1); - color: #ff4500; + border-color: #ff4500; + background: rgba(255, 69, 0, 0.1); + color: #ff4500; } .lobsters-share:hover { - border-color: #c00; - background: rgba(204, 0, 0, 0.1); - color: #c00; + border-color: #c00; + background: rgba(204, 0, 0, 0.1); + color: #c00; } .article-meta .separator { - opacity: 0.5; + opacity: 0.5; } .article-meta .category { - background: linear-gradient(135deg, - var(--color-primary) 0%, - var(--color-primary-light) 100% - ); - color: white; - padding: 0.25rem 0.75rem; - border-radius: 20px; - font-size: 0.75rem; - font-weight: 500; - text-transform: uppercase; - box-shadow: var(--shadow-subtle); - position: relative; - overflow: hidden; + background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%); + color: white; + padding: 0.25rem 0.75rem; + border-radius: 20px; + font-size: 0.75rem; + font-weight: 500; + text-transform: uppercase; + box-shadow: var(--shadow-subtle); + position: relative; + overflow: hidden; } .article-meta .category::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, - transparent, - rgba(255, 255, 255, 0.3), - transparent - ); - transition: var(--transition-elegant); + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); + transition: var(--transition-elegant); } .article-meta .category:hover::before { - left: 100%; + left: 100%; } .intro { - margin-bottom: var(--spacing-xl); - animation: fadeInUp 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s forwards; - opacity: 0; - transform: translateY(20px); + margin-bottom: var(--spacing-xl); + animation: fadeInUp 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s forwards; + opacity: 0; + transform: translateY(20px); } .intro::before { - content: ''; - position: absolute; - left: 0; - top: 0; - width: 4px; - height: 100%; - background: linear-gradient(180deg, - var(--color-primary) 0%, - var(--color-primary-light) 100% - ); - border-radius: 2px; - opacity: 0.3; + content: ''; + position: absolute; + left: 0; + top: 0; + width: 4px; + height: 100%; + background: linear-gradient(180deg, var(--color-primary) 0%, var(--color-primary-light) 100%); + border-radius: 2px; + opacity: 0.3; } .intro .lead { - font-size: calc(1rem * var(--golden-ratio)); - font-weight: 400; - line-height: var(--golden-ratio); - color: var(--color-text); - margin-bottom: var(--spacing-lg); - position: relative; - padding-left: var(--spacing-md); - font-feature-settings: "kern" 1, "liga" 1, "swsh" 1; + font-size: calc(1rem * var(--golden-ratio)); + font-weight: 400; + line-height: var(--golden-ratio); + color: var(--color-text); + margin-bottom: var(--spacing-lg); + position: relative; + padding-left: var(--spacing-md); + font-feature-settings: + 'kern' 1, + 'liga' 1, + 'swsh' 1; } .article-container section { - margin-bottom: var(--spacing-xl); - animation: fadeInUp 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; - opacity: 0; - transform: translateY(15px); + margin-bottom: var(--spacing-xl); + animation: fadeInUp 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; + opacity: 0; + transform: translateY(15px); } .article-container section:nth-child(even) { - animation-delay: 0.1s; + animation-delay: 0.1s; } .article-container section:nth-child(odd) { - animation-delay: 0.2s; + animation-delay: 0.2s; } .article-container h2 { - font-size: calc(1rem * var(--golden-ratio) * var(--golden-ratio)); - font-weight: 600; - color: var(--color-text); - margin-bottom: var(--spacing-md); - margin-top: var(--spacing-xl); - line-height: var(--golden-line-height); - position: relative; - padding-left: var(--spacing-md); - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - /* Mouse-responsive drop shadow (subtler for h2) */ - filter: drop-shadow(calc(var(--shadow-x) * 0.8) calc(var(--shadow-y) * 0.8) calc(var(--shadow-blur) * 0.9) rgba(0, 0, 0, 0.12)); + font-size: calc(1rem * var(--golden-ratio) * var(--golden-ratio)); + font-weight: 600; + color: var(--color-text); + margin-bottom: var(--spacing-md); + margin-top: var(--spacing-xl); + line-height: var(--golden-line-height); + position: relative; + padding-left: var(--spacing-md); + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + /* Mouse-responsive drop shadow (subtler for h2) */ + filter: drop-shadow( + calc(var(--shadow-x) * 0.8) calc(var(--shadow-y) * 0.8) calc(var(--shadow-blur) * 0.9) + rgba(0, 0, 0, 0.12) + ); } .article-container h2::before { - content: ''; - position: absolute; - left: 0; - top: 50%; - width: 3px; - height: 60%; - background: linear-gradient(180deg, - var(--color-primary) 0%, - var(--color-primary-light) 100% - ); - border-radius: 2px; - transform: translateY(-50%); - opacity: 0.7; + content: ''; + position: absolute; + left: 0; + top: 50%; + width: 3px; + height: 60%; + background: linear-gradient(180deg, var(--color-primary) 0%, var(--color-primary-light) 100%); + border-radius: 2px; + transform: translateY(-50%); + opacity: 0.7; } .article-container h3 { - font-size: calc(1rem * var(--golden-ratio)); - font-weight: 600; - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - margin-bottom: var(--spacing-sm); - margin-top: var(--spacing-lg); - line-height: var(--golden-line-height); - position: relative; - padding-left: var(--spacing-sm); - /* Mouse-responsive drop shadow (very subtle for h3) */ - filter: drop-shadow(calc(var(--shadow-x) * 0.5) calc(var(--shadow-y) * 0.5) calc(var(--shadow-blur) * 0.7) rgba(0, 0, 0, 0.08)); + font-size: calc(1rem * var(--golden-ratio)); + font-weight: 600; + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: var(--spacing-sm); + margin-top: var(--spacing-lg); + line-height: var(--golden-line-height); + position: relative; + padding-left: var(--spacing-sm); + /* Mouse-responsive drop shadow (very subtle for h3) */ + filter: drop-shadow( + calc(var(--shadow-x) * 0.5) calc(var(--shadow-y) * 0.5) calc(var(--shadow-blur) * 0.7) + rgba(0, 0, 0, 0.08) + ); } .article-container h3::before { - content: ''; - position: absolute; - left: 0; - top: 50%; - width: 2px; - height: 50%; - background: var(--color-primary-light); - border-radius: 1px; - transform: translateY(-50%); - opacity: 0.5; + content: ''; + position: absolute; + left: 0; + top: 50%; + width: 2px; + height: 50%; + background: var(--color-primary-light); + border-radius: 1px; + transform: translateY(-50%); + opacity: 0.5; } .article-container h4 { - font-size: 1.25rem; - font-weight: 600; - color: var(--color-text); - margin-bottom: var(--spacing-sm); - margin-top: var(--spacing-md); + font-size: 1.25rem; + font-weight: 600; + color: var(--color-text); + margin-bottom: var(--spacing-sm); + margin-top: var(--spacing-md); } .article-container p { - font-size: 1.1rem; - line-height: var(--golden-ratio); - margin-bottom: var(--spacing-md); - color: var(--color-text); - font-feature-settings: "kern" 1, "liga" 1; - text-align: justify; - hyphens: none; - transition: var(--transition-smooth); + font-size: 1.1rem; + line-height: var(--golden-ratio); + margin-bottom: var(--spacing-md); + color: var(--color-text); + font-feature-settings: + 'kern' 1, + 'liga' 1; + text-align: justify; + hyphens: none; + transition: var(--transition-smooth); } .article-container p:hover { - color: rgba(44, 44, 44, 0.9); + color: rgba(44, 44, 44, 0.9); } -.article-container ul, .article-container ol { - margin-bottom: var(--spacing-md); - padding-left: var(--spacing-lg); +.article-container ul, +.article-container ol { + margin-bottom: var(--spacing-md); + padding-left: var(--spacing-lg); } .article-container li { - font-size: 1.1rem; - line-height: 1.7; - margin-bottom: var(--spacing-xs); - color: var(--color-text); + font-size: 1.1rem; + line-height: 1.7; + margin-bottom: var(--spacing-xs); + color: var(--color-text); } .article-container li strong { - color: var(--color-primary); + color: var(--color-primary); } /* Table Styles */ .article-container table { - width: 100%; - margin: var(--spacing-lg) 0; - border-collapse: collapse; - background: var(--color-bg-white); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05), var(--shadow-dynamic-tier2); - border-radius: 8px; - overflow: hidden; - font-size: 1rem; + width: 100%; + margin: var(--spacing-lg) 0; + border-collapse: collapse; + background: var(--color-bg-white); + box-shadow: + 0 2px 4px rgba(0, 0, 0, 0.05), + var(--shadow-dynamic-tier2); + border-radius: 8px; + overflow: hidden; + font-size: 1rem; } .article-container table thead { - background: linear-gradient(135deg, #f5f5f7 0%, #f9f9fb 100%); - border-bottom: 2px solid var(--color-primary); + background: linear-gradient(135deg, #f5f5f7 0%, #f9f9fb 100%); + border-bottom: 2px solid var(--color-primary); } .article-container table th { - padding: var(--spacing-md); - text-align: left; - font-weight: 600; - color: var(--color-text); - letter-spacing: 0.02em; - font-size: 0.95rem; - text-transform: uppercase; + padding: var(--spacing-md); + text-align: left; + font-weight: 600; + color: var(--color-text); + letter-spacing: 0.02em; + font-size: 0.95rem; + text-transform: uppercase; } .article-container table tbody tr { - border-bottom: 1px solid rgba(229, 231, 235, 0.6); - transition: background-color 0.2s ease; + border-bottom: 1px solid rgba(229, 231, 235, 0.6); + transition: background-color 0.2s ease; } .article-container table tbody tr:last-child { - border-bottom: none; + border-bottom: none; } .article-container table tbody tr:hover { - background-color: rgba(139, 92, 246, 0.03); + background-color: rgba(139, 92, 246, 0.03); } .article-container table td { - padding: var(--spacing-md); - color: var(--color-text); - line-height: 1.6; + padding: var(--spacing-md); + color: var(--color-text); + line-height: 1.6; } .article-container table td strong { - color: var(--color-primary); - font-weight: 600; + color: var(--color-primary); + font-weight: 600; } /* Responsive table */ @media (max-width: 768px) { - .article-container table { - font-size: 0.9rem; - } - - .article-container table th, - .article-container table td { - padding: var(--spacing-sm); - } + .article-container table { + font-size: 0.9rem; + } + + .article-container table th, + .article-container table td { + padding: var(--spacing-sm); + } } .article-container blockquote { - border-left: 4px solid var(--color-primary); - padding: var(--spacing-md) var(--spacing-lg); - margin: var(--spacing-lg) 0; - background: linear-gradient(135deg, - var(--color-bg-secondary) 0%, - rgba(248, 249, 250, 0.7) 100% - ); - font-style: italic; - color: var(--color-text-light); - border-radius: 0 var(--radius-md) var(--radius-md) 0; - position: relative; - box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); - transition: var(--transition-smooth); + border-left: 4px solid var(--color-primary); + padding: var(--spacing-md) var(--spacing-lg); + margin: var(--spacing-lg) 0; + background: linear-gradient(135deg, var(--color-bg-secondary) 0%, rgba(248, 249, 250, 0.7) 100%); + font-style: italic; + color: var(--color-text-light); + border-radius: 0 var(--radius-md) var(--radius-md) 0; + position: relative; + box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); + transition: var(--transition-smooth); } .article-container blockquote::before { - content: '“'; - position: absolute; - top: -10px; - left: var(--spacing-md); - font-size: 3rem; - color: var(--color-primary); - opacity: 0.3; - font-family: Georgia, serif; - line-height: 1; + content: '“'; + position: absolute; + top: -10px; + left: var(--spacing-md); + font-size: 3rem; + color: var(--color-primary); + opacity: 0.3; + font-family: Georgia, serif; + line-height: 1; } .article-container blockquote:hover { - box-shadow: var(--shadow-medium); + box-shadow: var(--shadow-medium); } /* Inline code styling - not for code blocks */ .article-container :not(pre) > code { - background: linear-gradient(135deg, - var(--color-bg-secondary) 0%, - rgba(248, 249, 250, 0.8) 100% - ); - padding: 0.125rem 0.375rem; - border-radius: var(--radius-sm); - font-family: 'Monaco', 'Consolas', 'Fira Code', monospace; - font-size: 0.9em; - color: var(--color-primary); - border: 1px solid rgba(229, 231, 235, 0.5); - transition: var(--transition-quick); - position: relative; + background: linear-gradient(135deg, var(--color-bg-secondary) 0%, rgba(248, 249, 250, 0.8) 100%); + padding: 0.125rem 0.375rem; + border-radius: var(--radius-sm); + font-family: 'Monaco', 'Consolas', 'Fira Code', monospace; + font-size: 0.9em; + color: var(--color-primary); + border: 1px solid rgba(229, 231, 235, 0.5); + transition: var(--transition-quick); + position: relative; } - .article-container pre { - /* Dark theme to match JetBrains Darcula */ - background: #2b2b2b; - padding: var(--spacing-md); - border-radius: var(--radius-lg); - overflow-x: auto; - margin: var(--spacing-lg) 0; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - position: relative; - transition: var(--transition-smooth); + /* Dark theme to match JetBrains Darcula */ + background: #2b2b2b; + padding: var(--spacing-md); + border-radius: var(--radius-lg); + overflow-x: auto; + margin: var(--spacing-lg) 0; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + position: relative; + transition: var(--transition-smooth); } .article-container pre::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: linear-gradient(90deg, - var(--color-primary) 0%, - var(--color-primary-light) 50%, - var(--color-primary) 100% - ); - border-radius: var(--radius-lg) var(--radius-lg) 0 0; + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient( + 90deg, + var(--color-primary) 0%, + var(--color-primary-light) 50%, + var(--color-primary) 100% + ); + border-radius: var(--radius-lg) var(--radius-lg) 0 0; } .article-container pre:hover { - box-shadow: var(--shadow-medium); + box-shadow: var(--shadow-medium); } .article-container pre code { - /* All styling handled by syntax-highlighting.css */ - background-color: transparent; - padding: 0; - font-size: 0.9rem; + /* All styling handled by syntax-highlighting.css */ + background-color: transparent; + padding: 0; + font-size: 0.9rem; } .article-container a { - color: var(--color-primary); - text-decoration: none; - transition: var(--transition); + color: var(--color-primary); + text-decoration: none; + transition: var(--transition); } .article-container a:hover { - color: var(--color-primary-dark); - text-decoration: underline; + color: var(--color-primary-dark); + text-decoration: underline; } .cta { - background: linear-gradient(135deg, - var(--color-bg-secondary) 0%, - rgba(248, 249, 250, 0.8) 100% - ); - padding: var(--spacing-xl); - border-radius: var(--radius-xl); - text-align: center; - margin-top: var(--spacing-xxl); - border: 1px solid rgba(229, 231, 235, 0.6); - box-shadow: var(--shadow-medium), var(--shadow-dynamic-tier3); - position: relative; - overflow: hidden; - transition: var(--transition-smooth); + background: linear-gradient(135deg, var(--color-bg-secondary) 0%, rgba(248, 249, 250, 0.8) 100%); + padding: var(--spacing-xl); + border-radius: var(--radius-xl); + text-align: center; + margin-top: var(--spacing-xxl); + border: 1px solid rgba(229, 231, 235, 0.6); + box-shadow: var(--shadow-medium), var(--shadow-dynamic-tier3); + position: relative; + overflow: hidden; + transition: var(--transition-smooth); } .cta::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: linear-gradient(90deg, - var(--color-primary) 0%, - var(--color-primary-light) 50%, - var(--color-primary) 100% - ); - border-radius: var(--radius-xl) var(--radius-xl) 0 0; + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient( + 90deg, + var(--color-primary) 0%, + var(--color-primary-light) 50%, + var(--color-primary) 100% + ); + border-radius: var(--radius-xl) var(--radius-xl) 0 0; } .cta:hover { - box-shadow: var(--shadow-elegant); + box-shadow: var(--shadow-elegant); } .cta h2 { - color: var(--color-primary); - margin-bottom: var(--spacing-md); + color: var(--color-primary); + margin-bottom: var(--spacing-md); } .cta p { - font-size: 1.1rem; - margin-bottom: 0; + font-size: 1.1rem; + margin-bottom: 0; } .footer-content { - text-align: center; - padding: var(--spacing-lg) 0; - border-top: 1px solid var(--color-border); + text-align: center; + padding: var(--spacing-lg) 0; + border-top: 1px solid var(--color-border); } .footer-links { - display: flex; - justify-content: center; - gap: var(--spacing-lg); - margin-top: var(--spacing-sm); + display: flex; + justify-content: center; + gap: var(--spacing-lg); + margin-top: var(--spacing-sm); } .footer-links a { - color: var(--color-text-light); - text-decoration: none; - transition: var(--transition); + color: var(--color-text-light); + text-decoration: none; + transition: var(--transition); } .footer-links a:hover { - color: var(--color-primary); + color: var(--color-primary); } @media (max-width: 768px) { - .article-container { - padding: var(--spacing-lg) var(--spacing-md); - } - - .article-header h1 { - font-size: 2rem; - line-height: var(--golden-line-height); - word-wrap: break-word; - overflow-wrap: break-word; - hyphens: none; - } - - .article-meta { - flex-direction: column; - gap: var(--spacing-xs); - } - - .article-container h2 { - font-size: 1.5rem; - } - - .article-container h3 { - font-size: 1.25rem; - } - - .article-container p, - .article-container li { - font-size: 1rem; - } - - .intro .lead { - font-size: 1.1rem; - } - - .footer-links { - flex-direction: column; - gap: var(--spacing-sm); - } + .article-container { + padding: var(--spacing-lg) var(--spacing-md); + } + + .article-header h1 { + font-size: 2rem; + line-height: var(--golden-line-height); + word-wrap: break-word; + overflow-wrap: break-word; + hyphens: none; + } + + .article-meta { + flex-direction: column; + gap: var(--spacing-xs); + } + + .article-container h2 { + font-size: 1.5rem; + } + + .article-container h3 { + font-size: 1.25rem; + } + + .article-container p, + .article-container li { + font-size: 1rem; + } + + .intro .lead { + font-size: 1.1rem; + } + + .footer-links { + flex-direction: column; + gap: var(--spacing-sm); + } } .search-container { - margin-bottom: var(--spacing-xl); + margin-bottom: var(--spacing-xl); } .search-input { - width: 100%; - padding: 0.875rem 1.25rem; - font-size: 1rem; - border: 2px solid var(--color-border); - border-radius: 8px; - transition: var(--transition); - font-family: var(--font-main); - margin-bottom: var(--spacing-md); - box-shadow: var(--shadow-dynamic-tier2); + width: 100%; + padding: 0.875rem 1.25rem; + font-size: 1rem; + border: 2px solid var(--color-border); + border-radius: 8px; + transition: var(--transition); + font-family: var(--font-main); + margin-bottom: var(--spacing-md); + box-shadow: var(--shadow-dynamic-tier2); } .search-input:focus { - outline: none; - border-color: var(--color-primary); + outline: none; + border-color: var(--color-primary); } .search-filters { - display: flex; - gap: var(--spacing-sm); - flex-wrap: wrap; + display: flex; + gap: var(--spacing-sm); + flex-wrap: wrap; } .filter-btn { - padding: 0.5rem 1rem; - background-color: transparent; - border: 1px solid var(--color-border); - border-radius: 20px; - font-size: 0.875rem; - color: var(--color-text-light); - cursor: pointer; - transition: var(--transition); - font-family: var(--font-main); - box-shadow: var(--shadow-dynamic-tier1); + padding: 0.5rem 1rem; + background-color: transparent; + border: 1px solid var(--color-border); + border-radius: 20px; + font-size: 0.875rem; + color: var(--color-text-light); + cursor: pointer; + transition: var(--transition); + font-family: var(--font-main); + box-shadow: var(--shadow-dynamic-tier1); } .filter-btn:hover { - border-color: var(--color-primary); - color: var(--color-primary); + border-color: var(--color-primary); + color: var(--color-primary); } .filter-btn.active { - background-color: var(--color-primary); - color: white; - border-color: var(--color-primary); + background-color: var(--color-primary); + color: white; + border-color: var(--color-primary); } .articles-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); - gap: var(--spacing-lg); + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: var(--spacing-lg); } .article-card { - background: rgba(255, 255, 255, 0.95); - backdrop-filter: var(--blur-subtle); - border: 1px solid rgba(229, 231, 235, 0.6); - border-radius: var(--radius-lg); - padding: var(--spacing-lg); - transition: var(--transition-smooth); - cursor: pointer; - position: relative; - overflow: hidden; - box-shadow: var(--shadow-dynamic-card); + background: rgba(255, 255, 255, 0.95); + backdrop-filter: var(--blur-subtle); + border: 1px solid rgba(229, 231, 235, 0.6); + border-radius: var(--radius-lg); + padding: var(--spacing-lg); + transition: var(--transition-smooth); + cursor: pointer; + position: relative; + overflow: hidden; + box-shadow: var(--shadow-dynamic-card); } .article-card::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, - transparent, - rgba(0, 102, 204, 0.05), - transparent - ); - transition: var(--transition-elegant); + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(0, 102, 204, 0.05), transparent); + transition: var(--transition-elegant); } .article-card:hover::before { - left: 100%; + left: 100%; } .article-card:hover { - box-shadow: var(--shadow-elegant); - border-color: rgba(0, 102, 204, 0.2); + box-shadow: var(--shadow-elegant); + border-color: rgba(0, 102, 204, 0.2); } .article-card .article-meta { - display: flex; - gap: var(--spacing-md); - margin-bottom: var(--spacing-sm); - font-size: 0.875rem; - color: var(--color-text-light); + display: flex; + gap: var(--spacing-md); + margin-bottom: var(--spacing-sm); + font-size: 0.875rem; + color: var(--color-text-light); } .article-category { - display: inline-block; - padding: 0.125rem 0.5rem; - background-color: var(--color-bg-secondary); - border-radius: 4px; - font-size: 0.75rem; - /* text-transform: uppercase; - Removed to show proper case labels */ - font-weight: 500; + display: inline-block; + padding: 0.125rem 0.5rem; + background-color: var(--color-bg-secondary); + border-radius: 4px; + font-size: 0.75rem; + /* text-transform: uppercase; - Removed to show proper case labels */ + font-weight: 500; } .article-category.ecommerce { - background-color: #e3f2fd; - color: #1976d2; + background-color: #e3f2fd; + color: #1976d2; } .article-category.devops { - background-color: #f3e5f5; - color: #7b1fa2; + background-color: #f3e5f5; + color: #7b1fa2; } .article-category.linux { - background-color: #e8f5e9; - color: #388e3c; + background-color: #e8f5e9; + color: #388e3c; } - .article-title { - font-size: 1.25rem; - font-weight: 600; - margin-bottom: var(--spacing-sm); - color: var(--color-text); - text-decoration: none; - display: block; + font-size: 1.25rem; + font-weight: 600; + margin-bottom: var(--spacing-sm); + color: var(--color-text); + text-decoration: none; + display: block; } /* Legacy styles for backward compatibility */ .article-title:hover { - color: var(--color-primary); - text-decoration: underline; + color: var(--color-primary); + text-decoration: underline; } /* Override for linked titles */ .article-card-link .article-title:hover { - text-decoration: none; + text-decoration: none; } .article-excerpt { - color: var(--color-text-light); - line-height: 1.6; - margin-bottom: var(--spacing-md); + color: var(--color-text-light); + line-height: 1.6; + margin-bottom: var(--spacing-md); } .article-read-more { - color: var(--color-primary); - text-decoration: none; - font-weight: 500; - display: inline-flex; - align-items: center; - gap: 0.25rem; - transition: var(--transition); + color: var(--color-primary); + text-decoration: none; + font-weight: 500; + display: inline-flex; + align-items: center; + gap: 0.25rem; + transition: var(--transition); } .article-read-more:hover { - gap: 0.5rem; + gap: 0.5rem; } /* Article card link wrapper - makes entire card clickable */ .article-card-link { - display: block; - color: inherit; - text-decoration: none; - height: 100%; + display: block; + color: inherit; + text-decoration: none; + height: 100%; } .article-card-link:hover { - color: inherit; - text-decoration: none; + color: inherit; + text-decoration: none; } .article-card-link .article-title { - color: var(--color-text); - text-decoration: none; - margin-bottom: var(--spacing-sm); - font-size: 1.25rem; - font-weight: 600; - line-height: 1.3; - transition: var(--transition); + color: var(--color-text); + text-decoration: none; + margin-bottom: var(--spacing-sm); + font-size: 1.25rem; + font-weight: 600; + line-height: 1.3; + transition: var(--transition); } .article-card:hover .article-card-link .article-title { - color: var(--color-primary); + color: var(--color-primary); } .article-footer { - display: flex; - justify-content: space-between; - align-items: center; - margin-top: auto; - padding-top: var(--spacing-sm); + display: flex; + justify-content: space-between; + align-items: center; + margin-top: auto; + padding-top: var(--spacing-sm); } .read-more { - color: var(--color-primary); - font-weight: 500; - font-size: 0.9rem; + color: var(--color-primary); + font-weight: 500; + font-size: 0.9rem; } .reading-time { - color: var(--color-text-light); - font-size: 0.875rem; + color: var(--color-text-light); + font-size: 0.875rem; } .no-results { - text-align: center; - padding: var(--spacing-xxl) 0; - color: var(--color-text-light); + text-align: center; + padding: var(--spacing-xxl) 0; + color: var(--color-text-light); } .loading-indicator { - text-align: center; - padding: var(--spacing-xl) 0; - color: var(--color-text-light); + text-align: center; + padding: var(--spacing-xl) 0; + color: var(--color-text-light); } /* Scroll-triggered animations */ .fade-in { - opacity: 0; - transform: translateY(20px); - animation: fadeInUp 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; + opacity: 0; + transform: translateY(20px); + animation: fadeInUp 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; } -.fade-in.delay-1 { animation-delay: 0.1s; } -.fade-in.delay-2 { animation-delay: 0.2s; } -.fade-in.delay-3 { animation-delay: 0.3s; } -.fade-in.delay-4 { animation-delay: 0.4s; } +.fade-in.delay-1 { + animation-delay: 0.1s; +} +.fade-in.delay-2 { + animation-delay: 0.2s; +} +.fade-in.delay-3 { + animation-delay: 0.3s; +} +.fade-in.delay-4 { + animation-delay: 0.4s; +} /* Reading progress indicator */ .reading-progress { - position: fixed; - top: 0; - left: 0; - width: 0%; - height: 3px; - background: linear-gradient(90deg, - var(--color-primary) 0%, - var(--color-primary-light) 50%, - var(--color-accent) 100% - ); - z-index: 1001; - transition: width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + position: fixed; + top: 0; + left: 0; + width: 0%; + height: 3px; + background: linear-gradient( + 90deg, + var(--color-primary) 0%, + var(--color-primary-light) 50%, + var(--color-accent) 100% + ); + z-index: 1001; + transition: width 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); } /* Elegant scrollbar */ ::-webkit-scrollbar { - width: 8px; + width: 8px; } ::-webkit-scrollbar-track { - background: rgba(248, 249, 250, 0.5); - border-radius: 4px; + background: rgba(248, 249, 250, 0.5); + border-radius: 4px; } ::-webkit-scrollbar-thumb { - background: linear-gradient(180deg, - var(--color-primary) 0%, - var(--color-primary-light) 100% - ); - border-radius: 4px; - border: 1px solid rgba(255, 255, 255, 0.2); + background: linear-gradient(180deg, var(--color-primary) 0%, var(--color-primary-light) 100%); + border-radius: 4px; + border: 1px solid rgba(255, 255, 255, 0.2); } ::-webkit-scrollbar-thumb:hover { - background: linear-gradient(180deg, - var(--color-primary-dark) 0%, - var(--color-primary) 100% - ); + background: linear-gradient(180deg, var(--color-primary-dark) 0%, var(--color-primary) 100%); } /* Elegant selection */ ::selection { - background: rgba(0, 102, 204, 0.2); - color: var(--color-text); + background: rgba(0, 102, 204, 0.2); + color: var(--color-text); } ::-moz-selection { - background: rgba(0, 102, 204, 0.2); - color: var(--color-text); + background: rgba(0, 102, 204, 0.2); + color: var(--color-text); } @media (max-width: 768px) { - .articles-grid { - grid-template-columns: 1fr; - } - - .search-filters { - justify-content: center; - } - - .article-container::before { - left: 1rem; - } - - .intro .lead { - padding-left: var(--spacing-sm); - } - - .article-container h2 { - padding-left: var(--spacing-sm); - } - - .article-container h3 { - padding-left: 0.5rem; - } -} \ No newline at end of file + .articles-grid { + grid-template-columns: 1fr; + } + + .search-filters { + justify-content: center; + } + + .article-container::before { + left: 1rem; + } + + .intro .lead { + padding-left: var(--spacing-sm); + } + + .article-container h2 { + padding-left: var(--spacing-sm); + } + + .article-container h3 { + padding-left: 0.5rem; + } +} diff --git a/ARCHIVE/private_html/css/author.css b/ARCHIVE/private_html/css/author.css index 340765d4..f65d2c6f 100644 --- a/ARCHIVE/private_html/css/author.css +++ b/ARCHIVE/private_html/css/author.css @@ -1,141 +1,143 @@ /* Author Page Specific Styles */ .author-section { - padding: var(--spacing-xxl) 0; + padding: var(--spacing-xxl) 0; } .author-profile { - display: flex; - align-items: center; - gap: var(--spacing-xl); - margin-bottom: var(--spacing-xl); + display: flex; + align-items: center; + gap: var(--spacing-xl); + margin-bottom: var(--spacing-xl); } .author-image { - width: 150px; - height: 150px; - border-radius: 50%; - object-fit: cover; + width: 150px; + height: 150px; + border-radius: 50%; + object-fit: cover; } .author-info h1 { - margin-bottom: var(--spacing-sm); - color: var(--color-text); + margin-bottom: var(--spacing-sm); + color: var(--color-text); } .author-bio { - font-size: 1.1rem; - line-height: 1.6; - color: var(--color-text-light); - margin-bottom: var(--spacing-md); + font-size: 1.1rem; + line-height: 1.6; + color: var(--color-text-light); + margin-bottom: var(--spacing-md); } .book-showcase { - background-color: var(--color-bg-secondary); - padding: var(--spacing-xl); - border-radius: 8px; - margin: var(--spacing-xl) 0; - box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); + background-color: var(--color-bg-secondary); + padding: var(--spacing-xl); + border-radius: 8px; + margin: var(--spacing-xl) 0; + box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); } .book-details { - display: flex; - gap: var(--spacing-lg); - align-items: flex-start; + display: flex; + gap: var(--spacing-lg); + align-items: flex-start; } .book-cover { - flex-shrink: 0; + flex-shrink: 0; } .book-cover img { - width: 200px; - height: auto; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), var(--shadow-dynamic-tier2); + width: 200px; + height: auto; + border-radius: 8px; + box-shadow: + 0 4px 12px rgba(0, 0, 0, 0.1), + var(--shadow-dynamic-tier2); } .book-info h2 { - margin-bottom: var(--spacing-md); - color: var(--color-text); + margin-bottom: var(--spacing-md); + color: var(--color-text); } .book-description { - color: var(--color-text-light); - line-height: 1.6; - margin-bottom: var(--spacing-md); + color: var(--color-text-light); + line-height: 1.6; + margin-bottom: var(--spacing-md); } .book-links { - display: flex; - gap: var(--spacing-md); - flex-wrap: wrap; + display: flex; + gap: var(--spacing-md); + flex-wrap: wrap; } .book-link { - display: inline-block; - padding: var(--spacing-sm) var(--spacing-md); - background-color: var(--color-primary); - color: white; - text-decoration: none; - border-radius: 4px; - transition: var(--transition); - box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier1); + display: inline-block; + padding: var(--spacing-sm) var(--spacing-md); + background-color: var(--color-primary); + color: white; + text-decoration: none; + border-radius: 4px; + transition: var(--transition); + box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier1); } .book-link:hover { - background-color: var(--color-primary-dark); + background-color: var(--color-primary-dark); } .expertise-highlights { - margin-top: var(--spacing-xl); + margin-top: var(--spacing-xl); } .expertise-highlights h2 { - margin-bottom: var(--spacing-lg); - color: var(--color-text); + margin-bottom: var(--spacing-lg); + color: var(--color-text); } .expertise-list { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: var(--spacing-md); + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: var(--spacing-md); } .expertise-item { - background-color: var(--color-bg-secondary); - padding: var(--spacing-md); - border-radius: 8px; - border-left: 4px solid var(--color-primary); - box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier1); + background-color: var(--color-bg-secondary); + padding: var(--spacing-md); + border-radius: 8px; + border-left: 4px solid var(--color-primary); + box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier1); } .expertise-item h3 { - margin-bottom: var(--spacing-sm); - color: var(--color-text); + margin-bottom: var(--spacing-sm); + color: var(--color-text); } .expertise-item p { - color: var(--color-text-light); - line-height: 1.5; + color: var(--color-text-light); + line-height: 1.5; } @media (max-width: 768px) { - .author-profile { - flex-direction: column; - text-align: center; - } - - .book-details { - flex-direction: column; - text-align: center; - } - - .book-cover img { - width: 150px; - } - - .expertise-list { - grid-template-columns: 1fr; - } -} \ No newline at end of file + .author-profile { + flex-direction: column; + text-align: center; + } + + .book-details { + flex-direction: column; + text-align: center; + } + + .book-cover img { + width: 150px; + } + + .expertise-list { + grid-template-columns: 1fr; + } +} diff --git a/ARCHIVE/private_html/css/book.css b/ARCHIVE/private_html/css/book.css index b8748eda..17c87f5e 100644 --- a/ARCHIVE/private_html/css/book.css +++ b/ARCHIVE/private_html/css/book.css @@ -1,122 +1,124 @@ /* Book showcase styles */ .book-showcase { - display: flex; - gap: 2rem; - margin: 2rem 0; - padding: 2rem; - background: #f8f9fa; - border-radius: 8px; - align-items: flex-start; - box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); + display: flex; + gap: 2rem; + margin: 2rem 0; + padding: 2rem; + background: #f8f9fa; + border-radius: 8px; + align-items: flex-start; + box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); } .book-cover { - width: 200px; - height: auto; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), var(--shadow-dynamic-tier2); - flex-shrink: 0; + width: 200px; + height: auto; + border-radius: 8px; + box-shadow: + 0 4px 12px rgba(0, 0, 0, 0.15), + var(--shadow-dynamic-tier2); + flex-shrink: 0; } .book-details { - flex: 1; + flex: 1; } .book-details h3 { - margin: 0 0 0.5rem 0; - color: #2c3e50; - font-size: 1.2rem; + margin: 0 0 0.5rem 0; + color: #2c3e50; + font-size: 1.2rem; } .book-rating { - color: #e67e22; - font-weight: 500; - margin: 0.5rem 0; + color: #e67e22; + font-weight: 500; + margin: 0.5rem 0; } .book-description { - font-style: italic; - color: #555; - margin: 1rem 0; - font-size: 1.1rem; + font-style: italic; + color: #555; + margin: 1rem 0; + font-size: 1.1rem; } .book-availability { - background: #e8f5e8; - padding: 1.5rem; - border-radius: 8px; - margin: 2rem 0; - border-left: 4px solid #27ae60; - box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); + background: #e8f5e8; + padding: 1.5rem; + border-radius: 8px; + margin: 2rem 0; + border-left: 4px solid #27ae60; + box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); } .book-availability h3 { - color: #27ae60; - margin-top: 0; + color: #27ae60; + margin-top: 0; } .book-availability ul { - list-style: none; - padding: 0; + list-style: none; + padding: 0; } .book-availability li { - padding: 0.25rem 0; - position: relative; - padding-left: 1.5rem; + padding: 0.25rem 0; + position: relative; + padding-left: 1.5rem; } .book-availability li:before { - content: "📚"; - position: absolute; - left: 0; + content: '📚'; + position: absolute; + left: 0; } .book-stats ul, .author-credentials ul, .writing-topics ul { - list-style: none; - padding: 0; + list-style: none; + padding: 0; } .book-stats li, .author-credentials li, .writing-topics li { - padding: 0.5rem 0; - border-bottom: 1px solid #eee; + padding: 0.5rem 0; + border-bottom: 1px solid #eee; } .book-stats li:last-child, .author-credentials li:last-child, .writing-topics li:last-child { - border-bottom: none; + border-bottom: none; } blockquote { - border-left: 4px solid #3498db; - padding-left: 1rem; - margin: 1.5rem 0; - font-style: italic; - color: #555; - background: #f8f9fa; - padding: 1rem 1rem 1rem 2rem; - border-radius: 0 4px 4px 0; - box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier1); + border-left: 4px solid #3498db; + padding-left: 1rem; + margin: 1.5rem 0; + font-style: italic; + color: #555; + background: #f8f9fa; + padding: 1rem 1rem 1rem 2rem; + border-radius: 0 4px 4px 0; + box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier1); } /* Responsive design */ @media (max-width: 768px) { - .book-showcase { - flex-direction: column; - text-align: center; - } - - .book-cover { - width: 150px; - align-self: center; - } - - .book-details { - text-align: left; - } -} \ No newline at end of file + .book-showcase { + flex-direction: column; + text-align: center; + } + + .book-cover { + width: 150px; + align-self: center; + } + + .book-details { + text-align: left; + } +} diff --git a/ARCHIVE/private_html/css/category-styles.css b/ARCHIVE/private_html/css/category-styles.css index dbedca89..0a32ec9d 100644 --- a/ARCHIVE/private_html/css/category-styles.css +++ b/ARCHIVE/private_html/css/category-styles.css @@ -2,27 +2,26 @@ /* Generated from private_html/data/categories.json */ .article-category.php { - background-color: #f3e5f5; - color: #7b1fa2; + background-color: #f3e5f5; + color: #7b1fa2; } .article-category.infrastructure { - background-color: #e8f5e9; - color: #388e3c; + background-color: #e8f5e9; + color: #388e3c; } .article-category.database { - background-color: #e3f2fd; - color: #1976d2; + background-color: #e3f2fd; + color: #1976d2; } .article-category.ai { - background-color: #fff3e0; - color: #f57c00; + background-color: #fff3e0; + color: #f57c00; } .article-category.typescript { - background-color: #e1f5fe; - color: #0277bd; + background-color: #e1f5fe; + color: #0277bd; } - diff --git a/ARCHIVE/private_html/css/contact.css b/ARCHIVE/private_html/css/contact.css index 826ec384..1cfc40bc 100644 --- a/ARCHIVE/private_html/css/contact.css +++ b/ARCHIVE/private_html/css/contact.css @@ -1,444 +1,462 @@ /* Contact Page Specific Styles */ .contact-section { - padding: var(--spacing-xxl) 0; + padding: var(--spacing-xxl) 0; } .contact-grid { - display: grid; - grid-template-columns: 2fr 1fr; - gap: var(--spacing-xxl); + display: grid; + grid-template-columns: 2fr 1fr; + gap: var(--spacing-xxl); } /* Contact Form */ .contact-form { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: var(--spacing-lg); + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--spacing-lg); } .form-group { - display: flex; - flex-direction: column; - position: relative; + display: flex; + flex-direction: column; + position: relative; } .form-group.full-width { - grid-column: 1 / -1; + grid-column: 1 / -1; } .form-group label { - font-weight: 500; - margin-bottom: var(--spacing-xs); - color: var(--color-text); - font-size: 0.875rem; - text-transform: uppercase; - letter-spacing: 0.5px; - transition: color 0.3s ease; + font-weight: 500; + margin-bottom: var(--spacing-xs); + color: var(--color-text); + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.5px; + transition: color 0.3s ease; } .form-group label::after { - content: "*"; - color: #ef4444; - margin-left: 4px; - display: none; + content: '*'; + color: #ef4444; + margin-left: 4px; + display: none; } .form-group:has([required]) label::after { - display: inline; + display: inline; } .form-group input, .form-group select, .form-group textarea { - padding: 0.875rem 1rem; - border: 2px solid #e5e7eb; - border-radius: 8px; - font-family: var(--font-main); - font-size: 1rem; - transition: all 0.3s ease; - background-color: #ffffff; - color: var(--color-text); + padding: 0.875rem 1rem; + border: 2px solid #e5e7eb; + border-radius: 8px; + font-family: var(--font-main); + font-size: 1rem; + transition: all 0.3s ease; + background-color: #ffffff; + color: var(--color-text); } .form-group input::placeholder, .form-group textarea::placeholder { - color: #9ca3af; - font-size: 0.95rem; + color: #9ca3af; + font-size: 0.95rem; } /* Validation States */ .form-group.error input, .form-group.error select, .form-group.error textarea { - border-color: #ef4444; - background-color: #fef2f2; + border-color: #ef4444; + background-color: #fef2f2; } .form-group.error label { - color: #ef4444; + color: #ef4444; } .form-group.success input, .form-group.success select, .form-group.success textarea { - border-color: #10b981; - background-color: #f0fdf4; + border-color: #10b981; + background-color: #f0fdf4; } .form-group.focused label { - color: var(--color-primary); + color: var(--color-primary); } /* Error message */ .form-error { - position: absolute; - bottom: -20px; - left: 0; - font-size: 0.75rem; - color: #ef4444; - display: none; - animation: slideDown 0.3s ease; + position: absolute; + bottom: -20px; + left: 0; + font-size: 0.75rem; + color: #ef4444; + display: none; + animation: slideDown 0.3s ease; } .form-group.error .form-error { - display: block; + display: block; } @keyframes slideDown { - from { - opacity: 0; - transform: translateY(-5px); - } - to { - opacity: 1; - transform: translateY(0); - } + from { + opacity: 0; + transform: translateY(-5px); + } + to { + opacity: 1; + transform: translateY(0); + } } .form-group input:focus, .form-group select:focus, .form-group textarea:focus { - outline: none; - border-color: var(--color-primary); - box-shadow: 0 0 0 4px rgba(0, 102, 204, 0.1); - background-color: #f9fafb; - transform: translateY(-1px); + outline: none; + border-color: var(--color-primary); + box-shadow: 0 0 0 4px rgba(0, 102, 204, 0.1); + background-color: #f9fafb; + transform: translateY(-1px); } /* Hover states */ .form-group input:hover:not(:focus), .form-group select:hover:not(:focus), .form-group textarea:hover:not(:focus) { - border-color: #d1d5db; + border-color: #d1d5db; } .form-group textarea { - resize: vertical; - min-height: 120px; - line-height: 1.6; + resize: vertical; + min-height: 120px; + line-height: 1.6; } /* Select dropdown arrow styling */ .form-group select { - appearance: none; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%236b7280'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 0.75rem center; - background-size: 1.5rem; - padding-right: 2.5rem; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%236b7280'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 0.75rem center; + background-size: 1.5rem; + padding-right: 2.5rem; } .form-group select:focus { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%230066cc'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E"); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%230066cc'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E"); } /* Character counter for textarea */ .char-counter { - position: absolute; - bottom: 8px; - right: 8px; - font-size: 0.75rem; - color: #9ca3af; - pointer-events: none; + position: absolute; + bottom: 8px; + right: 8px; + font-size: 0.75rem; + color: #9ca3af; + pointer-events: none; } .form-message { - padding: 1rem 1.25rem; - border-radius: 8px; - margin-top: var(--spacing-md); - display: none; - animation: fadeInUp 0.4s ease; - position: relative; - font-size: 0.95rem; + padding: 1rem 1.25rem; + border-radius: 8px; + margin-top: var(--spacing-md); + display: none; + animation: fadeInUp 0.4s ease; + position: relative; + font-size: 0.95rem; } @keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } } .form-message.success { - background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); - color: #155724; - border: 1px solid #c3e6cb; - display: block; - box-shadow: 0 4px 6px rgba(34, 197, 94, 0.1); + background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%); + color: #155724; + border: 1px solid #c3e6cb; + display: block; + box-shadow: 0 4px 6px rgba(34, 197, 94, 0.1); } .form-message.error { - background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); - color: #991b1b; - border: 1px solid #fca5a5; - display: block; - box-shadow: 0 4px 6px rgba(239, 68, 68, 0.1); + background: linear-gradient(135deg, #fee2e2 0%, #fecaca 100%); + color: #991b1b; + border: 1px solid #fca5a5; + display: block; + box-shadow: 0 4px 6px rgba(239, 68, 68, 0.1); } .form-message.info { - background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); - color: #1e40af; - border: 1px solid #93c5fd; - display: block; - box-shadow: 0 4px 6px rgba(59, 130, 246, 0.1); + background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); + color: #1e40af; + border: 1px solid #93c5fd; + display: block; + box-shadow: 0 4px 6px rgba(59, 130, 246, 0.1); } /* Contact Info */ .contact-info h2 { - font-size: 1.75rem; - margin-bottom: var(--spacing-md); + font-size: 1.75rem; + margin-bottom: var(--spacing-md); } .contact-info > p { - font-size: 1.0625rem; - line-height: 1.7; - color: var(--color-text-light); - margin-bottom: var(--spacing-lg); + font-size: 1.0625rem; + line-height: 1.7; + color: var(--color-text-light); + margin-bottom: var(--spacing-lg); } .info-section { - margin-bottom: var(--spacing-lg); - padding: var(--spacing-lg); - background-color: var(--color-bg-secondary); - border-radius: 8px; + margin-bottom: var(--spacing-lg); + padding: var(--spacing-lg); + background-color: var(--color-bg-secondary); + border-radius: 8px; } .info-section h3 { - font-size: 1.25rem; - margin-bottom: var(--spacing-sm); - color: var(--color-text); + font-size: 1.25rem; + margin-bottom: var(--spacing-sm); + color: var(--color-text); } .info-section p { - color: var(--color-text-light); - line-height: 1.6; + color: var(--color-text-light); + line-height: 1.6; } .process-list { - list-style: none; - counter-reset: process-counter; + list-style: none; + counter-reset: process-counter; } .process-list li { - position: relative; - padding-left: 2rem; - margin-bottom: var(--spacing-sm); - color: var(--color-text-light); - line-height: 1.6; - counter-increment: process-counter; + position: relative; + padding-left: 2rem; + margin-bottom: var(--spacing-sm); + color: var(--color-text-light); + line-height: 1.6; + counter-increment: process-counter; } .process-list li::before { - content: counter(process-counter); - position: absolute; - left: 0; - top: 0; - width: 1.5rem; - height: 1.5rem; - background-color: var(--color-primary); - color: white; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 0.875rem; - font-weight: 600; + content: counter(process-counter); + position: absolute; + left: 0; + top: 0; + width: 1.5rem; + height: 1.5rem; + background-color: var(--color-primary); + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.875rem; + font-weight: 600; } .social-links { - display: flex; - gap: var(--spacing-md); + display: flex; + gap: var(--spacing-md); } .social-link { - color: var(--color-primary); - text-decoration: none; - font-weight: 500; - transition: var(--transition); + color: var(--color-primary); + text-decoration: none; + font-weight: 500; + transition: var(--transition); } .social-link:hover { - color: var(--color-primary-dark); - text-decoration: underline; + color: var(--color-primary-dark); + text-decoration: underline; } /* Button enhancements */ .btn-primary { - position: relative; - overflow: hidden; - font-weight: 600; - letter-spacing: 0.5px; - transition: all 0.3s ease; + position: relative; + overflow: hidden; + font-weight: 600; + letter-spacing: 0.5px; + transition: all 0.3s ease; } .btn-primary::before { - content: ""; - position: absolute; - top: 50%; - left: 50%; - width: 0; - height: 0; - border-radius: 50%; - background: rgba(255, 255, 255, 0.2); - transform: translate(-50%, -50%); - transition: width 0.6s, height 0.6s; + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + background: rgba(255, 255, 255, 0.2); + transform: translate(-50%, -50%); + transition: + width 0.6s, + height 0.6s; } .btn-primary:hover::before { - width: 300px; - height: 300px; + width: 300px; + height: 300px; } .btn-primary:hover { - transform: translateY(-2px); - box-shadow: 0 10px 20px rgba(0, 102, 204, 0.2); + transform: translateY(-2px); + box-shadow: 0 10px 20px rgba(0, 102, 204, 0.2); } .btn-primary:active { - transform: translateY(0); + transform: translateY(0); } /* Loading State */ .btn-primary:disabled { - opacity: 0.6; - cursor: not-allowed; - transform: none; + opacity: 0.6; + cursor: not-allowed; + transform: none; } .btn-primary.loading { - color: transparent; + color: transparent; } .btn-primary.loading::after { - content: ""; - position: absolute; - width: 20px; - height: 20px; - top: 50%; - left: 50%; - margin-left: -10px; - margin-top: -10px; - border: 2px solid #ffffff; - border-radius: 50%; - border-top-color: transparent; - animation: spinner 0.8s linear infinite; + content: ''; + position: absolute; + width: 20px; + height: 20px; + top: 50%; + left: 50%; + margin-left: -10px; + margin-top: -10px; + border: 2px solid #ffffff; + border-radius: 50%; + border-top-color: transparent; + animation: spinner 0.8s linear infinite; } @keyframes spinner { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } /* Shake animation for validation errors */ @keyframes shake { - 0%, 100% { transform: translateX(0); } - 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } - 20%, 40%, 60%, 80% { transform: translateX(5px); } + 0%, + 100% { + transform: translateX(0); + } + 10%, + 30%, + 50%, + 70%, + 90% { + transform: translateX(-5px); + } + 20%, + 40%, + 60%, + 80% { + transform: translateX(5px); + } } .btn-primary.shake { - animation: shake 0.5s ease-in-out; + animation: shake 0.5s ease-in-out; } /* Smooth transitions for form groups */ .form-group { - transition: margin-bottom 0.3s ease; + transition: margin-bottom 0.3s ease; } .form-group.error { - margin-bottom: 28px; + margin-bottom: 28px; } /* Focus within styling */ .form-group:focus-within label { - color: var(--color-primary); + color: var(--color-primary); } /* Progressive enhancement for modern browsers */ @supports (backdrop-filter: blur(10px)) { - .form-message { - backdrop-filter: blur(10px); - background-color: rgba(255, 255, 255, 0.9); - } + .form-message { + backdrop-filter: blur(10px); + background-color: rgba(255, 255, 255, 0.9); + } } /* Accessibility improvements */ .form-group input:focus-visible, .form-group select:focus-visible, .form-group textarea:focus-visible { - outline: 2px solid var(--color-primary); - outline-offset: 2px; + outline: 2px solid var(--color-primary); + outline-offset: 2px; } /* Smooth field transitions */ .form-group input, .form-group select, .form-group textarea { - will-change: transform, border-color, background-color; + will-change: transform, border-color, background-color; } /* Success checkmark animation */ @keyframes checkmark { - 0% { - transform: scale(0) rotate(45deg); - opacity: 0; - } - 50% { - transform: scale(1.2) rotate(45deg); - } - 100% { - transform: scale(1) rotate(45deg); - opacity: 1; - } + 0% { + transform: scale(0) rotate(45deg); + opacity: 0; + } + 50% { + transform: scale(1.2) rotate(45deg); + } + 100% { + transform: scale(1) rotate(45deg); + opacity: 1; + } } .form-group.success::after { - content: "✓"; - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - color: #10b981; - font-weight: bold; - font-size: 1.25rem; - animation: checkmark 0.4s ease; + content: '✓'; + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + color: #10b981; + font-weight: bold; + font-size: 1.25rem; + animation: checkmark 0.4s ease; } /* Responsive */ @media (max-width: 968px) { - .contact-grid { - grid-template-columns: 1fr; - } - - .contact-form { - grid-template-columns: 1fr; - } - - .form-group { - grid-column: 1 / -1; - } -} \ No newline at end of file + .contact-grid { + grid-template-columns: 1fr; + } + + .contact-form { + grid-template-columns: 1fr; + } + + .form-group { + grid-column: 1 / -1; + } +} diff --git a/ARCHIVE/private_html/css/main.css b/ARCHIVE/private_html/css/main.css index a5886939..eeae0cfc 100644 --- a/ARCHIVE/private_html/css/main.css +++ b/ARCHIVE/private_html/css/main.css @@ -1,854 +1,858 @@ :root { - /* Core Colors with Mathematical Precision */ - --color-primary: #0066cc; - --color-primary-dark: #0052a3; - --color-primary-light: #338bff; - --color-secondary: #333333; - --color-text: #2c2c2c; - --color-text-light: #666666; - --color-bg: #ffffff; - --color-bg-secondary: #f8f9fa; - --color-border: #e5e7eb; - --color-accent: #ff6b6b; - --color-success: #51cf66; - - /* Golden Ratio Based Typography */ - --golden-ratio: 1.618; - --golden-line-height: calc(2 / var(--golden-ratio)); - --font-main: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - - /* Dynamic Gradient Variables */ - --gradient-angle: 135deg; - --max-width: 1200px; - - /* Dynamic Shadow Variables (synced with mouse movement) */ - --shadow-x: 2px; - --shadow-y: 2px; - --shadow-blur: 2px; - - /* Mathematical Spacing Scale (Fibonacci-inspired) */ - --spacing-xxs: 0.25rem; /* 4px */ - --spacing-xs: 0.5rem; /* 8px */ - --spacing-sm: 0.8rem; /* 13px */ - --spacing-md: 1.3rem; /* 21px */ - --spacing-lg: 2.1rem; /* 34px */ - --spacing-xl: 3.4rem; /* 55px */ - --spacing-xxl: 5.5rem; /* 89px */ - - /* Extended color palette */ - --color-bg-white: #ffffff; - --color-border-light: rgba(229, 231, 235, 0.4); - --color-text-muted: #999999; - - /* Elegant Easing Functions */ - --transition-quick: all 0.15s cubic-bezier(0.4, 0.0, 0.2, 1); - --transition-smooth: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); - --transition-bounce: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); - --transition-elegant: all 0.6s cubic-bezier(0.23, 1, 0.32, 1); - - /* Shadows with Mathematical Precision */ - --shadow-subtle: 0 1px 3px rgba(0, 0, 0, 0.08); - --shadow-medium: 0 4px 12px rgba(0, 0, 0, 0.1); - --shadow-elegant: 0 8px 32px rgba(0, 0, 0, 0.12); - --shadow-dramatic: 0 20px 60px rgba(0, 0, 0, 0.15); - - /* Dynamic Shadows (mouse-responsive, tiered system) */ - /* Tier 1: Small elements (buttons, badges, small cards) */ - --shadow-dynamic-tier1: calc(var(--shadow-x) * 0.5) calc(var(--shadow-y) * 0.5) calc(var(--shadow-blur) * 0.5) rgba(0, 0, 0, 0.03); - - /* Tier 2: Medium elements (navigation, medium cards, form elements) */ - --shadow-dynamic-tier2: var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.05); - - /* Tier 3: Large elements (article cards, main containers, hero sections) */ - --shadow-dynamic-tier3: calc(var(--shadow-x) * 1.5) calc(var(--shadow-y) * 1.5) calc(var(--shadow-blur) * 1.2) rgba(0, 0, 0, 0.08); - - /* Legacy aliases for backward compatibility */ - --shadow-dynamic-subtle: var(--shadow-dynamic-tier1); - --shadow-dynamic-medium: var(--shadow-dynamic-tier2); - --shadow-dynamic-card: var(--shadow-dynamic-tier3); - - /* Border Radius Based on Golden Ratio */ - --radius-sm: 4px; - --radius-md: 6px; - --radius-lg: 10px; - --radius-xl: 16px; - - /* Backdrop Blur Effects */ - --blur-subtle: blur(8px); - --blur-medium: blur(16px); - --blur-strong: blur(32px); + /* Core Colors with Mathematical Precision */ + --color-primary: #0066cc; + --color-primary-dark: #0052a3; + --color-primary-light: #338bff; + --color-secondary: #333333; + --color-text: #2c2c2c; + --color-text-light: #666666; + --color-bg: #ffffff; + --color-bg-secondary: #f8f9fa; + --color-border: #e5e7eb; + --color-accent: #ff6b6b; + --color-success: #51cf66; + + /* Golden Ratio Based Typography */ + --golden-ratio: 1.618; + --golden-line-height: calc(2 / var(--golden-ratio)); + --font-main: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + + /* Dynamic Gradient Variables */ + --gradient-angle: 135deg; + --max-width: 1200px; + + /* Dynamic Shadow Variables (synced with mouse movement) */ + --shadow-x: 2px; + --shadow-y: 2px; + --shadow-blur: 2px; + + /* Mathematical Spacing Scale (Fibonacci-inspired) */ + --spacing-xxs: 0.25rem; /* 4px */ + --spacing-xs: 0.5rem; /* 8px */ + --spacing-sm: 0.8rem; /* 13px */ + --spacing-md: 1.3rem; /* 21px */ + --spacing-lg: 2.1rem; /* 34px */ + --spacing-xl: 3.4rem; /* 55px */ + --spacing-xxl: 5.5rem; /* 89px */ + + /* Extended color palette */ + --color-bg-white: #ffffff; + --color-border-light: rgba(229, 231, 235, 0.4); + --color-text-muted: #999999; + + /* Elegant Easing Functions */ + --transition-quick: all 0.15s cubic-bezier(0.4, 0, 0.2, 1); + --transition-smooth: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); + --transition-bounce: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); + --transition-elegant: all 0.6s cubic-bezier(0.23, 1, 0.32, 1); + + /* Shadows with Mathematical Precision */ + --shadow-subtle: 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-medium: 0 4px 12px rgba(0, 0, 0, 0.1); + --shadow-elegant: 0 8px 32px rgba(0, 0, 0, 0.12); + --shadow-dramatic: 0 20px 60px rgba(0, 0, 0, 0.15); + + /* Dynamic Shadows (mouse-responsive, tiered system) */ + /* Tier 1: Small elements (buttons, badges, small cards) */ + --shadow-dynamic-tier1: calc(var(--shadow-x) * 0.5) calc(var(--shadow-y) * 0.5) + calc(var(--shadow-blur) * 0.5) rgba(0, 0, 0, 0.03); + + /* Tier 2: Medium elements (navigation, medium cards, form elements) */ + --shadow-dynamic-tier2: var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.05); + + /* Tier 3: Large elements (article cards, main containers, hero sections) */ + --shadow-dynamic-tier3: calc(var(--shadow-x) * 1.5) calc(var(--shadow-y) * 1.5) + calc(var(--shadow-blur) * 1.2) rgba(0, 0, 0, 0.08); + + /* Legacy aliases for backward compatibility */ + --shadow-dynamic-subtle: var(--shadow-dynamic-tier1); + --shadow-dynamic-medium: var(--shadow-dynamic-tier2); + --shadow-dynamic-card: var(--shadow-dynamic-tier3); + + /* Border Radius Based on Golden Ratio */ + --radius-sm: 4px; + --radius-md: 6px; + --radius-lg: 10px; + --radius-xl: 16px; + + /* Backdrop Blur Effects */ + --blur-subtle: blur(8px); + --blur-medium: blur(16px); + --blur-strong: blur(32px); } * { - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } html { - font-size: 16px; - scroll-behavior: smooth; + font-size: 16px; + scroll-behavior: smooth; } - body { - font-family: var(--font-main); - color: var(--color-text); - background: linear-gradient(135deg, - var(--color-bg) 0%, - rgba(248, 249, 250, 0.4) 100% - ); - background-attachment: fixed; - line-height: var(--golden-ratio); - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-rendering: optimizeLegibility; - font-feature-settings: "kern" 1, "liga" 1; - opacity: 0; - transition: opacity 0.3s ease-in-out; + font-family: var(--font-main); + color: var(--color-text); + background: linear-gradient(135deg, var(--color-bg) 0%, rgba(248, 249, 250, 0.4) 100%); + background-attachment: fixed; + line-height: var(--golden-ratio); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; + font-feature-settings: + 'kern' 1, + 'liga' 1; + opacity: 0; + transition: opacity 0.3s ease-in-out; } body.loaded { - opacity: 1; + opacity: 1; } /* Fix for sectioned h1 elements */ h1 { - font-size: 2.5rem; - font-weight: 600; - line-height: 1.3; - margin: 0 0 var(--spacing-md) 0; - word-wrap: break-word; - overflow-wrap: break-word; - hyphens: auto; - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - /* Mouse-responsive drop shadow */ - filter: drop-shadow(var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.15)); + font-size: 2.5rem; + font-weight: 600; + line-height: 1.3; + margin: 0 0 var(--spacing-md) 0; + word-wrap: break-word; + overflow-wrap: break-word; + hyphens: auto; + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + /* Mouse-responsive drop shadow */ + filter: drop-shadow(var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.15)); } section h1 { - font-size: 2.5rem; - font-weight: 600; - line-height: 1.3; - margin: 0 0 var(--spacing-md) 0; - word-wrap: break-word; - overflow-wrap: break-word; - hyphens: auto; - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - /* Mouse-responsive drop shadow */ - filter: drop-shadow(var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.15)); + font-size: 2.5rem; + font-weight: 600; + line-height: 1.3; + margin: 0 0 var(--spacing-md) 0; + word-wrap: break-word; + overflow-wrap: break-word; + hyphens: auto; + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + /* Mouse-responsive drop shadow */ + filter: drop-shadow(var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.15)); } h2 { - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - /* Mouse-responsive drop shadow (subtler for h2) */ - filter: drop-shadow(calc(var(--shadow-x) * 0.8) calc(var(--shadow-y) * 0.8) calc(var(--shadow-blur) * 0.9) rgba(0, 0, 0, 0.12)); + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + /* Mouse-responsive drop shadow (subtler for h2) */ + filter: drop-shadow( + calc(var(--shadow-x) * 0.8) calc(var(--shadow-y) * 0.8) calc(var(--shadow-blur) * 0.9) + rgba(0, 0, 0, 0.12) + ); } h3 { - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - /* Mouse-responsive drop shadow (even more subtle for h3) */ - filter: drop-shadow(calc(var(--shadow-x) * 0.6) calc(var(--shadow-y) * 0.6) calc(var(--shadow-blur) * 0.8) rgba(0, 0, 0, 0.10)); + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + /* Mouse-responsive drop shadow (even more subtle for h3) */ + filter: drop-shadow( + calc(var(--shadow-x) * 0.6) calc(var(--shadow-y) * 0.6) calc(var(--shadow-blur) * 0.8) + rgba(0, 0, 0, 0.1) + ); } .container { - max-width: var(--max-width); - margin: 0 auto; - padding: 0 var(--spacing-md); + max-width: var(--max-width); + margin: 0 auto; + padding: 0 var(--spacing-md); } /* Header & Navigation with Elegant Backdrop */ .site-header { - position: sticky; - top: 0; - background: rgba(255, 255, 255, 0.85); - border-bottom: 1px solid rgba(229, 231, 235, 0.6); - z-index: 1000; - backdrop-filter: var(--blur-medium) saturate(180%); - -webkit-backdrop-filter: var(--blur-medium) saturate(180%); - transition: var(--transition-smooth); - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05), var(--shadow-dynamic-tier2); + position: sticky; + top: 0; + background: rgba(255, 255, 255, 0.85); + border-bottom: 1px solid rgba(229, 231, 235, 0.6); + z-index: 1000; + backdrop-filter: var(--blur-medium) saturate(180%); + -webkit-backdrop-filter: var(--blur-medium) saturate(180%); + transition: var(--transition-smooth); + box-shadow: + 0 1px 0 rgba(0, 0, 0, 0.05), + var(--shadow-dynamic-tier2); } - .nav-container { - max-width: var(--max-width); - margin: 0 auto; - padding: 0 var(--spacing-md); - display: flex; - align-items: center; - justify-content: space-between; - height: 64px; + max-width: var(--max-width); + margin: 0 auto; + padding: 0 var(--spacing-md); + display: flex; + align-items: center; + justify-content: space-between; + height: 64px; } .nav-brand .brand-link { - font-size: 1.25rem; - font-weight: 600; - color: var(--color-text); - text-decoration: none; - transition: var(--transition); + font-size: 1.25rem; + font-weight: 600; + color: var(--color-text); + text-decoration: none; + transition: var(--transition); } .nav-brand .brand-link:hover { - color: var(--color-primary); + color: var(--color-primary); } .nav-menu { - display: flex; - list-style: none; - gap: var(--spacing-lg); + display: flex; + list-style: none; + gap: var(--spacing-lg); } .nav-link { - color: var(--color-text-light); - text-decoration: none; - font-weight: 500; - transition: var(--transition-smooth); - position: relative; - padding: 0.5rem 0; - overflow: hidden; + color: var(--color-text-light); + text-decoration: none; + font-weight: 500; + transition: var(--transition-smooth); + position: relative; + padding: 0.5rem 0; + overflow: hidden; } - .nav-link:hover, .nav-link.active { - color: var(--color-primary); + color: var(--color-primary); } .nav-link.active::after { - content: ''; - position: absolute; - bottom: -20px; - left: 50%; - width: 0; - height: 2px; - background: linear-gradient(90deg, - var(--color-primary), - var(--color-primary-light) - ); - border-radius: 1px; - transform: translateX(-50%); - animation: expandLine 0.6s cubic-bezier(0.23, 1, 0.32, 1) forwards; + content: ''; + position: absolute; + bottom: -20px; + left: 50%; + width: 0; + height: 2px; + background: linear-gradient(90deg, var(--color-primary), var(--color-primary-light)); + border-radius: 1px; + transform: translateX(-50%); + animation: expandLine 0.6s cubic-bezier(0.23, 1, 0.32, 1) forwards; } @keyframes expandLine { - to { - width: 100%; - } + to { + width: 100%; + } } .nav-toggle { - display: none; - background: none; - border: none; - cursor: pointer; - padding: var(--spacing-xs); + display: none; + background: none; + border: none; + cursor: pointer; + padding: var(--spacing-xs); } .hamburger { - display: block; - width: 24px; - height: 2px; - background-color: var(--color-text); - position: relative; - transition: var(--transition); + display: block; + width: 24px; + height: 2px; + background-color: var(--color-text); + position: relative; + transition: var(--transition); } .hamburger::before, .hamburger::after { - content: ''; - position: absolute; - width: 24px; - height: 2px; - background-color: var(--color-text); - transition: var(--transition); + content: ''; + position: absolute; + width: 24px; + height: 2px; + background-color: var(--color-text); + transition: var(--transition); } .hamburger::before { - top: -8px; + top: -8px; } .hamburger::after { - bottom: -8px; + bottom: -8px; } /* Hero Section with Subtle Animation */ .hero { - padding: var(--spacing-xxl) 0; - text-align: center; - position: relative; - overflow: hidden; + padding: var(--spacing-xxl) 0; + text-align: center; + position: relative; + overflow: hidden; } .hero::before { - content: ''; - position: absolute; - top: -50%; - left: -50%; - width: 200%; - height: 200%; - background: radial-gradient( - ellipse at center, - rgba(0, 102, 204, 0.03) 0%, - transparent 50% - ); - animation: gentleFloat 20s ease-in-out infinite; - pointer-events: none; + content: ''; + position: absolute; + top: -50%; + left: -50%; + width: 200%; + height: 200%; + background: radial-gradient(ellipse at center, rgba(0, 102, 204, 0.03) 0%, transparent 50%); + animation: gentleFloat 20s ease-in-out infinite; + pointer-events: none; } @keyframes gentleFloat { - 0%, 100% { transform: translate(0, 0) rotate(0deg); } - 33% { transform: translate(30px, -30px) rotate(1deg); } - 66% { transform: translate(-20px, 20px) rotate(-0.5deg); } + 0%, + 100% { + transform: translate(0, 0) rotate(0deg); + } + 33% { + transform: translate(30px, -30px) rotate(1deg); + } + 66% { + transform: translate(-20px, 20px) rotate(-0.5deg); + } } .hero-content { - max-width: 800px; - margin: 0 auto; - padding: 0 var(--spacing-md); + max-width: 800px; + margin: 0 auto; + padding: 0 var(--spacing-md); } .hero-title { - font-size: clamp(2rem, 5vw, 3.5rem); - font-weight: 600; - line-height: var(--golden-line-height); - margin-bottom: var(--spacing-md); - color: var(--color-text); - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - /* Mouse-responsive drop shadow (prominent for hero) */ - filter: drop-shadow(calc(var(--shadow-x) * 1.2) calc(var(--shadow-y) * 1.2) calc(var(--shadow-blur) * 1.3) rgba(0, 0, 0, 0.18)); - animation: subtleGlow 3s ease-in-out infinite alternate; + font-size: clamp(2rem, 5vw, 3.5rem); + font-weight: 600; + line-height: var(--golden-line-height); + margin-bottom: var(--spacing-md); + color: var(--color-text); + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + /* Mouse-responsive drop shadow (prominent for hero) */ + filter: drop-shadow( + calc(var(--shadow-x) * 1.2) calc(var(--shadow-y) * 1.2) calc(var(--shadow-blur) * 1.3) + rgba(0, 0, 0, 0.18) + ); + animation: subtleGlow 3s ease-in-out infinite alternate; } @keyframes subtleGlow { - from { filter: brightness(1); } - to { filter: brightness(1.05); } + from { + filter: brightness(1); + } + to { + filter: brightness(1.05); + } } .hero-subtitle { - font-size: 1.25rem; - color: var(--color-text-light); - margin-bottom: var(--spacing-lg); - line-height: 1.6; + font-size: 1.25rem; + color: var(--color-text-light); + margin-bottom: var(--spacing-lg); + line-height: 1.6; } .hero-actions { - display: flex; - gap: var(--spacing-md); - justify-content: center; - flex-wrap: wrap; + display: flex; + gap: var(--spacing-md); + justify-content: center; + flex-wrap: wrap; } /* Elegant Buttons with Micro-interactions */ .btn { - display: inline-block; - padding: 0.75rem 1.5rem; - font-weight: 500; - text-decoration: none; - border-radius: var(--radius-md); - transition: var(--transition-smooth); - border: 2px solid transparent; - position: relative; - overflow: hidden; - transform-style: preserve-3d; - backface-visibility: hidden; + display: inline-block; + padding: 0.75rem 1.5rem; + font-weight: 500; + text-decoration: none; + border-radius: var(--radius-md); + transition: var(--transition-smooth); + border: 2px solid transparent; + position: relative; + overflow: hidden; + transform-style: preserve-3d; + backface-visibility: hidden; } .btn::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, - transparent, - rgba(255, 255, 255, 0.3), - transparent - ); - transition: var(--transition-elegant); - z-index: 1; + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); + transition: var(--transition-elegant); + z-index: 1; } .btn:hover::before { - left: 100%; + left: 100%; } .btn-primary { - background: linear-gradient(135deg, - var(--color-primary) 0%, - var(--color-primary-dark) 100% - ); - color: white; - box-shadow: var(--shadow-medium), var(--shadow-dynamic-tier1); + background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%); + color: white; + box-shadow: var(--shadow-medium), var(--shadow-dynamic-tier1); } .btn-primary:hover { - background: linear-gradient(135deg, - var(--color-primary-light) 0%, - var(--color-primary) 100% - ); - box-shadow: var(--shadow-elegant); + background: linear-gradient(135deg, var(--color-primary-light) 0%, var(--color-primary) 100%); + box-shadow: var(--shadow-elegant); } .btn-primary:active { - transition: var(--transition-quick); + transition: var(--transition-quick); } .btn-secondary { - background-color: transparent; - color: var(--color-primary); - border-color: var(--color-primary); + background-color: transparent; + color: var(--color-primary); + border-color: var(--color-primary); } .btn-secondary:hover { - background-color: var(--color-primary); - color: white; - box-shadow: var(--shadow-elegant); + background-color: var(--color-primary); + color: white; + box-shadow: var(--shadow-elegant); } .btn-secondary:active { - transition: var(--transition-quick); + transition: var(--transition-quick); } /* Expertise Section with Geometric Patterns */ .expertise { - padding: var(--spacing-xxl) 0; - background: linear-gradient(135deg, - var(--color-bg-secondary) 0%, - rgba(248, 249, 250, 0.8) 100% - ); - position: relative; + padding: var(--spacing-xxl) 0; + background: linear-gradient(135deg, var(--color-bg-secondary) 0%, rgba(248, 249, 250, 0.8) 100%); + position: relative; } .expertise::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: - radial-gradient(circle at 20% 20%, rgba(0, 102, 204, 0.03) 0%, transparent 50%), - radial-gradient(circle at 80% 80%, rgba(0, 102, 204, 0.02) 0%, transparent 50%), - radial-gradient(circle at 40% 60%, rgba(0, 102, 204, 0.01) 0%, transparent 50%); - pointer-events: none; + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + radial-gradient(circle at 20% 20%, rgba(0, 102, 204, 0.03) 0%, transparent 50%), + radial-gradient(circle at 80% 80%, rgba(0, 102, 204, 0.02) 0%, transparent 50%), + radial-gradient(circle at 40% 60%, rgba(0, 102, 204, 0.01) 0%, transparent 50%); + pointer-events: none; } .section-title { - font-size: 2.5rem; - font-weight: 600; - text-align: center; - margin-bottom: var(--spacing-xl); - background: linear-gradient(var(--gradient-angle), - var(--color-text) 0%, - var(--color-primary-dark) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - /* Mouse-responsive drop shadow */ - filter: drop-shadow(var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.15)); + font-size: 2.5rem; + font-weight: 600; + text-align: center; + margin-bottom: var(--spacing-xl); + background: linear-gradient( + var(--gradient-angle), + var(--color-text) 0%, + var(--color-primary-dark) 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + /* Mouse-responsive drop shadow */ + filter: drop-shadow(var(--shadow-x) var(--shadow-y) var(--shadow-blur) rgba(0, 0, 0, 0.15)); } .expertise-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: var(--spacing-lg); + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: var(--spacing-lg); } .expertise-card { - background: rgba(255, 255, 255, 0.95); - backdrop-filter: var(--blur-subtle); - padding: var(--spacing-lg); - border-radius: var(--radius-lg); - border: 1px solid rgba(229, 231, 235, 0.5); - box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); - transition: var(--transition-smooth); - position: relative; - overflow: hidden; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: var(--blur-subtle); + padding: var(--spacing-lg); + border-radius: var(--radius-lg); + border: 1px solid rgba(229, 231, 235, 0.5); + box-shadow: var(--shadow-subtle), var(--shadow-dynamic-tier2); + transition: var(--transition-smooth); + position: relative; + overflow: hidden; } .expertise-card::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: linear-gradient(90deg, - var(--color-primary) 0%, - var(--color-primary-light) 50%, - var(--color-primary) 100% - ); - transform: scaleX(0); - transition: var(--transition-elegant); + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient( + 90deg, + var(--color-primary) 0%, + var(--color-primary-light) 50%, + var(--color-primary) 100% + ); + transform: scaleX(0); + transition: var(--transition-elegant); } .expertise-card:hover { - box-shadow: var(--shadow-elegant); - border-color: rgba(0, 102, 204, 0.2); + box-shadow: var(--shadow-elegant); + border-color: rgba(0, 102, 204, 0.2); } .expertise-card:hover::before { - transform: scaleX(1); + transform: scaleX(1); } .expertise-icon { - width: 48px; - height: 48px; - margin-bottom: var(--spacing-md); - color: var(--color-primary); + width: 48px; + height: 48px; + margin-bottom: var(--spacing-md); + color: var(--color-primary); } .expertise-card h3 { - font-size: 1.25rem; - margin-bottom: var(--spacing-sm); + font-size: 1.25rem; + margin-bottom: var(--spacing-sm); } .expertise-card p { - color: var(--color-text-light); - line-height: 1.6; + color: var(--color-text-light); + line-height: 1.6; } /* Recent Work Section */ .recent-work { - padding: var(--spacing-xxl) 0; + padding: var(--spacing-xxl) 0; } .articles-preview { - text-align: center; - padding: var(--spacing-xl) 0; + text-align: center; + padding: var(--spacing-xl) 0; } .coming-soon { - color: var(--color-text-light); - font-style: italic; + color: var(--color-text-light); + font-style: italic; } /* Elegant Footer */ .site-footer { - background: linear-gradient(135deg, - var(--color-bg-secondary) 0%, - rgba(248, 249, 250, 0.9) 100% - ); - padding: var(--spacing-lg) 0; - margin-top: var(--spacing-xxl); - border-top: 1px solid rgba(229, 231, 235, 0.6); - position: relative; + background: linear-gradient(135deg, var(--color-bg-secondary) 0%, rgba(248, 249, 250, 0.9) 100%); + padding: var(--spacing-lg) 0; + margin-top: var(--spacing-xxl); + border-top: 1px solid rgba(229, 231, 235, 0.6); + position: relative; } .site-footer::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, - transparent 0%, - var(--color-primary) 50%, - transparent 100% - ); + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 1px; + background: linear-gradient(90deg, transparent 0%, var(--color-primary) 50%, transparent 100%); } .footer-content { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: var(--spacing-md); + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: var(--spacing-md); } .footer-nav { - display: flex; - gap: var(--spacing-lg); + display: flex; + gap: var(--spacing-lg); } .footer-nav a { - color: var(--color-text-light); - text-decoration: none; - transition: var(--transition-smooth); - position: relative; - padding: 0.25rem 0; + color: var(--color-text-light); + text-decoration: none; + transition: var(--transition-smooth); + position: relative; + padding: 0.25rem 0; } .footer-nav a::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 0; - height: 1px; - background: var(--color-primary); - transition: var(--transition-smooth); + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 1px; + background: var(--color-primary); + transition: var(--transition-smooth); } .footer-nav a:hover { - color: var(--color-primary); + color: var(--color-primary); } .footer-nav a:hover::after { - width: 100%; + width: 100%; } /* Responsive Design */ /* Enhanced mobile experience */ .menu-open { - overflow: hidden; + overflow: hidden; } @media (max-width: 768px) { - .nav-menu { - position: fixed; - left: -100%; - top: 64px; - flex-direction: column; - background: rgba(255, 255, 255, 0.95); - backdrop-filter: var(--blur-medium); - width: 100%; - padding: var(--spacing-lg); - box-shadow: var(--shadow-elegant); - transition: var(--transition-smooth); - border-bottom: 1px solid rgba(229, 231, 235, 0.6); - } - - .nav-menu.active { - left: 0; - } - - .nav-menu .nav-link { - padding: var(--spacing-sm) 0; - border-bottom: 1px solid rgba(229, 231, 235, 0.3); - transition: var(--transition-smooth); - } - - .nav-menu .nav-link:hover { - background: rgba(0, 102, 204, 0.05); - padding-left: var(--spacing-sm); - border-radius: var(--radius-sm); - } - - .nav-link.active::after { - display: none; - } - - .nav-toggle { - display: block; - } - - .hero-actions { - flex-direction: column; - align-items: center; - } - - .btn { - width: 100%; - max-width: 300px; - text-align: center; - } - - .footer-content { - flex-direction: column; - text-align: center; - } + .nav-menu { + position: fixed; + left: -100%; + top: 64px; + flex-direction: column; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: var(--blur-medium); + width: 100%; + padding: var(--spacing-lg); + box-shadow: var(--shadow-elegant); + transition: var(--transition-smooth); + border-bottom: 1px solid rgba(229, 231, 235, 0.6); + } + + .nav-menu.active { + left: 0; + } + + .nav-menu .nav-link { + padding: var(--spacing-sm) 0; + border-bottom: 1px solid rgba(229, 231, 235, 0.3); + transition: var(--transition-smooth); + } + + .nav-menu .nav-link:hover { + background: rgba(0, 102, 204, 0.05); + padding-left: var(--spacing-sm); + border-radius: var(--radius-sm); + } + + .nav-link.active::after { + display: none; + } + + .nav-toggle { + display: block; + } + + .hero-actions { + flex-direction: column; + align-items: center; + } + + .btn { + width: 100%; + max-width: 300px; + text-align: center; + } + + .footer-content { + flex-direction: column; + text-align: center; + } } /* Data Tables */ .data-table { - width: 100%; - border-collapse: collapse; - margin: 2rem 0; - background: var(--color-bg); - border-radius: var(--radius-md); - overflow: hidden; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), var(--shadow-dynamic-tier2); + width: 100%; + border-collapse: collapse; + margin: 2rem 0; + background: var(--color-bg); + border-radius: var(--radius-md); + overflow: hidden; + box-shadow: + 0 4px 6px rgba(0, 0, 0, 0.1), + var(--shadow-dynamic-tier2); } .data-table thead { - background: var(--color-primary); - color: white; + background: var(--color-primary); + color: white; } .data-table th, .data-table td { - padding: 1rem 1.5rem; - text-align: left; - border-bottom: 1px solid var(--color-border); + padding: 1rem 1.5rem; + text-align: left; + border-bottom: 1px solid var(--color-border); } .data-table th { - font-weight: 600; - font-size: 0.875rem; - text-transform: uppercase; - letter-spacing: 0.05em; + font-weight: 600; + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.05em; } .data-table tbody tr:hover { - background: rgba(99, 102, 241, 0.05); + background: rgba(99, 102, 241, 0.05); } .data-table tbody tr:last-child td { - border-bottom: none; + border-bottom: none; } /* CTA Section */ .cta-section { - background: linear-gradient(135deg, var(--color-bg) 0%, rgba(99, 102, 241, 0.1) 100%); - padding: 2rem; - border-radius: var(--radius-md); - margin: 3rem 0; - border: 1px solid var(--color-primary); + background: linear-gradient(135deg, var(--color-bg) 0%, rgba(99, 102, 241, 0.1) 100%); + padding: 2rem; + border-radius: var(--radius-md); + margin: 3rem 0; + border: 1px solid var(--color-primary); } .cta-section h3 { - color: var(--color-primary); - margin-bottom: 1rem; + color: var(--color-primary); + margin-bottom: 1rem; } .cta-section pre { - margin-top: 1rem; + margin-top: 1rem; } /* Latest Articles Section */ .latest-articles { - padding: 4rem 0; - background: var(--color-bg); + padding: 4rem 0; + background: var(--color-bg); } .latest-articles .section-title { - text-align: center; - font-size: 2.5rem; - margin-bottom: 3rem; - color: var(--color-text); + text-align: center; + font-size: 2.5rem; + margin-bottom: 3rem; + color: var(--color-text); } .articles-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); - gap: 2rem; - margin-bottom: 3rem; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; + margin-bottom: 3rem; } .article-card { - background: var(--color-bg); - border-radius: var(--radius-md); - padding: 2rem; - transition: transform 0.3s, box-shadow 0.3s; - border: 1px solid var(--color-border); - display: flex; - flex-direction: column; + background: var(--color-bg); + border-radius: var(--radius-md); + padding: 2rem; + transition: + transform 0.3s, + box-shadow 0.3s; + border: 1px solid var(--color-border); + display: flex; + flex-direction: column; } .article-card:hover { - transform: translateY(-4px); - box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); - border-color: var(--color-primary); + transform: translateY(-4px); + box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); + border-color: var(--color-primary); } .article-category { - display: inline-block; - padding: 0.25rem 0.75rem; - border-radius: 4px; - font-size: 0.875rem; - font-weight: 600; - text-transform: uppercase; - margin-bottom: 1rem; - width: fit-content; + display: inline-block; + padding: 0.25rem 0.75rem; + border-radius: 4px; + font-size: 0.875rem; + font-weight: 600; + text-transform: uppercase; + margin-bottom: 1rem; + width: fit-content; } .article-category.php { - background: rgba(147, 51, 234, 0.1); - color: #9333ea; + background: rgba(147, 51, 234, 0.1); + color: #9333ea; } .article-category.infrastructure { - background: rgba(34, 197, 94, 0.1); - color: #22c55e; + background: rgba(34, 197, 94, 0.1); + color: #22c55e; } .article-category.database { - background: rgba(59, 130, 246, 0.1); - color: #3b82f6; + background: rgba(59, 130, 246, 0.1); + color: #3b82f6; } .article-category.ai { - background: rgba(251, 146, 60, 0.1); - color: #fb923c; + background: rgba(251, 146, 60, 0.1); + color: #fb923c; } .article-category.typescript { - background: rgba(6, 182, 212, 0.1); - color: #06b6d4; + background: rgba(6, 182, 212, 0.1); + color: #06b6d4; } .article-title { - font-size: 1.5rem; - margin-bottom: 1rem; - flex-grow: 1; + font-size: 1.5rem; + margin-bottom: 1rem; + flex-grow: 1; } .article-title a { - color: var(--color-text); - text-decoration: none; - transition: color 0.3s; + color: var(--color-text); + text-decoration: none; + transition: color 0.3s; } .article-title a:hover { - color: var(--color-primary); + color: var(--color-primary); } .article-excerpt { - color: var(--color-text-light); - line-height: 1.6; - margin-bottom: 1.5rem; + color: var(--color-text-light); + line-height: 1.6; + margin-bottom: 1.5rem; } .article-meta { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.875rem; - color: var(--color-text-light); - padding-top: 1rem; - border-top: 1px solid var(--color-border); + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.875rem; + color: var(--color-text-light); + padding-top: 1rem; + border-top: 1px solid var(--color-border); } .articles-cta { - text-align: center; + text-align: center; } .articles-cta .btn { - padding: 0.75rem 2rem; - font-size: 1rem; -} \ No newline at end of file + padding: 0.75rem 2rem; + font-size: 1rem; +} diff --git a/ARCHIVE/private_html/css/services.css b/ARCHIVE/private_html/css/services.css index 19bbb400..01614ec8 100644 --- a/ARCHIVE/private_html/css/services.css +++ b/ARCHIVE/private_html/css/services.css @@ -1,134 +1,134 @@ /* Services Page Specific Styles */ .page-hero { - padding: var(--spacing-xl) 0; - background-color: var(--color-bg-secondary); - text-align: center; + padding: var(--spacing-xl) 0; + background-color: var(--color-bg-secondary); + text-align: center; } .page-title { - font-size: 2.5rem; - font-weight: 600; - margin-bottom: var(--spacing-sm); + font-size: 2.5rem; + font-weight: 600; + margin-bottom: var(--spacing-sm); } .page-subtitle { - font-size: 1.25rem; - color: var(--color-text-light); + font-size: 1.25rem; + color: var(--color-text-light); } .services-detail { - padding: var(--spacing-xxl) 0; + padding: var(--spacing-xxl) 0; } .service-section { - margin-bottom: var(--spacing-xxl); - padding-bottom: var(--spacing-xxl); - border-bottom: 1px solid var(--color-border); + margin-bottom: var(--spacing-xxl); + padding-bottom: var(--spacing-xxl); + border-bottom: 1px solid var(--color-border); } .service-section:last-of-type { - border-bottom: none; + border-bottom: none; } .service-header h2 { - font-size: 2rem; - margin-bottom: var(--spacing-lg); - color: var(--color-text); + font-size: 2rem; + margin-bottom: var(--spacing-lg); + color: var(--color-text); } .service-content { - display: grid; - gap: var(--spacing-lg); + display: grid; + gap: var(--spacing-lg); } .service-description { - font-size: 1.125rem; - line-height: 1.7; - color: var(--color-text-light); + font-size: 1.125rem; + line-height: 1.7; + color: var(--color-text-light); } .service-features h3, .tech-stack h3 { - font-size: 1.25rem; - margin-bottom: var(--spacing-md); - color: var(--color-text); + font-size: 1.25rem; + margin-bottom: var(--spacing-md); + color: var(--color-text); } .feature-list { - list-style: none; - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: var(--spacing-sm); + list-style: none; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: var(--spacing-sm); } .feature-list li { - position: relative; - padding-left: 1.5rem; - color: var(--color-text-light); - line-height: 1.6; + position: relative; + padding-left: 1.5rem; + color: var(--color-text-light); + line-height: 1.6; } .feature-list li::before { - content: "✓"; - position: absolute; - left: 0; - color: var(--color-primary); - font-weight: 600; + content: '✓'; + position: absolute; + left: 0; + color: var(--color-primary); + font-weight: 600; } .tech-tags { - display: flex; - flex-wrap: wrap; - gap: var(--spacing-xs); + display: flex; + flex-wrap: wrap; + gap: var(--spacing-xs); } .tech-tag { - display: inline-block; - padding: 0.25rem 0.75rem; - background-color: var(--color-bg-secondary); - border: 1px solid var(--color-border); - border-radius: 20px; - font-size: 0.875rem; - color: var(--color-text); - transition: var(--transition); + display: inline-block; + padding: 0.25rem 0.75rem; + background-color: var(--color-bg-secondary); + border: 1px solid var(--color-border); + border-radius: 20px; + font-size: 0.875rem; + color: var(--color-text); + transition: var(--transition); } .tech-tag:hover { - background-color: var(--color-primary); - color: white; - border-color: var(--color-primary); + background-color: var(--color-primary); + color: white; + border-color: var(--color-primary); } .cta-section { - text-align: center; - padding: var(--spacing-xxl) 0; - background-color: var(--color-bg-secondary); - border-radius: 8px; - margin-top: var(--spacing-xxl); + text-align: center; + padding: var(--spacing-xxl) 0; + background-color: var(--color-bg-secondary); + border-radius: 8px; + margin-top: var(--spacing-xxl); } .cta-section h2 { - font-size: 2rem; - margin-bottom: var(--spacing-sm); + font-size: 2rem; + margin-bottom: var(--spacing-sm); } .cta-section p { - font-size: 1.125rem; - color: var(--color-text-light); - margin-bottom: var(--spacing-lg); + font-size: 1.125rem; + color: var(--color-text-light); + margin-bottom: var(--spacing-lg); } @media (max-width: 768px) { - .page-title { - font-size: 2rem; - } - - .service-header h2 { - font-size: 1.75rem; - } - - .feature-list { - grid-template-columns: 1fr; - } -} \ No newline at end of file + .page-title { + font-size: 2rem; + } + + .service-header h2 { + font-size: 1.75rem; + } + + .feature-list { + grid-template-columns: 1fr; + } +} diff --git a/ARCHIVE/private_html/css/syntax-highlighting.css b/ARCHIVE/private_html/css/syntax-highlighting.css index bf31399e..72765628 100644 --- a/ARCHIVE/private_html/css/syntax-highlighting.css +++ b/ARCHIVE/private_html/css/syntax-highlighting.css @@ -2,43 +2,45 @@ /* Code blocks with dark background matching terminal windows */ pre { - background-color: #2b2b2b; - border-radius: 8px; - padding: 1.25rem; - overflow-x: auto; - margin: 1.5rem 0; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - color: #a9b7c6; /* Light gray base text */ - font-size: 14px; - line-height: 1.6; + background-color: #2b2b2b; + border-radius: 8px; + padding: 1.25rem; + overflow-x: auto; + margin: 1.5rem 0; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + color: #a9b7c6; /* Light gray base text */ + font-size: 14px; + line-height: 1.6; } /* Inline code */ code { - background-color: #2b2b2b; - padding: 0.2rem 0.5rem; - border-radius: 4px; - font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace; - font-size: 0.9em; - color: #a9b7c6; - border: 1px solid #3c3f41; + background-color: #2b2b2b; + padding: 0.2rem 0.5rem; + border-radius: 4px; + font-family: + 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', + monospace; + font-size: 0.9em; + color: #a9b7c6; + border: 1px solid #3c3f41; } /* Code inside pre blocks */ pre code { - background-color: transparent; - padding: 0; - border-radius: 0; - white-space: pre; - display: block; - border: none; - color: #a9b7c6; /* Light gray base text */ + background-color: transparent; + padding: 0; + border-radius: 0; + white-space: pre; + display: block; + border: none; + color: #a9b7c6; /* Light gray base text */ } /* Highlight.js base styling */ .hljs { - background-color: #2b2b2b; - color: #a9b7c6; /* Light gray base text, not blue */ + background-color: #2b2b2b; + color: #a9b7c6; /* Light gray base text, not blue */ } /* JetBrains Darcula Color Scheme */ @@ -49,8 +51,8 @@ pre code { .token.doctype, .token.cdata, .hljs-comment { - color: #629755; - font-style: italic; + color: #629755; + font-style: italic; } /* Strings - Green */ @@ -61,7 +63,7 @@ pre code { .token.variable, .hljs-string, .hljs-attr { - color: #6a8759; + color: #6a8759; } /* Keywords - Orange */ @@ -73,8 +75,8 @@ pre code { .token.atrule, .hljs-keyword, .hljs-built_in { - color: #cc7832; - font-weight: bold; + color: #cc7832; + font-weight: bold; } /* Functions and Methods - Yellow */ @@ -82,7 +84,7 @@ pre code { .token.method, .hljs-function, .hljs-title.function_ { - color: #ffc66d; + color: #ffc66d; } /* Classes and Types - Light */ @@ -91,7 +93,7 @@ pre code { .token.namespace, .hljs-class .hljs-title, .hljs-type { - color: #a9b7c6; + color: #a9b7c6; } /* Numbers - Blue */ @@ -100,14 +102,14 @@ pre code { .token.constant, .hljs-number, .hljs-literal { - color: #6897bb; + color: #6897bb; } /* Operators and Punctuation */ .token.operator, .token.punctuation, .hljs-operator { - color: #a9b7c6; + color: #a9b7c6; } /* Properties and Attributes - Purple/Magenta */ @@ -118,7 +120,7 @@ pre code { .hljs-attribute, .hljs-variable, .hljs-params { - color: #9876aa; + color: #9876aa; } /* Selectors - Orange */ @@ -127,26 +129,26 @@ pre code { .token.pseudo-class, .hljs-selector-class, .hljs-selector-id { - color: #ffc66d; + color: #ffc66d; } /* Tags - Yellow */ .token.tag, .token.tag.punctuation, .hljs-tag { - color: #e8bf6a; + color: #e8bf6a; } /* Insertions - Green background */ .token.inserted { - color: #a6e22e; - background-color: rgba(166, 226, 46, 0.1); + color: #a6e22e; + background-color: rgba(166, 226, 46, 0.1); } /* Deletions - Red background */ .token.deleted { - color: #f92672; - background-color: rgba(249, 38, 114, 0.1); + color: #f92672; + background-color: rgba(249, 38, 114, 0.1); } /* Entity */ @@ -154,164 +156,164 @@ pre code { .token.url, .language-css .token.string, .style .token.string { - color: #6897bb; + color: #6897bb; } /* Bold */ .token.important, .token.bold, .hljs-strong { - font-weight: bold; + font-weight: bold; } /* Italic */ .token.italic, .hljs-emphasis { - font-style: italic; + font-style: italic; } /* Cursor */ .token.entity { - cursor: help; + cursor: help; } /* Language-specific adjustments */ /* PHP */ .language-php .token.variable { - color: #9876aa; + color: #9876aa; } .language-php .token.keyword { - color: #cc7832; + color: #cc7832; } /* JavaScript/TypeScript */ .language-javascript .token.keyword, .language-typescript .token.keyword { - color: #cc7832; + color: #cc7832; } .language-javascript .token.string, .language-typescript .token.string { - color: #6a8759; + color: #6a8759; } /* CSS */ .language-css .token.selector { - color: #ffc66d; + color: #ffc66d; } .language-css .token.property { - color: #a9b7c6; + color: #a9b7c6; } .language-css .token.important { - color: #cc7832; + color: #cc7832; } /* SQL */ .language-sql .token.keyword { - color: #cc7832; - font-weight: bold; + color: #cc7832; + font-weight: bold; } .language-sql .token.function { - color: #ffc66d; + color: #ffc66d; } /* Bash/Shell */ .language-bash .token.function, .language-shell .token.function { - color: #ffc66d; + color: #ffc66d; } .language-bash .token.builtin, .language-shell .token.builtin { - color: #cc7832; + color: #cc7832; } /* YAML */ .language-yaml .token.key { - color: #9876aa; + color: #9876aa; } /* JSON */ .language-json .token.property { - color: #9876aa; + color: #9876aa; } /* Markdown */ .language-markdown .token.title { - color: #ffc66d; - font-weight: bold; + color: #ffc66d; + font-weight: bold; } .language-markdown .token.code { - color: #6a8759; + color: #6a8759; } /* Line numbers (if used) */ .line-numbers .line-numbers-rows { - border-right: 1px solid #3c3f41; + border-right: 1px solid #3c3f41; } .line-numbers-rows > span:before { - color: #606366; + color: #606366; } /* Selection */ pre::selection, pre code::selection, code::selection { - background-color: #214283; - color: inherit; + background-color: #214283; + color: inherit; } pre::-moz-selection, pre code::-moz-selection, code::-moz-selection { - background-color: #214283; - color: inherit; + background-color: #214283; + color: inherit; } /* Scrollbar styling for code blocks */ pre::-webkit-scrollbar { - height: 8px; - width: 8px; + height: 8px; + width: 8px; } pre::-webkit-scrollbar-track { - background: #1e1e1e; - border-radius: 4px; + background: #1e1e1e; + border-radius: 4px; } pre::-webkit-scrollbar-thumb { - background: #4a4a4a; - border-radius: 4px; + background: #4a4a4a; + border-radius: 4px; } pre::-webkit-scrollbar-thumb:hover { - background: #5a5a5a; + background: #5a5a5a; } /* Ensure consistency with terminal windows */ pre, .terminal-window { - font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace; + font-family: 'JetBrains Mono', 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace; } /* Special highlighting for diffs */ .token.coord { - color: #6897bb; + color: #6897bb; } .token.diff.deleted { - color: #cc7832; - background-color: rgba(255, 0, 0, 0.1); + color: #cc7832; + background-color: rgba(255, 0, 0, 0.1); } .token.diff.inserted { - color: #629755; - background-color: rgba(0, 255, 0, 0.1); -} \ No newline at end of file + color: #629755; + background-color: rgba(0, 255, 0, 0.1); +} diff --git a/ARCHIVE/private_html/css/terminal.css b/ARCHIVE/private_html/css/terminal.css index 1fdf3a42..4a239378 100644 --- a/ARCHIVE/private_html/css/terminal.css +++ b/ARCHIVE/private_html/css/terminal.css @@ -1,151 +1,157 @@ /* Terminal Window Styling - Site-wide */ .terminal-window { - background: #1e1e1e; - border-radius: 8px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); - margin: 20px 0; - overflow: hidden; - font-size: 14px; + background: #1e1e1e; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); + margin: 20px 0; + overflow: hidden; + font-size: 14px; } .terminal-header { - background: #323232; - padding: 8px 12px; - display: flex; - align-items: center; - gap: 8px; - font-size: 13px; - color: #999; + background: #323232; + padding: 8px 12px; + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + color: #999; } .terminal-dot { - width: 12px; - height: 12px; - border-radius: 50%; - background: #ff5f56; + width: 12px; + height: 12px; + border-radius: 50%; + background: #ff5f56; } -.terminal-dot:nth-child(2) { - background: #ffbd2e; +.terminal-dot:nth-child(2) { + background: #ffbd2e; } -.terminal-dot:nth-child(3) { - background: #27c93f; +.terminal-dot:nth-child(3) { + background: #27c93f; } .terminal-title { - margin-left: auto; - margin-right: auto; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + margin-left: auto; + margin-right: auto; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } .terminal-content { - padding: 16px; - font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace; - font-size: 14px; - line-height: 1.6; - color: #d4d4d4; - background: #1e1e1e; + padding: 16px; + font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace; + font-size: 14px; + line-height: 1.6; + color: #d4d4d4; + background: #1e1e1e; } /* Terminal text colors */ .terminal-prompt { - color: #569cd6; - font-weight: bold; + color: #569cd6; + font-weight: bold; } .terminal-command { - color: #ce9178; + color: #ce9178; } .terminal-output { - color: #9cdcfe; - margin-left: 0; - display: block; - margin-top: 4px; + color: #9cdcfe; + margin-left: 0; + display: block; + margin-top: 4px; } .terminal-user { - color: #4ec9b0; + color: #4ec9b0; } .terminal-assistant { - color: #dcdcaa; + color: #dcdcaa; } .terminal-comment { - color: #6a9955; - font-style: italic; + color: #6a9955; + font-style: italic; } .terminal-error { - color: #f48771; + color: #f48771; } .terminal-success { - color: #4ec9b0; + color: #4ec9b0; } .terminal-warning { - color: #ffbd2e; + color: #ffbd2e; } .terminal-divider { - color: #666; - margin: 10px 0; - text-align: center; - border-top: 1px solid #444; - padding-top: 10px; + color: #666; + margin: 10px 0; + text-align: center; + border-top: 1px solid #444; + padding-top: 10px; } /* Conversation flow in terminal */ .terminal-conversation { - margin: 8px 0; + margin: 8px 0; } .terminal-conversation .terminal-user { - margin-bottom: 8px; + margin-bottom: 8px; } .terminal-conversation .terminal-assistant { - margin-bottom: 8px; - padding-left: 20px; + margin-bottom: 8px; + padding-left: 20px; } /* Compact terminal for inline examples */ .terminal-inline { - display: inline-block; - background: #1e1e1e; - color: #d4d4d4; - padding: 2px 8px; - border-radius: 4px; - font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace; - font-size: 0.9em; + display: inline-block; + background: #1e1e1e; + color: #d4d4d4; + padding: 2px 8px; + border-radius: 4px; + font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace; + font-size: 0.9em; } /* Terminal with no header (for quick examples) */ .terminal-simple { - background: #1e1e1e; - border-radius: 8px; - padding: 16px; - margin: 20px 0; - font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace; - font-size: 14px; - line-height: 1.6; - color: #d4d4d4; + background: #1e1e1e; + border-radius: 8px; + padding: 16px; + margin: 20px 0; + font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', monospace; + font-size: 14px; + line-height: 1.6; + color: #d4d4d4; } /* Animation for typing effect (optional) */ @keyframes terminal-blink { - 0%, 50% { opacity: 1; } - 51%, 100% { opacity: 0; } + 0%, + 50% { + opacity: 1; + } + 51%, + 100% { + opacity: 0; + } } .terminal-cursor { - display: inline-block; - width: 8px; - height: 16px; - background: #d4d4d4; - animation: terminal-blink 1s infinite; - vertical-align: text-bottom; -} \ No newline at end of file + display: inline-block; + width: 8px; + height: 16px; + background: #d4d4d4; + animation: terminal-blink 1s infinite; + vertical-align: text-bottom; +} diff --git a/ARCHIVE/private_html/data/categories.json b/ARCHIVE/private_html/data/categories.json index a5cda772..942f6903 100644 --- a/ARCHIVE/private_html/data/categories.json +++ b/ARCHIVE/private_html/data/categories.json @@ -29,4 +29,4 @@ "backgroundColor": "#e1f5fe", "textColor": "#0277bd" } -} \ No newline at end of file +} diff --git a/ARCHIVE/private_html/data/navigation.json b/ARCHIVE/private_html/data/navigation.json index a52ff9e3..f50b0b03 100644 --- a/ARCHIVE/private_html/data/navigation.json +++ b/ARCHIVE/private_html/data/navigation.json @@ -8,4 +8,4 @@ { "label": "Author", "path": "/author.html", "key": "author" }, { "label": "Contact", "path": "/contact.html", "key": "contact" } ] -} \ No newline at end of file +} diff --git a/ARCHIVE/private_html/data/site.json b/ARCHIVE/private_html/data/site.json index 3d7def3d..c88131ac 100644 --- a/ARCHIVE/private_html/data/site.json +++ b/ARCHIVE/private_html/data/site.json @@ -18,4 +18,4 @@ "language": "en", "charset": "UTF-8" } -} \ No newline at end of file +} diff --git a/ARCHIVE/private_html/js/about.js b/ARCHIVE/private_html/js/about.js index 79486667..5d893b41 100644 --- a/ARCHIVE/private_html/js/about.js +++ b/ARCHIVE/private_html/js/about.js @@ -3,7 +3,7 @@ import '../css/main.css'; import '../css/about.css'; // About page functionality -document.addEventListener('DOMContentLoaded', function() { - // Add any about page specific functionality here - console.log('About page loaded'); -}); \ No newline at end of file +document.addEventListener('DOMContentLoaded', function () { + // Add any about page specific functionality here + console.log('About page loaded'); +}); diff --git a/ARCHIVE/private_html/js/articles-hybrid.js b/ARCHIVE/private_html/js/articles-hybrid.js index 2250e1c9..f1741434 100644 --- a/ARCHIVE/private_html/js/articles-hybrid.js +++ b/ARCHIVE/private_html/js/articles-hybrid.js @@ -8,36 +8,36 @@ const articles = [ ]; const categories = { - "php": { - "label": "PHP", - "description": "PHP development, frameworks, best practices", - "backgroundColor": "#f3e5f5", - "textColor": "#7b1fa2" + php: { + label: 'PHP', + description: 'PHP development, frameworks, best practices', + backgroundColor: '#f3e5f5', + textColor: '#7b1fa2', }, - "infrastructure": { - "label": "Infrastructure", - "description": "DevOps, hosting, deployment, automation", - "backgroundColor": "#e8f5e9", - "textColor": "#388e3c" + infrastructure: { + label: 'Infrastructure', + description: 'DevOps, hosting, deployment, automation', + backgroundColor: '#e8f5e9', + textColor: '#388e3c', }, - "database": { - "label": "Database", - "description": "MySQL, PostgreSQL, optimization, architecture", - "backgroundColor": "#e3f2fd", - "textColor": "#1976d2" + database: { + label: 'Database', + description: 'MySQL, PostgreSQL, optimization, architecture', + backgroundColor: '#e3f2fd', + textColor: '#1976d2', }, - "ai": { - "label": "AI", - "description": "AI tools, ML integration, automation", - "backgroundColor": "#fff3e0", - "textColor": "#f57c00" + ai: { + label: 'AI', + description: 'AI tools, ML integration, automation', + backgroundColor: '#fff3e0', + textColor: '#f57c00', + }, + typescript: { + label: 'TypeScript', + description: 'TypeScript, Node.js, modern JavaScript', + backgroundColor: '#e1f5fe', + textColor: '#0277bd', }, - "typescript": { - "label": "TypeScript", - "description": "TypeScript, Node.js, modern JavaScript", - "backgroundColor": "#e1f5fe", - "textColor": "#0277bd" - } }; class HybridArticleManager { @@ -49,7 +49,7 @@ class HybridArticleManager { this.isStaticMode = true; this.currentPage = 1; this.articlesPerPage = 6; // For dynamic pagination - + // Detect if we're on a static paginated page this.detectPageMode(); this.init(); @@ -84,7 +84,7 @@ class HybridArticleManager { } this.filterArticles(); }; - + // Use debounce if available if (window.appUtils && window.appUtils.debounce) { searchInput.addEventListener('input', window.appUtils.debounce(handler, 300)); @@ -106,32 +106,32 @@ class HybridArticleManager { }); button.classList.add('active'); this.currentCategory = button.dataset.category; - + // Switch to dynamic mode when filtering (unless "All" on page 1) if (this.currentCategory !== 'all' || this.currentPage !== 1) { this.switchToDynamicMode(); } - + this.filterArticles(); } switchToDynamicMode() { if (this.isStaticMode) { this.isStaticMode = false; - + // Hide static pagination const paginationTop = document.querySelector('.pagination-top'); const paginationBottom = document.querySelector('.pagination-bottom'); if (paginationTop) paginationTop.style.display = 'none'; if (paginationBottom) paginationBottom.style.display = 'none'; - + // Update help text const helpText = document.querySelector('.filter-help'); if (helpText) { helpText.innerHTML = '✨ Dynamic mode active - showing filtered results from all articles.'; helpText.style.color = '#388e3c'; } - + // Show dynamic pagination placeholder this.showDynamicPagination(); } @@ -147,13 +147,15 @@ class HybridArticleManager { filterArticles() { this.filteredArticles = this.articles.filter(article => { - const categoryMatch = this.currentCategory === 'all' || article.category === this.currentCategory; - const searchMatch = this.searchTerm === '' || + const categoryMatch = + this.currentCategory === 'all' || article.category === this.currentCategory; + const searchMatch = + this.searchTerm === '' || article.title.toLowerCase().includes(this.searchTerm) || article.excerpt.toLowerCase().includes(this.searchTerm); return categoryMatch && searchMatch; }); - + if (!this.isStaticMode) { this.currentPage = 1; // Reset to first page when filtering this.renderArticles(); @@ -164,29 +166,29 @@ class HybridArticleManager { renderArticles() { const grid = document.getElementById('articlesGrid'); const noResults = document.getElementById('noResults'); - + if (!grid) return; // In static mode, don't re-render if (this.isStaticMode) return; - + if (this.filteredArticles.length === 0) { grid.style.display = 'none'; if (noResults) noResults.style.display = 'block'; return; } - + grid.style.display = 'grid'; if (noResults) noResults.style.display = 'none'; - + // Calculate articles for current page in dynamic mode const startIndex = (this.currentPage - 1) * this.articlesPerPage; const endIndex = Math.min(startIndex + this.articlesPerPage, this.filteredArticles.length); const pageArticles = this.filteredArticles.slice(startIndex, endIndex); - + grid.innerHTML = pageArticles.map(article => this.createArticleCard(article)).join(''); grid.classList.remove('static-page'); // Remove static class - + // Animate articles requestAnimationFrame(() => { grid.querySelectorAll('.article-card').forEach((card, index) => { @@ -202,11 +204,11 @@ class HybridArticleManager { const date = new Date(article.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', - day: 'numeric' + day: 'numeric', }); - + const categoryLabel = categories[article.category]?.label || article.category; - + return `