diff --git a/.github/workflows/lint-check.yml b/.github/workflows/lint-check.yml index 7fb420377..e6ffa7f07 100644 --- a/.github/workflows/lint-check.yml +++ b/.github/workflows/lint-check.yml @@ -53,6 +53,11 @@ jobs: cd frontend pnpm install --frozen-lockfile + - name: Check frontend formatting + run: | + cd frontend + pnpm format + - name: Run frontend linting run: | cd frontend diff --git a/backend/Makefile b/backend/Makefile index adc862857..8fe28c985 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -12,6 +12,7 @@ test: lint: uvx ruff check . + uvx ruff format --check . format: uvx ruff check . --fix && uvx ruff format . diff --git a/backend/app/channels/manager.py b/backend/app/channels/manager.py index 8ebe7a748..3e3fa17d4 100644 --- a/backend/app/channels/manager.py +++ b/backend/app/channels/manager.py @@ -66,14 +66,9 @@ def _normalize_custom_agent_name(raw_value: str) -> str: """Normalize legacy channel assistant IDs into valid custom agent names.""" normalized = raw_value.strip().lower().replace("_", "-") if not normalized: - raise InvalidChannelSessionConfigError( - "Channel session assistant_id is empty. Use 'lead_agent' or a valid custom agent name." - ) + raise InvalidChannelSessionConfigError("Channel session assistant_id is empty. Use 'lead_agent' or a valid custom agent name.") if not CUSTOM_AGENT_NAME_PATTERN.fullmatch(normalized): - raise InvalidChannelSessionConfigError( - f"Invalid channel session assistant_id {raw_value!r}. " - "Use 'lead_agent' or a custom agent name containing only letters, digits, and hyphens." - ) + raise InvalidChannelSessionConfigError(f"Invalid channel session assistant_id {raw_value!r}. Use 'lead_agent' or a custom agent name containing only letters, digits, and hyphens.") return normalized diff --git a/backend/app/gateway/routers/uploads.py b/backend/app/gateway/routers/uploads.py index 27b49418d..9d9d0c9bc 100644 --- a/backend/app/gateway/routers/uploads.py +++ b/backend/app/gateway/routers/uploads.py @@ -48,9 +48,7 @@ def _make_file_sandbox_writable(file_path: os.PathLike[str] | str) -> None: logger.warning("Skipping sandbox chmod for symlinked upload path: %s", file_path) return - writable_mode = ( - stat.S_IMODE(file_stat.st_mode) | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH - ) + writable_mode = stat.S_IMODE(file_stat.st_mode) | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH chmod_kwargs = {"follow_symlinks": False} if os.chmod in os.supports_follow_symlinks else {} os.chmod(file_path, writable_mode, **chmod_kwargs) diff --git a/backend/packages/harness/deerflow/agents/memory/storage.py b/backend/packages/harness/deerflow/agents/memory/storage.py index fcd1298e9..ba941a7a4 100644 --- a/backend/packages/harness/deerflow/agents/memory/storage.py +++ b/backend/packages/harness/deerflow/agents/memory/storage.py @@ -71,9 +71,7 @@ class FileMemoryStorage(MemoryStorage): if not agent_name: raise ValueError("Agent name must be a non-empty string.") if not AGENT_NAME_PATTERN.match(agent_name): - raise ValueError( - f"Invalid agent name {agent_name!r}: names must match {AGENT_NAME_PATTERN.pattern}" - ) + raise ValueError(f"Invalid agent name {agent_name!r}: names must match {AGENT_NAME_PATTERN.pattern}") def _get_memory_file_path(self, agent_name: str | None = None) -> Path: """Get the path to the memory file.""" @@ -180,18 +178,15 @@ def get_memory_storage() -> MemoryStorage: try: module_path, class_name = storage_class_path.rsplit(".", 1) import importlib + module = importlib.import_module(module_path) storage_class = getattr(module, class_name) # Validate that the configured storage is a MemoryStorage implementation if not isinstance(storage_class, type): - raise TypeError( - f"Configured memory storage '{storage_class_path}' is not a class: {storage_class!r}" - ) + raise TypeError(f"Configured memory storage '{storage_class_path}' is not a class: {storage_class!r}") if not issubclass(storage_class, MemoryStorage): - raise TypeError( - f"Configured memory storage '{storage_class_path}' is not a subclass of MemoryStorage" - ) + raise TypeError(f"Configured memory storage '{storage_class_path}' is not a subclass of MemoryStorage") _storage_instance = storage_class() except Exception as e: diff --git a/backend/packages/harness/deerflow/agents/memory/updater.py b/backend/packages/harness/deerflow/agents/memory/updater.py index 65da18ef4..75b9e7b54 100644 --- a/backend/packages/harness/deerflow/agents/memory/updater.py +++ b/backend/packages/harness/deerflow/agents/memory/updater.py @@ -27,10 +27,12 @@ def _save_memory_to_file(memory_data: dict[str, Any], agent_name: str | None = N """Backward-compatible wrapper around the configured memory storage save path.""" return get_memory_storage().save(memory_data, agent_name) + def get_memory_data(agent_name: str | None = None) -> dict[str, Any]: """Get the current memory data via storage provider.""" return get_memory_storage().load(agent_name) + def reload_memory_data(agent_name: str | None = None) -> dict[str, Any]: """Reload memory data via storage provider.""" return get_memory_storage().reload(agent_name) diff --git a/backend/packages/harness/deerflow/models/claude_provider.py b/backend/packages/harness/deerflow/models/claude_provider.py index a53939acf..1e732bd79 100644 --- a/backend/packages/harness/deerflow/models/claude_provider.py +++ b/backend/packages/harness/deerflow/models/claude_provider.py @@ -162,10 +162,7 @@ class ClaudeChatModel(ChatAnthropic): system = payload.get("system") if isinstance(system, list): # Remove any existing billing blocks, then insert a single one at index 0. - filtered = [ - b for b in system - if not (isinstance(b, dict) and OAUTH_BILLING_HEADER in b.get("text", "")) - ] + filtered = [b for b in system if not (isinstance(b, dict) and OAUTH_BILLING_HEADER in b.get("text", ""))] payload["system"] = [billing_block] + filtered elif isinstance(system, str): if OAUTH_BILLING_HEADER in system: @@ -183,11 +180,13 @@ class ClaudeChatModel(ChatAnthropic): hostname = socket.gethostname() device_id = hashlib.sha256(f"deerflow-{hostname}".encode()).hexdigest() session_id = str(uuid.uuid4()) - payload["metadata"]["user_id"] = json.dumps({ - "device_id": device_id, - "account_uuid": "deerflow", - "session_id": session_id, - }) + payload["metadata"]["user_id"] = json.dumps( + { + "device_id": device_id, + "account_uuid": "deerflow", + "session_id": session_id, + } + ) def _apply_prompt_caching(self, payload: dict) -> None: """Apply ephemeral cache_control to system and recent messages.""" diff --git a/backend/packages/harness/deerflow/models/patched_openai.py b/backend/packages/harness/deerflow/models/patched_openai.py index f2fe2427c..9a7801f48 100644 --- a/backend/packages/harness/deerflow/models/patched_openai.py +++ b/backend/packages/harness/deerflow/models/patched_openai.py @@ -84,9 +84,7 @@ class PatchedChatOpenAI(ChatOpenAI): else: # Fallback: match assistant-role entries positionally against AIMessages. ai_messages = [m for m in original_messages if isinstance(m, AIMessage)] - assistant_payloads = [ - (i, m) for i, m in enumerate(payload_messages) if m.get("role") == "assistant" - ] + assistant_payloads = [(i, m) for i, m in enumerate(payload_messages) if m.get("role") == "assistant"] for (_, payload_msg), ai_msg in zip(assistant_payloads, ai_messages): _restore_tool_call_signatures(payload_msg, ai_msg) diff --git a/backend/packages/harness/deerflow/sandbox/tools.py b/backend/packages/harness/deerflow/sandbox/tools.py index 6cdcac1e9..414326a92 100644 --- a/backend/packages/harness/deerflow/sandbox/tools.py +++ b/backend/packages/harness/deerflow/sandbox/tools.py @@ -100,7 +100,7 @@ def _resolve_skills_path(path: str) -> str: if path == skills_container: return skills_host - relative = path[len(skills_container):].lstrip("/") + relative = path[len(skills_container) :].lstrip("/") return _join_path_preserving_style(skills_host, relative) diff --git a/backend/packages/harness/deerflow/tools/builtins/task_tool.py b/backend/packages/harness/deerflow/tools/builtins/task_tool.py index 55a41e3da..e1f8987a4 100644 --- a/backend/packages/harness/deerflow/tools/builtins/task_tool.py +++ b/backend/packages/harness/deerflow/tools/builtins/task_tool.py @@ -197,6 +197,7 @@ async def task_tool( writer({"type": "task_timed_out", "task_id": task_id}) return f"Task polling timed out after {timeout_minutes} minutes. This may indicate the background task is stuck. Status: {result.status.value}" except asyncio.CancelledError: + async def cleanup_when_done() -> None: max_cleanup_polls = max_poll_count cleanup_poll_count = 0 @@ -211,9 +212,7 @@ async def task_tool( return if cleanup_poll_count > max_cleanup_polls: - logger.warning( - f"[trace={trace_id}] Deferred cleanup for task {task_id} timed out after {cleanup_poll_count} polls" - ) + logger.warning(f"[trace={trace_id}] Deferred cleanup for task {task_id} timed out after {cleanup_poll_count} polls") return await asyncio.sleep(5) diff --git a/backend/packages/harness/deerflow/tools/builtins/tool_search.py b/backend/packages/harness/deerflow/tools/builtins/tool_search.py index d9fb986c2..e58bf014c 100644 --- a/backend/packages/harness/deerflow/tools/builtins/tool_search.py +++ b/backend/packages/harness/deerflow/tools/builtins/tool_search.py @@ -118,9 +118,7 @@ def _regex_score(pattern: str, entry: DeferredToolEntry) -> int: # loop.run_in_executor, Python copies the current context to the worker thread, # so the ContextVar value is correctly inherited there too. -_registry_var: contextvars.ContextVar[DeferredToolRegistry | None] = contextvars.ContextVar( - "deferred_tool_registry", default=None -) +_registry_var: contextvars.ContextVar[DeferredToolRegistry | None] = contextvars.ContextVar("deferred_tool_registry", default=None) def get_deferred_registry() -> DeferredToolRegistry | None: diff --git a/backend/tests/test_channels.py b/backend/tests/test_channels.py index 7ec5c4149..c48137c76 100644 --- a/backend/tests/test_channels.py +++ b/backend/tests/test_channels.py @@ -600,10 +600,7 @@ class TestChannelManager: await manager.stop() mock_client.runs.wait.assert_not_called() - assert outbound_received[0].text == ( - "Invalid channel session assistant_id 'bad agent!'. " - "Use 'lead_agent' or a custom agent name containing only letters, digits, and hyphens." - ) + assert outbound_received[0].text == ("Invalid channel session assistant_id 'bad agent!'. Use 'lead_agent' or a custom agent name containing only letters, digits, and hyphens.") _run(go()) diff --git a/backend/tests/test_claude_provider_oauth_billing.py b/backend/tests/test_claude_provider_oauth_billing.py index a341ffe2c..9f9329bb1 100644 --- a/backend/tests/test_claude_provider_oauth_billing.py +++ b/backend/tests/test_claude_provider_oauth_billing.py @@ -56,10 +56,7 @@ def test_billing_not_duplicated_on_second_call(model): payload = {"system": [{"type": "text", "text": "prompt"}]} model._apply_oauth_billing(payload) model._apply_oauth_billing(payload) - billing_count = sum( - 1 for b in payload["system"] - if isinstance(b, dict) and OAUTH_BILLING_HEADER in b.get("text", "") - ) + billing_count = sum(1 for b in payload["system"] if isinstance(b, dict) and OAUTH_BILLING_HEADER in b.get("text", "")) assert billing_count == 1 diff --git a/backend/tests/test_client.py b/backend/tests/test_client.py index 3a1d6e346..b03ab0333 100644 --- a/backend/tests/test_client.py +++ b/backend/tests/test_client.py @@ -65,14 +65,7 @@ class TestClientInit: def test_custom_params(self, mock_app_config): mock_middleware = MagicMock() with patch("deerflow.client.get_app_config", return_value=mock_app_config): - c = DeerFlowClient( - model_name="gpt-4", - thinking_enabled=False, - subagent_enabled=True, - plan_mode=True, - agent_name="test-agent", - middlewares=[mock_middleware] - ) + c = DeerFlowClient(model_name="gpt-4", thinking_enabled=False, subagent_enabled=True, plan_mode=True, agent_name="test-agent", middlewares=[mock_middleware]) assert c._model_name == "gpt-4" assert c._thinking_enabled is False assert c._subagent_enabled is True diff --git a/backend/tests/test_lead_agent_model_resolution.py b/backend/tests/test_lead_agent_model_resolution.py index b964f53ca..9373c2895 100644 --- a/backend/tests/test_lead_agent_model_resolution.py +++ b/backend/tests/test_lead_agent_model_resolution.py @@ -132,18 +132,13 @@ def test_build_middlewares_uses_resolved_model_name_for_vision(monkeypatch): monkeypatch.setattr(lead_agent_module, "_create_summarization_middleware", lambda: None) monkeypatch.setattr(lead_agent_module, "_create_todo_list_middleware", lambda is_plan_mode: None) - middlewares = lead_agent_module._build_middlewares( - {"configurable": {"model_name": "stale-model", "is_plan_mode": False, "subagent_enabled": False}}, - model_name="vision-model", - custom_middlewares=[MagicMock()] - ) + middlewares = lead_agent_module._build_middlewares({"configurable": {"model_name": "stale-model", "is_plan_mode": False, "subagent_enabled": False}}, model_name="vision-model", custom_middlewares=[MagicMock()]) assert any(isinstance(m, lead_agent_module.ViewImageMiddleware) for m in middlewares) # verify the custom middleware is injected correctly assert len(middlewares) > 0 and isinstance(middlewares[-2], MagicMock) - def test_create_summarization_middleware_uses_configured_model_alias(monkeypatch): monkeypatch.setattr( lead_agent_module, diff --git a/backend/tests/test_memory_storage.py b/backend/tests/test_memory_storage.py index dd6b9f6af..f8e826e6a 100644 --- a/backend/tests/test_memory_storage.py +++ b/backend/tests/test_memory_storage.py @@ -33,6 +33,7 @@ class TestMemoryStorageInterface: def test_abstract_methods(self): """Should raise TypeError when trying to instantiate abstract class.""" + class TestStorage(MemoryStorage): pass @@ -45,6 +46,7 @@ class TestFileMemoryStorage: def test_get_memory_file_path_global(self, tmp_path): """Should return global memory file path when agent_name is None.""" + def mock_get_paths(): mock_paths = MagicMock() mock_paths.memory_file = tmp_path / "memory.json" @@ -58,6 +60,7 @@ class TestFileMemoryStorage: def test_get_memory_file_path_agent(self, tmp_path): """Should return per-agent memory file path when agent_name is provided.""" + def mock_get_paths(): mock_paths = MagicMock() mock_paths.agent_memory_file.return_value = tmp_path / "agents" / "test-agent" / "memory.json" @@ -68,9 +71,7 @@ class TestFileMemoryStorage: path = storage._get_memory_file_path("test-agent") assert path == tmp_path / "agents" / "test-agent" / "memory.json" - @pytest.mark.parametrize( - "invalid_name", ["", "../etc/passwd", "agent/name", "agent\\name", "agent name", "agent@123", "agent_name"] - ) + @pytest.mark.parametrize("invalid_name", ["", "../etc/passwd", "agent/name", "agent\\name", "agent name", "agent@123", "agent_name"]) def test_validate_agent_name_invalid(self, invalid_name): """Should raise ValueError for invalid agent names that don't match the pattern.""" storage = FileMemoryStorage() @@ -79,6 +80,7 @@ class TestFileMemoryStorage: def test_load_creates_empty_memory(self, tmp_path): """Should create empty memory when file doesn't exist.""" + def mock_get_paths(): mock_paths = MagicMock() mock_paths.memory_file = tmp_path / "non_existent_memory.json" @@ -125,10 +127,10 @@ class TestFileMemoryStorage: # First load memory1 = storage.load() assert memory1["facts"][0]["content"] == "initial fact" - + # Update file directly memory_file.write_text('{"version": "1.0", "facts": [{"content": "updated fact"}]}') - + # Reload should get updated data memory2 = storage.reload() assert memory2["facts"][0]["content"] == "updated fact" @@ -141,6 +143,7 @@ class TestGetMemoryStorage: def reset_storage_instance(self): """Reset the global storage instance before and after each test.""" import deerflow.agents.memory.storage as storage_mod + storage_mod._storage_instance = None yield storage_mod._storage_instance = None @@ -167,6 +170,7 @@ class TestGetMemoryStorage: def test_get_memory_storage_thread_safety(self): """Should safely initialize the singleton even with concurrent calls.""" results = [] + def get_storage(): # get_memory_storage is called concurrently from multiple threads while # get_memory_config is patched once around thread creation. This verifies diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 000000000..bd5535a60 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1 @@ +pnpm-lock.yaml diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md index 4943cdf89..7220c4af3 100644 --- a/frontend/CLAUDE.md +++ b/frontend/CLAUDE.md @@ -10,15 +10,15 @@ DeerFlow Frontend is a Next.js 16 web interface for an AI agent system. It commu ## Commands -| Command | Purpose | -|---------|---------| -| `pnpm dev` | Dev server with Turbopack (http://localhost:3000) | -| `pnpm build` | Production build | -| `pnpm check` | Lint + type check (run before committing) | -| `pnpm lint` | ESLint only | -| `pnpm lint:fix` | ESLint with auto-fix | -| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) | -| `pnpm start` | Start production server | +| Command | Purpose | +| ---------------- | ------------------------------------------------- | +| `pnpm dev` | Dev server with Turbopack (http://localhost:3000) | +| `pnpm build` | Production build | +| `pnpm check` | Lint + type check (run before committing) | +| `pnpm lint` | ESLint only | +| `pnpm lint:fix` | ESLint with auto-fix | +| `pnpm typecheck` | TypeScript type check (`tsc --noEmit`) | +| `pnpm start` | Start production server | No test framework is configured. @@ -81,6 +81,7 @@ The frontend is a stateful chat application. Users create **threads** (conversat ## Environment Backend API URLs are optional; an nginx proxy is used by default: + ``` NEXT_PUBLIC_BACKEND_BASE_URL=http://localhost:8001 NEXT_PUBLIC_LANGGRAPH_BASE_URL=http://localhost:2024 diff --git a/frontend/README.md b/frontend/README.md index a7ded3cbd..c58789a8b 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -114,17 +114,17 @@ src/ ## Scripts -| Command | Description | -|---------|-------------| -| `pnpm dev` | Start development server with Turbopack | -| `pnpm build` | Build for production | -| `pnpm start` | Start production server | -| `pnpm format` | Check formatting with Prettier | -| `pnpm format:write` | Apply formatting with Prettier | -| `pnpm lint` | Run ESLint | -| `pnpm lint:fix` | Fix ESLint issues | -| `pnpm typecheck` | Run TypeScript type checking | -| `pnpm check` | Run both lint and typecheck | +| Command | Description | +| ------------------- | --------------------------------------- | +| `pnpm dev` | Start development server with Turbopack | +| `pnpm build` | Build for production | +| `pnpm start` | Start production server | +| `pnpm format` | Check formatting with Prettier | +| `pnpm format:write` | Apply formatting with Prettier | +| `pnpm lint` | Run ESLint | +| `pnpm lint:fix` | Fix ESLint issues | +| `pnpm typecheck` | Run TypeScript type checking | +| `pnpm check` | Run both lint and typecheck | ## Development Notes diff --git a/frontend/public/demo/threads/3823e443-4e2b-4679-b496-a9506eae462b/user-data/outputs/fei-fei-li-podcast-timeline.md b/frontend/public/demo/threads/3823e443-4e2b-4679-b496-a9506eae462b/user-data/outputs/fei-fei-li-podcast-timeline.md index 657bc3227..8b4e5a8d9 100644 --- a/frontend/public/demo/threads/3823e443-4e2b-4679-b496-a9506eae462b/user-data/outputs/fei-fei-li-podcast-timeline.md +++ b/frontend/public/demo/threads/3823e443-4e2b-4679-b496-a9506eae462b/user-data/outputs/fei-fei-li-podcast-timeline.md @@ -1,6 +1,7 @@ # Dr. Fei-Fei Li: Recent Podcast Appearances Timeline (Last 6 Months) ## Overview + Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing on major podcasts discussing the future of artificial intelligence, spatial intelligence, human-centered AI, and her work at World Labs. This timeline compiles key highlights from her recent podcast appearances from August 2025 to January 2026. --- @@ -8,9 +9,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing ## Timeline of Recent Podcast Appearances ### January 15, 2025 - **Possible Podcast** (with Reid Hoffman and Aria Finger) + **Episode:** "Fei-Fei Li on spatial intelligence and human-centered AI" **Key Highlights:** + - **Spatial Intelligence as Next Frontier:** Emphasized that spatial intelligence represents the next major evolution beyond large language models (LLMs) - **Human-Centered AI Philosophy:** Discussed the importance of building AI that amplifies human potential rather than replacing humans - **Regulatory Guardrails:** Addressed the need for thoughtful regulation and governance frameworks for AI development @@ -22,9 +25,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### August 15, 2025 - **Firing Line (PBS)** + **Episode:** "Fei-Fei Li on ethical AI development" **Key Highlights:** + - **Ethical AI Development:** Discussed the challenges and responsibilities in developing AI ethically - **Societal Impact:** Addressed how AI will transform various sectors including healthcare, education, and employment - **Policy Recommendations:** Provided insights on what policy frameworks are needed for responsible AI deployment @@ -33,9 +38,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### November 16, 2025 - **Lenny's Podcast** + **Episode:** "The Godmother of AI on jobs, robots & why world models are next" **Key Highlights:** + - **World Models Introduction:** Explained why world models and spatial intelligence represent the next frontier beyond LLMs - **AI Won't Replace Humans:** Argued that AI won't replace humans but will require us to take responsibility for ourselves - **Marble Applications:** Revealed surprising applications of World Labs' Marble product, from movie production to psychological research @@ -44,6 +51,7 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing - **Participation for All:** Explained how anyone can participate in AI regardless of their role or background **Key Discussion Points:** + 1. How ImageNet helped spark the current AI explosion 2. The "bitter lesson" in AI and robotics 3. Applications of Marble in creative industries and therapy @@ -52,9 +60,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### November 25, 2025 - **Masters of Scale Summit** + **Episode:** "The 'Godmother of AI' on the next phase of AI" (with Reid Hoffman) **Key Highlights:** + - **Fearless Approach:** Discussed why scientists and entrepreneurs need to be fearless in the face of an uncertain AI future - **Spatial Intelligence & World Modeling:** Detailed the next phase of AI focusing on spatial understanding - **Trust Building:** Explained how leaders should build societal trust in AI products and companies @@ -62,6 +72,7 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing - **Entrepreneurial Responsibility:** Argued that entrepreneurs should care about trust from day one of AI development **Chapter Topics Covered:** + - The next phase of AI: spatial intelligence & world modeling - What spatial intelligence has done for humans - Whether AI is over-hyped @@ -71,9 +82,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### December 9, 2025 - **The Tim Ferriss Show** (#839) + **Episode:** "Dr. Fei-Fei Li, The Godmother of AI — Asking Audacious Questions, Civilizational Technology, and Finding Your North Star" **Key Highlights:** + - **Civilizational Technology:** Defined AI as a "civilizational technology" that will have profound economic, social, cultural, and political impacts - **Personal Journey:** Shared her immigrant story from Chengdu to New Jersey, and her family's seven years running a dry cleaning shop while she attended Princeton - **ImageNet Creation:** Detailed the creation of ImageNet and how it birthed modern AI, including innovative use of Amazon Mechanical Turk for data labeling @@ -82,11 +95,13 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing - **Human-Centered Focus:** Emphasized that "people are at the heart of everything" in AI development **Notable Quotes:** + - "Really, at the end of the day, people are at the heart of everything. People made AI, people will be using AI, people will be impacted by AI, and people should have a say in AI." - "AI is absolutely a civilizational technology... it'll have—or [is] already having—a profound impact in the economic, social, cultural, political, downstream effects of our society." - "What is your North Star?" **Key Topics Discussed:** + - From fighter jets to physics to asking "What is intelligence?" - The epiphany everyone missed: Big data as the hidden hypothesis - Against the single-genius myth: Science as non-linear lineage @@ -97,9 +112,11 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing --- ### June 16, 2025 - **Y Combinator Startup Podcast** + **Episode:** "Fei-Fei Li - Spatial Intelligence is the Next Frontier in AI" **Key Highlights:** + - **Startup Perspective:** Provided insights for AI startups on navigating the current landscape - **Technical Deep Dive:** Offered detailed explanations of spatial intelligence technologies - **Entrepreneurial Advice:** Shared lessons from transitioning from academia to entrepreneurship @@ -110,26 +127,31 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing ## Common Themes Across Recent Appearances ### 1. **Spatial Intelligence as the Next Frontier** + - Repeated emphasis that spatial intelligence represents the next major evolution beyond language models - World Labs' focus on creating AI that understands and interacts with the physical world - Applications ranging from robotics and autonomous systems to creative industries and therapy ### 2. **Human-Centered AI Philosophy** + - Consistent message that AI should augment rather than replace human capabilities - Emphasis on maintaining human agency and responsibility in AI systems - Focus on building trust and ethical frameworks ### 3. **Educational Transformation** + - Advocacy for integrating AI into education to enhance learning - Proposal to use AI as a benchmark for student improvement - Emphasis on making AI accessible to people from all backgrounds ### 4. **Historical Perspective** + - Frequent references to ImageNet's role in sparking the deep learning revolution - Context about how rapidly the AI landscape has changed - Emphasis on collaborative, non-linear progress in scientific advancement ### 5. **Entrepreneurial Vision** + - Insights on building AI companies in the current environment - Balance between technological innovation and responsible development - Focus on practical applications that solve real-world problems @@ -139,18 +161,21 @@ Dr. Fei-Fei Li, often called the "Godmother of AI," has been actively appearing ## Key Insights and Predictions ### **Near-Term Developments (1-3 years):** + - Rapid advancement in spatial intelligence and world modeling technologies - Increased integration of AI in education and creative industries - Growing focus on AI ethics and governance frameworks - Expansion of practical applications in healthcare, therapy, and accessibility ### **Medium-Term Vision (3-5 years):** + - More sophisticated human-AI collaboration systems - Breakthroughs in robotics enabled by spatial intelligence - Transformation of how we teach and learn with AI assistance - Development of new industries centered around spatial AI ### **Long-Term Philosophy:** + - AI as a "civilizational technology" that requires thoughtful stewardship - Emphasis on maintaining human values and agency in technological progress - Vision of technology that helps humanity "raise above our paleolithic emotions" @@ -166,6 +191,7 @@ The timeline shows her evolving role from academic researcher to entrepreneur wh --- ## Sources + 1. The Tim Ferriss Show (December 9, 2025) 2. Lenny's Podcast (November 16, 2025) 3. Masters of Scale Summit (November 25, 2025) @@ -173,4 +199,4 @@ The timeline shows her evolving role from academic researcher to entrepreneur wh 5. Firing Line, PBS (August 15, 2025) 6. Y Combinator Startup Podcast (June 16, 2025) -*Compiled on January 25, 2026* \ No newline at end of file +_Compiled on January 25, 2026_ diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/thread.json b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/thread.json index 4bdfa6e7c..0e5edd2d9 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/thread.json +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/thread.json @@ -802,4 +802,4 @@ "interrupts": [], "checkpoint_id": "1f0f46d7-77ea-64ca-802c-0462f9bf4fdd", "parent_checkpoint_id": "1f0f46d7-77e2-6496-802b-68a165ed83e9" -} \ No newline at end of file +} diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/css/style.css b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/css/style.css index 567280bdf..da56c5313 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/css/style.css +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/css/style.css @@ -1,1838 +1,1919 @@ /* 江苏城市足球联赛2025赛季 - 主样式文件 */ :root { - /* 主色调 - 江苏蓝与活力橙 */ - --color-primary: #1a56db; - --color-primary-dark: #1e3a8a; - --color-primary-light: #3b82f6; - --color-secondary: #f59e0b; - --color-secondary-dark: #d97706; - --color-secondary-light: #fbbf24; - - /* 中性色 */ - --color-white: #ffffff; - --color-gray-50: #f9fafb; - --color-gray-100: #f3f4f6; - --color-gray-200: #e5e7eb; - --color-gray-300: #d1d5db; - --color-gray-400: #9ca3af; - --color-gray-500: #6b7280; - --color-gray-600: #4b5563; - --color-gray-700: #374151; - --color-gray-800: #1f2937; - --color-gray-900: #111827; - --color-black: #000000; - - /* 功能色 */ - --color-success: #10b981; - --color-warning: #f59e0b; - --color-danger: #ef4444; - --color-info: #3b82f6; - - /* 字体 */ - --font-heading: 'Oswald', sans-serif; - --font-body: 'Inter', sans-serif; - --font-display: 'Montserrat', sans-serif; - - /* 尺寸 */ - --container-max: 1280px; - --border-radius-sm: 4px; - --border-radius-md: 8px; - --border-radius-lg: 16px; - --border-radius-xl: 24px; - --border-radius-2xl: 32px; - - /* 阴影 */ - --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); - --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); - --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); - --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); - - /* 过渡 */ - --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); - --transition-normal: 300ms cubic-bezier(0.4, 0, 0.2, 1); - --transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1); - - /* 动效 */ - --animation-bounce: bounce 1s infinite; - --animation-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; - --animation-spin: spin 1s linear infinite; + /* 主色调 - 江苏蓝与活力橙 */ + --color-primary: #1a56db; + --color-primary-dark: #1e3a8a; + --color-primary-light: #3b82f6; + --color-secondary: #f59e0b; + --color-secondary-dark: #d97706; + --color-secondary-light: #fbbf24; + + /* 中性色 */ + --color-white: #ffffff; + --color-gray-50: #f9fafb; + --color-gray-100: #f3f4f6; + --color-gray-200: #e5e7eb; + --color-gray-300: #d1d5db; + --color-gray-400: #9ca3af; + --color-gray-500: #6b7280; + --color-gray-600: #4b5563; + --color-gray-700: #374151; + --color-gray-800: #1f2937; + --color-gray-900: #111827; + --color-black: #000000; + + /* 功能色 */ + --color-success: #10b981; + --color-warning: #f59e0b; + --color-danger: #ef4444; + --color-info: #3b82f6; + + /* 字体 */ + --font-heading: "Oswald", sans-serif; + --font-body: "Inter", sans-serif; + --font-display: "Montserrat", sans-serif; + + /* 尺寸 */ + --container-max: 1280px; + --border-radius-sm: 4px; + --border-radius-md: 8px; + --border-radius-lg: 16px; + --border-radius-xl: 24px; + --border-radius-2xl: 32px; + + /* 阴影 */ + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); + --shadow-md: + 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + --shadow-lg: + 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); + --shadow-xl: + 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); + --shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + + /* 过渡 */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-normal: 300ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-slow: 500ms cubic-bezier(0.4, 0, 0.2, 1); + + /* 动效 */ + --animation-bounce: bounce 1s infinite; + --animation-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; + --animation-spin: spin 1s linear infinite; } /* 暗色主题变量 */ [data-theme="dark"] { - --color-white: #111827; - --color-gray-50: #1f2937; - --color-gray-100: #374151; - --color-gray-200: #4b5563; - --color-gray-300: #6b7280; - --color-gray-400: #9ca3af; - --color-gray-500: #d1d5db; - --color-gray-600: #e5e7eb; - --color-gray-700: #f3f4f6; - --color-gray-800: #f9fafb; - --color-gray-900: #ffffff; - --color-black: #f9fafb; + --color-white: #111827; + --color-gray-50: #1f2937; + --color-gray-100: #374151; + --color-gray-200: #4b5563; + --color-gray-300: #6b7280; + --color-gray-400: #9ca3af; + --color-gray-500: #d1d5db; + --color-gray-600: #e5e7eb; + --color-gray-700: #f3f4f6; + --color-gray-800: #f9fafb; + --color-gray-900: #ffffff; + --color-black: #f9fafb; } /* 重置与基础样式 */ * { - margin: 0; - padding: 0; - box-sizing: border-box; + margin: 0; + padding: 0; + box-sizing: border-box; } html { - scroll-behavior: smooth; - font-size: 16px; + scroll-behavior: smooth; + font-size: 16px; } body { - font-family: var(--font-body); - font-size: 1rem; - line-height: 1.5; - color: var(--color-gray-800); - background-color: var(--color-white); - overflow-x: hidden; - transition: background-color var(--transition-normal), color var(--transition-normal); + font-family: var(--font-body); + font-size: 1rem; + line-height: 1.5; + color: var(--color-gray-800); + background-color: var(--color-white); + overflow-x: hidden; + transition: + background-color var(--transition-normal), + color var(--transition-normal); } .container { - width: 100%; - max-width: var(--container-max); - margin: 0 auto; - padding: 0 1.5rem; + width: 100%; + max-width: var(--container-max); + margin: 0 auto; + padding: 0 1.5rem; } /* 加载动画 */ .loader { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%); - display: flex; - align-items: center; - justify-content: center; - z-index: 9999; - opacity: 1; - visibility: visible; - transition: opacity var(--transition-normal), visibility var(--transition-normal); + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-primary-dark) 100% + ); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; + opacity: 1; + visibility: visible; + transition: + opacity var(--transition-normal), + visibility var(--transition-normal); } .loader.loaded { - opacity: 0; - visibility: hidden; + opacity: 0; + visibility: hidden; } .loader-content { - text-align: center; + text-align: center; } .football { - width: 80px; - height: 80px; - background: linear-gradient(45deg, var(--color-white) 25%, var(--color-gray-200) 25%, var(--color-gray-200) 50%, var(--color-white) 50%, var(--color-white) 75%, var(--color-gray-200) 75%); - background-size: 20px 20px; - border-radius: 50%; - margin: 0 auto 2rem; - animation: var(--animation-spin); - position: relative; + width: 80px; + height: 80px; + background: linear-gradient( + 45deg, + var(--color-white) 25%, + var(--color-gray-200) 25%, + var(--color-gray-200) 50%, + var(--color-white) 50%, + var(--color-white) 75%, + var(--color-gray-200) 75% + ); + background-size: 20px 20px; + border-radius: 50%; + margin: 0 auto 2rem; + animation: var(--animation-spin); + position: relative; } .football::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 30px; - height: 30px; - background: var(--color-secondary); - border-radius: 50%; - border: 3px solid var(--color-white); + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 30px; + height: 30px; + background: var(--color-secondary); + border-radius: 50%; + border: 3px solid var(--color-white); } .loader-text { - font-family: var(--font-heading); - font-size: 1.5rem; - font-weight: 500; - color: var(--color-white); - letter-spacing: 2px; - text-transform: uppercase; + font-family: var(--font-heading); + font-size: 1.5rem; + font-weight: 500; + color: var(--color-white); + letter-spacing: 2px; + text-transform: uppercase; } /* 导航栏 */ .navbar { - position: fixed; - top: 0; - left: 0; - width: 100%; - background: rgba(255, 255, 255, 0.95); - backdrop-filter: blur(10px); - border-bottom: 1px solid var(--color-gray-200); - z-index: 1000; - transition: all var(--transition-normal); + position: fixed; + top: 0; + left: 0; + width: 100%; + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(10px); + border-bottom: 1px solid var(--color-gray-200); + z-index: 1000; + transition: all var(--transition-normal); } [data-theme="dark"] .navbar { - background: rgba(17, 24, 39, 0.95); - border-bottom-color: var(--color-gray-700); + background: rgba(17, 24, 39, 0.95); + border-bottom-color: var(--color-gray-700); } .navbar .container { - display: flex; - align-items: center; - justify-content: space-between; - height: 80px; + display: flex; + align-items: center; + justify-content: space-between; + height: 80px; } .nav-brand { - display: flex; - align-items: center; - gap: 1rem; + display: flex; + align-items: center; + gap: 1rem; } .logo { - display: flex; - align-items: center; - gap: 0.75rem; - cursor: pointer; + display: flex; + align-items: center; + gap: 0.75rem; + cursor: pointer; } .logo-ball { - width: 36px; - height: 36px; - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%); - border-radius: 50%; - position: relative; - animation: var(--animation-pulse); + width: 36px; + height: 36px; + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-secondary) 100% + ); + border-radius: 50%; + position: relative; + animation: var(--animation-pulse); } .logo-ball::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 12px; - height: 12px; - background: var(--color-white); - border-radius: 50%; + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 12px; + height: 12px; + background: var(--color-white); + border-radius: 50%; } .logo-text { - font-family: var(--font-heading); - font-size: 1.5rem; - font-weight: 700; - color: var(--color-primary); - letter-spacing: 1px; + font-family: var(--font-heading); + font-size: 1.5rem; + font-weight: 700; + color: var(--color-primary); + letter-spacing: 1px; } [data-theme="dark"] .logo-text { - color: var(--color-white); + color: var(--color-white); } .league-name { - font-family: var(--font-body); - font-size: 0.875rem; - font-weight: 500; - color: var(--color-gray-600); - padding-left: 1rem; - border-left: 1px solid var(--color-gray-300); + font-family: var(--font-body); + font-size: 0.875rem; + font-weight: 500; + color: var(--color-gray-600); + padding-left: 1rem; + border-left: 1px solid var(--color-gray-300); } [data-theme="dark"] .league-name { - color: var(--color-gray-400); - border-left-color: var(--color-gray-600); + color: var(--color-gray-400); + border-left-color: var(--color-gray-600); } .nav-menu { - display: flex; - gap: 2rem; + display: flex; + gap: 2rem; } .nav-link { - font-family: var(--font-heading); - font-size: 1rem; - font-weight: 500; - color: var(--color-gray-700); - text-decoration: none; - text-transform: uppercase; - letter-spacing: 1px; - padding: 0.5rem 0; - position: relative; - transition: color var(--transition-fast); + font-family: var(--font-heading); + font-size: 1rem; + font-weight: 500; + color: var(--color-gray-700); + text-decoration: none; + text-transform: uppercase; + letter-spacing: 1px; + padding: 0.5rem 0; + position: relative; + transition: color var(--transition-fast); } .nav-link::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 0; - height: 2px; - background: var(--color-primary); - transition: width var(--transition-fast); + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: var(--color-primary); + transition: width var(--transition-fast); } .nav-link:hover { - color: var(--color-primary); + color: var(--color-primary); } .nav-link:hover::after { - width: 100%; + width: 100%; } .nav-link.active { - color: var(--color-primary); + color: var(--color-primary); } .nav-link.active::after { - width: 100%; + width: 100%; } [data-theme="dark"] .nav-link { - color: var(--color-gray-300); + color: var(--color-gray-300); } [data-theme="dark"] .nav-link:hover, [data-theme="dark"] .nav-link.active { - color: var(--color-primary-light); + color: var(--color-primary-light); } .nav-actions { - display: flex; - align-items: center; - gap: 1rem; + display: flex; + align-items: center; + gap: 1rem; } .btn-theme-toggle, .btn-menu-toggle { - width: 40px; - height: 40px; - border-radius: var(--border-radius-md); - border: 1px solid var(--color-gray-300); - background: var(--color-white); - color: var(--color-gray-700); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all var(--transition-fast); + width: 40px; + height: 40px; + border-radius: var(--border-radius-md); + border: 1px solid var(--color-gray-300); + background: var(--color-white); + color: var(--color-gray-700); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all var(--transition-fast); } .btn-theme-toggle:hover, .btn-menu-toggle:hover { - border-color: var(--color-primary); - color: var(--color-primary); - transform: translateY(-2px); + border-color: var(--color-primary); + color: var(--color-primary); + transform: translateY(-2px); } [data-theme="dark"] .btn-theme-toggle, [data-theme="dark"] .btn-menu-toggle { - border-color: var(--color-gray-600); - background: var(--color-gray-800); - color: var(--color-gray-300); + border-color: var(--color-gray-600); + background: var(--color-gray-800); + color: var(--color-gray-300); } .btn-menu-toggle { - display: none; + display: none; } /* 按钮样式 */ .btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - padding: 0.75rem 1.5rem; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - border-radius: var(--border-radius-md); - border: 2px solid transparent; - cursor: pointer; - transition: all var(--transition-fast); - text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + border-radius: var(--border-radius-md); + border: 2px solid transparent; + cursor: pointer; + transition: all var(--transition-fast); + text-decoration: none; } .btn-primary { - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-light) 100%); - color: var(--color-white); - box-shadow: var(--shadow-md); + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-primary-light) 100% + ); + color: var(--color-white); + box-shadow: var(--shadow-md); } .btn-primary:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-lg); + transform: translateY(-2px); + box-shadow: var(--shadow-lg); } .btn-secondary { - background: linear-gradient(135deg, var(--color-secondary) 0%, var(--color-secondary-light) 100%); - color: var(--color-white); - box-shadow: var(--shadow-md); + background: linear-gradient( + 135deg, + var(--color-secondary) 0%, + var(--color-secondary-light) 100% + ); + color: var(--color-white); + box-shadow: var(--shadow-md); } .btn-secondary:hover { - transform: translateY(-2px); - box-shadow: var(--shadow-lg); + transform: translateY(-2px); + box-shadow: var(--shadow-lg); } .btn-outline { - background: transparent; - border-color: var(--color-gray-300); - color: var(--color-gray-700); + background: transparent; + border-color: var(--color-gray-300); + color: var(--color-gray-700); } .btn-outline:hover { - border-color: var(--color-primary); - color: var(--color-primary); - transform: translateY(-2px); + border-color: var(--color-primary); + color: var(--color-primary); + transform: translateY(-2px); } [data-theme="dark"] .btn-outline { - border-color: var(--color-gray-600); - color: var(--color-gray-300); + border-color: var(--color-gray-600); + color: var(--color-gray-300); } /* 英雄区域 */ .hero { - position: relative; - min-height: 100vh; - padding-top: 80px; - overflow: hidden; + position: relative; + min-height: 100vh; + padding-top: 80px; + overflow: hidden; } .hero-background { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; } .hero-gradient { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(135deg, - rgba(26, 86, 219, 0.1) 0%, - rgba(59, 130, 246, 0.05) 50%, - rgba(245, 158, 11, 0.1) 100%); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 135deg, + rgba(26, 86, 219, 0.1) 0%, + rgba(59, 130, 246, 0.05) 50%, + rgba(245, 158, 11, 0.1) 100% + ); } .hero-pattern { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-image: - radial-gradient(circle at 25% 25%, rgba(26, 86, 219, 0.1) 2px, transparent 2px), - radial-gradient(circle at 75% 75%, rgba(245, 158, 11, 0.1) 2px, transparent 2px); - background-size: 60px 60px; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: + radial-gradient( + circle at 25% 25%, + rgba(26, 86, 219, 0.1) 2px, + transparent 2px + ), + radial-gradient( + circle at 75% 75%, + rgba(245, 158, 11, 0.1) 2px, + transparent 2px + ); + background-size: 60px 60px; } .hero-ball-animation { - position: absolute; - width: 300px; - height: 300px; - top: 50%; - right: 10%; - transform: translateY(-50%); - background: radial-gradient(circle at 30% 30%, - rgba(26, 86, 219, 0.2) 0%, - rgba(26, 86, 219, 0.1) 30%, - transparent 70%); - border-radius: 50%; - animation: float 6s ease-in-out infinite; + position: absolute; + width: 300px; + height: 300px; + top: 50%; + right: 10%; + transform: translateY(-50%); + background: radial-gradient( + circle at 30% 30%, + rgba(26, 86, 219, 0.2) 0%, + rgba(26, 86, 219, 0.1) 30%, + transparent 70% + ); + border-radius: 50%; + animation: float 6s ease-in-out infinite; } .hero .container { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 4rem; - align-items: center; - min-height: calc(100vh - 80px); + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; + min-height: calc(100vh - 80px); } .hero-content { - max-width: 600px; + max-width: 600px; } .hero-badge { - display: flex; - gap: 1rem; - margin-bottom: 2rem; + display: flex; + gap: 1rem; + margin-bottom: 2rem; } .badge-season, .badge-league { - padding: 0.5rem 1rem; - border-radius: var(--border-radius-full); - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; + padding: 0.5rem 1rem; + border-radius: var(--border-radius-full); + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; } .badge-season { - background: var(--color-primary); - color: var(--color-white); + background: var(--color-primary); + color: var(--color-white); } .badge-league { - background: var(--color-secondary); - color: var(--color-white); + background: var(--color-secondary); + color: var(--color-white); } .hero-title { - font-family: var(--font-display); - font-size: 4rem; - font-weight: 900; - line-height: 1.1; - margin-bottom: 1.5rem; - color: var(--color-gray-900); + font-family: var(--font-display); + font-size: 4rem; + font-weight: 900; + line-height: 1.1; + margin-bottom: 1.5rem; + color: var(--color-gray-900); } .title-line { - display: block; + display: block; } .highlight { - color: var(--color-primary); - position: relative; - display: inline-block; + color: var(--color-primary); + position: relative; + display: inline-block; } .highlight::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 8px; - background: var(--color-secondary); - opacity: 0.3; - z-index: -1; + content: ""; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 8px; + background: var(--color-secondary); + opacity: 0.3; + z-index: -1; } .hero-subtitle { - font-size: 1.25rem; - color: var(--color-gray-600); - margin-bottom: 3rem; - max-width: 500px; + font-size: 1.25rem; + color: var(--color-gray-600); + margin-bottom: 3rem; + max-width: 500px; } [data-theme="dark"] .hero-subtitle { - color: var(--color-gray-400); + color: var(--color-gray-400); } .hero-stats { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 1.5rem; - margin-bottom: 3rem; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1.5rem; + margin-bottom: 3rem; } .stat-item { - text-align: center; + text-align: center; } .stat-number { - font-family: var(--font-display); - font-size: 2.5rem; - font-weight: 800; - color: var(--color-primary); - margin-bottom: 0.25rem; + font-family: var(--font-display); + font-size: 2.5rem; + font-weight: 800; + color: var(--color-primary); + margin-bottom: 0.25rem; } .stat-label { - font-size: 0.875rem; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; + font-size: 0.875rem; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; } [data-theme="dark"] .stat-label { - color: var(--color-gray-400); + color: var(--color-gray-400); } .hero-actions { - display: flex; - gap: 1rem; + display: flex; + gap: 1rem; } .hero-visual { - position: relative; - height: 500px; + position: relative; + height: 500px; } .stadium-visual { - position: relative; - width: 100%; - height: 100%; - background: linear-gradient(135deg, var(--color-gray-100) 0%, var(--color-gray-200) 100%); - border-radius: var(--border-radius-2xl); - overflow: hidden; - box-shadow: var(--shadow-2xl); + position: relative; + width: 100%; + height: 100%; + background: linear-gradient( + 135deg, + var(--color-gray-100) 0%, + var(--color-gray-200) 100% + ); + border-radius: var(--border-radius-2xl); + overflow: hidden; + box-shadow: var(--shadow-2xl); } .stadium-field { - position: absolute; - top: 10%; - left: 5%; - width: 90%; - height: 80%; - background: linear-gradient(135deg, #16a34a 0%, #22c55e 100%); - border-radius: var(--border-radius-xl); + position: absolute; + top: 10%; + left: 5%; + width: 90%; + height: 80%; + background: linear-gradient(135deg, #16a34a 0%, #22c55e 100%); + border-radius: var(--border-radius-xl); } .stadium-stands { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(135deg, - transparent 0%, - rgba(0, 0, 0, 0.1) 20%, - rgba(0, 0, 0, 0.2) 100%); - border-radius: var(--border-radius-2xl); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 135deg, + transparent 0%, + rgba(0, 0, 0, 0.1) 20%, + rgba(0, 0, 0, 0.2) 100% + ); + border-radius: var(--border-radius-2xl); } .stadium-players { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 80%; - height: 60%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 80%; + height: 60%; } .player { - position: absolute; - width: 40px; - height: 60px; - background: var(--color-white); - border-radius: var(--border-radius-md); - box-shadow: var(--shadow-md); + position: absolute; + width: 40px; + height: 60px; + background: var(--color-white); + border-radius: var(--border-radius-md); + box-shadow: var(--shadow-md); } .player-1 { - top: 30%; - left: 20%; - animation: player-move-1 3s ease-in-out infinite; + top: 30%; + left: 20%; + animation: player-move-1 3s ease-in-out infinite; } .player-2 { - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - animation: player-move-2 4s ease-in-out infinite; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: player-move-2 4s ease-in-out infinite; } .player-3 { - top: 40%; - right: 25%; - animation: player-move-3 3.5s ease-in-out infinite; + top: 40%; + right: 25%; + animation: player-move-3 3.5s ease-in-out infinite; } .stadium-ball { - position: absolute; - width: 20px; - height: 20px; - background: linear-gradient(45deg, var(--color-white) 25%, var(--color-gray-200) 25%, var(--color-gray-200) 50%, var(--color-white) 50%, var(--color-white) 75%, var(--color-gray-200) 75%); - background-size: 5px 5px; - border-radius: 50%; - top: 45%; - left: 60%; - animation: ball-move 5s linear infinite; + position: absolute; + width: 20px; + height: 20px; + background: linear-gradient( + 45deg, + var(--color-white) 25%, + var(--color-gray-200) 25%, + var(--color-gray-200) 50%, + var(--color-white) 50%, + var(--color-white) 75%, + var(--color-gray-200) 75% + ); + background-size: 5px 5px; + border-radius: 50%; + top: 45%; + left: 60%; + animation: ball-move 5s linear infinite; } .hero-scroll { - position: absolute; - bottom: 2rem; - left: 50%; - transform: translateX(-50%); + position: absolute; + bottom: 2rem; + left: 50%; + transform: translateX(-50%); } .scroll-indicator { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; } .scroll-line { - width: 2px; - height: 40px; - background: linear-gradient(to bottom, var(--color-primary), transparent); - animation: scroll-line 2s ease-in-out infinite; + width: 2px; + height: 40px; + background: linear-gradient(to bottom, var(--color-primary), transparent); + animation: scroll-line 2s ease-in-out infinite; } /* 下一场比赛 */ .next-match { - padding: 6rem 0; - background: var(--color-gray-50); + padding: 6rem 0; + background: var(--color-gray-50); } [data-theme="dark"] .next-match { - background: var(--color-gray-900); + background: var(--color-gray-900); } .section-header { - text-align: center; - margin-bottom: 3rem; + text-align: center; + margin-bottom: 3rem; } .section-title { - font-family: var(--font-heading); - font-size: 2.5rem; - font-weight: 700; - color: var(--color-gray-900); - margin-bottom: 0.5rem; - text-transform: uppercase; - letter-spacing: 2px; + font-family: var(--font-heading); + font-size: 2.5rem; + font-weight: 700; + color: var(--color-gray-900); + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 2px; } [data-theme="dark"] .section-title { - color: var(--color-white); + color: var(--color-white); } .section-subtitle { - font-size: 1.125rem; - color: var(--color-gray-600); + font-size: 1.125rem; + color: var(--color-gray-600); } [data-theme="dark"] .section-subtitle { - color: var(--color-gray-400); + color: var(--color-gray-400); } .match-card { - background: var(--color-white); - border-radius: var(--border-radius-xl); - padding: 2rem; - box-shadow: var(--shadow-xl); - display: grid; - grid-template-columns: auto 1fr auto; - gap: 3rem; - align-items: center; + background: var(--color-white); + border-radius: var(--border-radius-xl); + padding: 2rem; + box-shadow: var(--shadow-xl); + display: grid; + grid-template-columns: auto 1fr auto; + gap: 3rem; + align-items: center; } [data-theme="dark"] .match-card { - background: var(--color-gray-800); + background: var(--color-gray-800); } .match-date { - text-align: center; - padding: 1.5rem; - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%); - border-radius: var(--border-radius-lg); - color: var(--color-white); + text-align: center; + padding: 1.5rem; + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-primary-dark) 100% + ); + border-radius: var(--border-radius-lg); + color: var(--color-white); } .match-day { - font-family: var(--font-heading); - font-size: 1.125rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - margin-bottom: 0.5rem; + font-family: var(--font-heading); + font-size: 1.125rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 0.5rem; } .match-date-number { - font-family: var(--font-display); - font-size: 3rem; - font-weight: 800; - line-height: 1; - margin-bottom: 0.25rem; + font-family: var(--font-display); + font-size: 3rem; + font-weight: 800; + line-height: 1; + margin-bottom: 0.25rem; } .match-month { - font-family: var(--font-heading); - font-size: 1.125rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - margin-bottom: 0.5rem; + font-family: var(--font-heading); + font-size: 1.125rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 0.5rem; } .match-time { - font-size: 1rem; - font-weight: 500; - opacity: 0.9; + font-size: 1rem; + font-weight: 500; + opacity: 0.9; } .match-teams { - display: grid; - grid-template-columns: 1fr auto 1fr; - gap: 2rem; - align-items: center; + display: grid; + grid-template-columns: 1fr auto 1fr; + gap: 2rem; + align-items: center; } .team { - text-align: center; + text-align: center; } .team-home { - text-align: right; + text-align: right; } .team-away { - text-align: left; + text-align: left; } .team-logo { - width: 80px; - height: 80px; - border-radius: 50%; - margin: 0 auto 1rem; - background: var(--color-gray-200); - position: relative; + width: 80px; + height: 80px; + border-radius: 50%; + margin: 0 auto 1rem; + background: var(--color-gray-200); + position: relative; } .logo-nanjing { - background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%); + background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%); } .logo-nanjing::before { - content: 'N'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-family: var(--font-heading); - font-size: 2rem; - font-weight: 700; - color: var(--color-white); + content: "N"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-family: var(--font-heading); + font-size: 2rem; + font-weight: 700; + color: var(--color-white); } .logo-suzhou { - background: linear-gradient(135deg, #059669 0%, #10b981 100%); + background: linear-gradient(135deg, #059669 0%, #10b981 100%); } .logo-suzhou::before { - content: 'S'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-family: var(--font-heading); - font-size: 2rem; - font-weight: 700; - color: var(--color-white); + content: "S"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-family: var(--font-heading); + font-size: 2rem; + font-weight: 700; + color: var(--color-white); } .team-name { - font-family: var(--font-heading); - font-size: 1.5rem; - font-weight: 600; - color: var(--color-gray-900); - margin-bottom: 0.5rem; + font-family: var(--font-heading); + font-size: 1.5rem; + font-weight: 600; + color: var(--color-gray-900); + margin-bottom: 0.5rem; } [data-theme="dark"] .team-name { - color: var(--color-white); + color: var(--color-white); } .team-record { - font-size: 0.875rem; - color: var(--color-gray-600); + font-size: 0.875rem; + color: var(--color-gray-600); } [data-theme="dark"] .team-record { - color: var(--color-gray-400); + color: var(--color-gray-400); } .match-vs { - text-align: center; + text-align: center; } .vs-text { - font-family: var(--font-display); - font-size: 2rem; - font-weight: 800; - color: var(--color-primary); - margin-bottom: 0.5rem; + font-family: var(--font-display); + font-size: 2rem; + font-weight: 800; + color: var(--color-primary); + margin-bottom: 0.5rem; } .match-info { - font-size: 0.875rem; - color: var(--color-gray-600); + font-size: 0.875rem; + color: var(--color-gray-600); } .match-venue { - font-weight: 600; - margin-bottom: 0.25rem; + font-weight: 600; + margin-bottom: 0.25rem; } .match-round { - opacity: 0.8; + opacity: 0.8; } .match-actions { - display: flex; - flex-direction: column; - gap: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; } /* 球队展示 */ .teams-section { - padding: 6rem 0; + padding: 6rem 0; } .teams-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); - gap: 2rem; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 2rem; } .team-card { - background: var(--color-white); - border-radius: var(--border-radius-lg); - padding: 1.5rem; - box-shadow: var(--shadow-md); - transition: all var(--transition-normal); - cursor: pointer; - text-align: center; + background: var(--color-white); + border-radius: var(--border-radius-lg); + padding: 1.5rem; + box-shadow: var(--shadow-md); + transition: all var(--transition-normal); + cursor: pointer; + text-align: center; } .team-card:hover { - transform: translateY(-8px); - box-shadow: var(--shadow-xl); + transform: translateY(-8px); + box-shadow: var(--shadow-xl); } [data-theme="dark"] .team-card { - background: var(--color-gray-800); + background: var(--color-gray-800); } .team-card-logo { - width: 80px; - height: 80px; - border-radius: 50%; - margin: 0 auto 1rem; - background: var(--color-gray-200); - display: flex; - align-items: center; - justify-content: center; - font-family: var(--font-heading); - font-size: 2rem; - font-weight: 700; - color: var(--color-white); + width: 80px; + height: 80px; + border-radius: 50%; + margin: 0 auto 1rem; + background: var(--color-gray-200); + display: flex; + align-items: center; + justify-content: center; + font-family: var(--font-heading); + font-size: 2rem; + font-weight: 700; + color: var(--color-white); } .team-card-name { - font-family: var(--font-heading); - font-size: 1.25rem; - font-weight: 600; - color: var(--color-gray-900); - margin-bottom: 0.5rem; + font-family: var(--font-heading); + font-size: 1.25rem; + font-weight: 600; + color: var(--color-gray-900); + margin-bottom: 0.5rem; } [data-theme="dark"] .team-card-name { - color: var(--color-white); + color: var(--color-white); } .team-card-city { - font-size: 0.875rem; - color: var(--color-gray-600); - margin-bottom: 1rem; + font-size: 0.875rem; + color: var(--color-gray-600); + margin-bottom: 1rem; } .team-card-stats { - display: flex; - justify-content: space-around; - margin-top: 1rem; - padding-top: 1rem; - border-top: 1px solid var(--color-gray-200); + display: flex; + justify-content: space-around; + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid var(--color-gray-200); } [data-theme="dark"] .team-card-stats { - border-top-color: var(--color-gray-700); + border-top-color: var(--color-gray-700); } .team-stat { - text-align: center; + text-align: center; } .team-stat-value { - font-family: var(--font-display); - font-size: 1.25rem; - font-weight: 700; - color: var(--color-primary); + font-family: var(--font-display); + font-size: 1.25rem; + font-weight: 700; + color: var(--color-primary); } .team-stat-label { - font-size: 0.75rem; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; + font-size: 0.75rem; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; } /* 积分榜 */ .standings-section { - padding: 6rem 0; - background: var(--color-gray-50); + padding: 6rem 0; + background: var(--color-gray-50); } [data-theme="dark"] .standings-section { - background: var(--color-gray-900); + background: var(--color-gray-900); } .standings-container { - overflow-x: auto; + overflow-x: auto; } .standings-table { - min-width: 800px; + min-width: 800px; } .standings-table table { - width: 100%; - border-collapse: collapse; - background: var(--color-white); - border-radius: var(--border-radius-lg); - overflow: hidden; - box-shadow: var(--shadow-md); + width: 100%; + border-collapse: collapse; + background: var(--color-white); + border-radius: var(--border-radius-lg); + overflow: hidden; + box-shadow: var(--shadow-md); } [data-theme="dark"] .standings-table table { - background: var(--color-gray-800); + background: var(--color-gray-800); } .standings-table thead { - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%); + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-primary-dark) 100% + ); } .standings-table th { - padding: 1rem; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-white); - text-transform: uppercase; - letter-spacing: 1px; - text-align: center; + padding: 1rem; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-white); + text-transform: uppercase; + letter-spacing: 1px; + text-align: center; } .standings-table tbody tr { - border-bottom: 1px solid var(--color-gray-200); - transition: background-color var(--transition-fast); + border-bottom: 1px solid var(--color-gray-200); + transition: background-color var(--transition-fast); } [data-theme="dark"] .standings-table tbody tr { - border-bottom-color: var(--color-gray-700); + border-bottom-color: var(--color-gray-700); } .standings-table tbody tr:hover { - background-color: var(--color-gray-100); + background-color: var(--color-gray-100); } [data-theme="dark"] .standings-table tbody tr:hover { - background-color: var(--color-gray-700); + background-color: var(--color-gray-700); } .standings-table td { - padding: 1rem; - text-align: center; - color: var(--color-gray-700); + padding: 1rem; + text-align: center; + color: var(--color-gray-700); } [data-theme="dark"] .standings-table td { - color: var(--color-gray-300); + color: var(--color-gray-300); } .standings-table td:first-child { - font-weight: 700; - color: var(--color-primary); + font-weight: 700; + color: var(--color-primary); } .standings-table td:nth-child(2) { - text-align: left; - font-weight: 600; - color: var(--color-gray-900); + text-align: left; + font-weight: 600; + color: var(--color-gray-900); } [data-theme="dark"] .standings-table td:nth-child(2) { - color: var(--color-white); + color: var(--color-white); } .standings-table td:last-child { - font-weight: 700; - color: var(--color-secondary); + font-weight: 700; + color: var(--color-secondary); } /* 赛程表 */ .fixtures-section { - padding: 6rem 0; + padding: 6rem 0; } .fixtures-tabs { - background: var(--color-white); - border-radius: var(--border-radius-xl); - overflow: hidden; - box-shadow: var(--shadow-lg); + background: var(--color-white); + border-radius: var(--border-radius-xl); + overflow: hidden; + box-shadow: var(--shadow-lg); } [data-theme="dark"] .fixtures-tabs { - background: var(--color-gray-800); + background: var(--color-gray-800); } .tabs { - display: flex; - background: var(--color-gray-100); - padding: 0.5rem; + display: flex; + background: var(--color-gray-100); + padding: 0.5rem; } [data-theme="dark"] .tabs { - background: var(--color-gray-900); + background: var(--color-gray-900); } .tab { - flex: 1; - padding: 1rem; - border: none; - background: transparent; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - cursor: pointer; - transition: all var(--transition-fast); - border-radius: var(--border-radius-md); + flex: 1; + padding: 1rem; + border: none; + background: transparent; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + cursor: pointer; + transition: all var(--transition-fast); + border-radius: var(--border-radius-md); } .tab:hover { - color: var(--color-primary); + color: var(--color-primary); } .tab.active { - background: var(--color-white); - color: var(--color-primary); - box-shadow: var(--shadow-sm); + background: var(--color-white); + color: var(--color-primary); + box-shadow: var(--shadow-sm); } [data-theme="dark"] .tab.active { - background: var(--color-gray-800); + background: var(--color-gray-800); } .fixtures-list { - padding: 2rem; + padding: 2rem; } .fixture-item { - display: grid; - grid-template-columns: auto 1fr auto; - gap: 2rem; - align-items: center; - padding: 1.5rem; - border-bottom: 1px solid var(--color-gray-200); - transition: background-color var(--transition-fast); + display: grid; + grid-template-columns: auto 1fr auto; + gap: 2rem; + align-items: center; + padding: 1.5rem; + border-bottom: 1px solid var(--color-gray-200); + transition: background-color var(--transition-fast); } .fixture-item:hover { - background-color: var(--color-gray-50); + background-color: var(--color-gray-50); } [data-theme="dark"] .fixture-item { - border-bottom-color: var(--color-gray-700); + border-bottom-color: var(--color-gray-700); } [data-theme="dark"] .fixture-item:hover { - background-color: var(--color-gray-900); + background-color: var(--color-gray-900); } .fixture-date { - text-align: center; - min-width: 100px; + text-align: center; + min-width: 100px; } .fixture-day { - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - margin-bottom: 0.25rem; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + margin-bottom: 0.25rem; } .fixture-time { - font-size: 1.125rem; - font-weight: 700; - color: var(--color-primary); + font-size: 1.125rem; + font-weight: 700; + color: var(--color-primary); } .fixture-teams { - display: grid; - grid-template-columns: 1fr auto 1fr; - gap: 1rem; - align-items: center; + display: grid; + grid-template-columns: 1fr auto 1fr; + gap: 1rem; + align-items: center; } .fixture-team { - display: flex; - align-items: center; - gap: 1rem; + display: flex; + align-items: center; + gap: 1rem; } .fixture-team.home { - justify-content: flex-end; + justify-content: flex-end; } .fixture-team-logo { - width: 40px; - height: 40px; - border-radius: 50%; - background: var(--color-gray-200); + width: 40px; + height: 40px; + border-radius: 50%; + background: var(--color-gray-200); } .fixture-team-name { - font-family: var(--font-heading); - font-size: 1.125rem; - font-weight: 600; - color: var(--color-gray-900); + font-family: var(--font-heading); + font-size: 1.125rem; + font-weight: 600; + color: var(--color-gray-900); } [data-theme="dark"] .fixture-team-name { - color: var(--color-white); + color: var(--color-white); } .fixture-vs { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 800; - color: var(--color-gray-400); - padding: 0 1rem; + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 800; + color: var(--color-gray-400); + padding: 0 1rem; } .fixture-score { - min-width: 100px; - text-align: center; + min-width: 100px; + text-align: center; } .fixture-score-value { - font-family: var(--font-display); - font-size: 1.5rem; - font-weight: 800; - color: var(--color-primary); + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 800; + color: var(--color-primary); } .fixture-score-status { - font-size: 0.75rem; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - margin-top: 0.25rem; + font-size: 0.75rem; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + margin-top: 0.25rem; } /* 数据统计 */ .stats-section { - padding: 6rem 0; - background: var(--color-gray-50); + padding: 6rem 0; + background: var(--color-gray-50); } [data-theme="dark"] .stats-section { - background: var(--color-gray-900); + background: var(--color-gray-900); } .stats-tabs { - background: var(--color-white); - border-radius: var(--border-radius-xl); - overflow: hidden; - box-shadow: var(--shadow-lg); + background: var(--color-white); + border-radius: var(--border-radius-xl); + overflow: hidden; + box-shadow: var(--shadow-lg); } [data-theme="dark"] .stats-tabs { - background: var(--color-gray-800); + background: var(--color-gray-800); } .stats-tab-nav { - display: flex; - background: var(--color-gray-100); - padding: 0.5rem; + display: flex; + background: var(--color-gray-100); + padding: 0.5rem; } [data-theme="dark"] .stats-tab-nav { - background: var(--color-gray-900); + background: var(--color-gray-900); } .stats-tab { - flex: 1; - padding: 1rem; - border: none; - background: transparent; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - cursor: pointer; - transition: all var(--transition-fast); - border-radius: var(--border-radius-md); + flex: 1; + padding: 1rem; + border: none; + background: transparent; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + cursor: pointer; + transition: all var(--transition-fast); + border-radius: var(--border-radius-md); } .stats-tab:hover { - color: var(--color-primary); + color: var(--color-primary); } .stats-tab.active { - background: var(--color-white); - color: var(--color-primary); - box-shadow: var(--shadow-sm); + background: var(--color-white); + color: var(--color-primary); + box-shadow: var(--shadow-sm); } [data-theme="dark"] .stats-tab.active { - background: var(--color-gray-800); + background: var(--color-gray-800); } .stats-content { - padding: 2rem; + padding: 2rem; } .stats-tab-content { - display: none; + display: none; } .stats-tab-content.active { - display: block; + display: block; } .stats-table { - width: 100%; - border-collapse: collapse; + width: 100%; + border-collapse: collapse; } .stats-table th { - padding: 1rem; - font-family: var(--font-heading); - font-size: 0.875rem; - font-weight: 600; - color: var(--color-gray-600); - text-transform: uppercase; - letter-spacing: 1px; - text-align: left; - border-bottom: 2px solid var(--color-gray-200); + padding: 1rem; + font-family: var(--font-heading); + font-size: 0.875rem; + font-weight: 600; + color: var(--color-gray-600); + text-transform: uppercase; + letter-spacing: 1px; + text-align: left; + border-bottom: 2px solid var(--color-gray-200); } [data-theme="dark"] .stats-table th { - border-bottom-color: var(--color-gray-700); + border-bottom-color: var(--color-gray-700); } .stats-table td { - padding: 1rem; - border-bottom: 1px solid var(--color-gray-200); - color: var(--color-gray-700); + padding: 1rem; + border-bottom: 1px solid var(--color-gray-200); + color: var(--color-gray-700); } [data-theme="dark"] .stats-table td { - border-bottom-color: var(--color-gray-700); - color: var(--color-gray-300); + border-bottom-color: var(--color-gray-700); + color: var(--color-gray-300); } .stats-table tr:hover { - background-color: var(--color-gray-50); + background-color: var(--color-gray-50); } [data-theme="dark"] .stats-table tr:hover { - background-color: var(--color-gray-900); + background-color: var(--color-gray-900); } .stats-rank { - font-weight: 700; - color: var(--color-primary); - width: 50px; + font-weight: 700; + color: var(--color-primary); + width: 50px; } .stats-player { - font-weight: 600; - color: var(--color-gray-900); + font-weight: 600; + color: var(--color-gray-900); } [data-theme="dark"] .stats-player { - color: var(--color-white); + color: var(--color-white); } .stats-team { - color: var(--color-gray-600); + color: var(--color-gray-600); } .stats-value { - font-weight: 700; - color: var(--color-secondary); - text-align: center; + font-weight: 700; + color: var(--color-secondary); + text-align: center; } /* 新闻动态 */ .news-section { - padding: 6rem 0; + padding: 6rem 0; } .news-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); - gap: 2rem; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 2rem; } .news-card { - background: var(--color-white); - border-radius: var(--border-radius-lg); - overflow: hidden; - box-shadow: var(--shadow-md); - transition: all var(--transition-normal); - cursor: pointer; + background: var(--color-white); + border-radius: var(--border-radius-lg); + overflow: hidden; + box-shadow: var(--shadow-md); + transition: all var(--transition-normal); + cursor: pointer; } .news-card:hover { - transform: translateY(-8px); - box-shadow: var(--shadow-xl); + transform: translateY(-8px); + box-shadow: var(--shadow-xl); } [data-theme="dark"] .news-card { - background: var(--color-gray-800); + background: var(--color-gray-800); } .news-card-image { - height: 200px; - background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%); - position: relative; - overflow: hidden; + height: 200px; + background: linear-gradient( + 135deg, + var(--color-primary) 0%, + var(--color-secondary) 100% + ); + position: relative; + overflow: hidden; } .news-card-image::before { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient(45deg, - transparent 30%, - rgba(255, 255, 255, 0.1) 50%, - transparent 70%); - animation: shimmer 2s infinite; + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient( + 45deg, + transparent 30%, + rgba(255, 255, 255, 0.1) 50%, + transparent 70% + ); + animation: shimmer 2s infinite; } .news-card-content { - padding: 1.5rem; + padding: 1.5rem; } .news-card-category { - display: inline-block; - padding: 0.25rem 0.75rem; - background: var(--color-primary); - color: var(--color-white); - font-family: var(--font-heading); - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 1px; - border-radius: var(--border-radius-sm); - margin-bottom: 1rem; + display: inline-block; + padding: 0.25rem 0.75rem; + background: var(--color-primary); + color: var(--color-white); + font-family: var(--font-heading); + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + border-radius: var(--border-radius-sm); + margin-bottom: 1rem; } .news-card-title { - font-family: var(--font-heading); - font-size: 1.25rem; - font-weight: 600; - color: var(--color-gray-900); - margin-bottom: 0.75rem; - line-height: 1.3; + font-family: var(--font-heading); + font-size: 1.25rem; + font-weight: 600; + color: var(--color-gray-900); + margin-bottom: 0.75rem; + line-height: 1.3; } [data-theme="dark"] .news-card-title { - color: var(--color-white); + color: var(--color-white); } .news-card-excerpt { - font-size: 0.875rem; - color: var(--color-gray-600); - margin-bottom: 1rem; - line-height: 1.5; + font-size: 0.875rem; + color: var(--color-gray-600); + margin-bottom: 1rem; + line-height: 1.5; } [data-theme="dark"] .news-card-excerpt { - color: var(--color-gray-400); + color: var(--color-gray-400); } .news-card-meta { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 0.75rem; - color: var(--color-gray-500); + display: flex; + justify-content: space-between; + align-items: center; + font-size: 0.75rem; + color: var(--color-gray-500); } .news-card-date { - display: flex; - align-items: center; - gap: 0.25rem; + display: flex; + align-items: center; + gap: 0.25rem; } /* 底部 */ .footer { - background: linear-gradient(135deg, var(--color-gray-900) 0%, var(--color-black) 100%); - color: var(--color-white); - padding: 4rem 0 2rem; + background: linear-gradient( + 135deg, + var(--color-gray-900) 0%, + var(--color-black) 100% + ); + color: var(--color-white); + padding: 4rem 0 2rem; } .footer-content { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 4rem; - margin-bottom: 3rem; + display: grid; + grid-template-columns: 1fr 2fr; + gap: 4rem; + margin-bottom: 3rem; } .footer-brand { - max-width: 300px; + max-width: 300px; } .footer .logo { - margin-bottom: 1.5rem; + margin-bottom: 1.5rem; } .footer-description { - font-size: 0.875rem; - color: var(--color-gray-400); - margin-bottom: 1.5rem; - line-height: 1.6; + font-size: 0.875rem; + color: var(--color-gray-400); + margin-bottom: 1.5rem; + line-height: 1.6; } .footer-social { - display: flex; - gap: 1rem; + display: flex; + gap: 1rem; } .social-link { - width: 40px; - height: 40px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.1); - display: flex; - align-items: center; - justify-content: center; - color: var(--color-white); - text-decoration: none; - transition: all var(--transition-fast); + width: 40px; + height: 40px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.1); + display: flex; + align-items: center; + justify-content: center; + color: var(--color-white); + text-decoration: none; + transition: all var(--transition-fast); } .social-link:hover { - background: var(--color-primary); - transform: translateY(-2px); + background: var(--color-primary); + transform: translateY(-2px); } .footer-links { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 2rem; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; } .footer-column { - display: flex; - flex-direction: column; - gap: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; } .footer-title { - font-family: var(--font-heading); - font-size: 1.125rem; - font-weight: 600; - margin-bottom: 0.5rem; - text-transform: uppercase; - letter-spacing: 1px; + font-family: var(--font-heading); + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 0.5rem; + text-transform: uppercase; + letter-spacing: 1px; } .footer-link { - font-size: 0.875rem; - color: var(--color-gray-400); - text-decoration: none; - transition: color var(--transition-fast); + font-size: 0.875rem; + color: var(--color-gray-400); + text-decoration: none; + transition: color var(--transition-fast); } .footer-link:hover { - color: var(--color-white); + color: var(--color-white); } .footer-bottom { - display: flex; - justify-content: space-between; - align-items: center; - padding-top: 2rem; - border-top: 1px solid rgba(255, 255, 255, 0.1); + display: flex; + justify-content: space-between; + align-items: center; + padding-top: 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); } .copyright { - font-size: 0.875rem; - color: var(--color-gray-400); + font-size: 0.875rem; + color: var(--color-gray-400); } .footer-legal { - display: flex; - gap: 1.5rem; + display: flex; + gap: 1.5rem; } .legal-link { - font-size: 0.875rem; - color: var(--color-gray-400); - text-decoration: none; - transition: color var(--transition-fast); + font-size: 0.875rem; + color: var(--color-gray-400); + text-decoration: none; + transition: color var(--transition-fast); } .legal-link:hover { - color: var(--color-white); + color: var(--color-white); } /* 动画 */ @keyframes float { - 0%, 100% { - transform: translateY(-50%) translateX(0); - } - 50% { - transform: translateY(-50%) translateX(20px); - } + 0%, + 100% { + transform: translateY(-50%) translateX(0); + } + 50% { + transform: translateY(-50%) translateX(20px); + } } @keyframes player-move-1 { - 0%, 100% { - transform: translate(0, 0); - } - 50% { - transform: translate(20px, -10px); - } + 0%, + 100% { + transform: translate(0, 0); + } + 50% { + transform: translate(20px, -10px); + } } @keyframes player-move-2 { - 0%, 100% { - transform: translate(-50%, -50%); - } - 50% { - transform: translate(-50%, -60%); - } + 0%, + 100% { + transform: translate(-50%, -50%); + } + 50% { + transform: translate(-50%, -60%); + } } @keyframes player-move-3 { - 0%, 100% { - transform: translate(0, 0); - } - 50% { - transform: translate(-15px, 10px); - } + 0%, + 100% { + transform: translate(0, 0); + } + 50% { + transform: translate(-15px, 10px); + } } @keyframes ball-move { - 0% { - transform: translate(0, 0); - } - 25% { - transform: translate(40px, -20px); - } - 50% { - transform: translate(80px, 0); - } - 75% { - transform: translate(40px, 20px); - } - 100% { - transform: translate(0, 0); - } + 0% { + transform: translate(0, 0); + } + 25% { + transform: translate(40px, -20px); + } + 50% { + transform: translate(80px, 0); + } + 75% { + transform: translate(40px, 20px); + } + 100% { + transform: translate(0, 0); + } } @keyframes scroll-line { - 0% { - height: 0; - opacity: 0; - } - 50% { - height: 40px; - opacity: 1; - } - 100% { - height: 0; - opacity: 0; - transform: translateY(40px); - } + 0% { + height: 0; + opacity: 0; + } + 50% { + height: 40px; + opacity: 1; + } + 100% { + height: 0; + opacity: 0; + transform: translateY(40px); + } } @keyframes spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } @keyframes bounce { - 0%, 100% { - transform: translateY(0); - } - 50% { - transform: translateY(-10px); - } + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-10px); + } } @keyframes pulse { - 0%, 100% { - opacity: 1; - } - 50% { - opacity: 0.5; - } + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } } @keyframes shimmer { - 0% { - transform: translateX(-100%); - } - 100% { - transform: translateX(100%); - } + 0% { + transform: translateX(-100%); + } + 100% { + transform: translateX(100%); + } } /* 响应式设计 */ @media (max-width: 1024px) { - .hero .container { - grid-template-columns: 1fr; - gap: 3rem; - text-align: center; - } - - .hero-content { - max-width: 100%; - } - - .hero-visual { - height: 400px; - } - - .hero-title { - font-size: 3rem; - } - - .footer-content { - grid-template-columns: 1fr; - gap: 3rem; - } + .hero .container { + grid-template-columns: 1fr; + gap: 3rem; + text-align: center; + } + + .hero-content { + max-width: 100%; + } + + .hero-visual { + height: 400px; + } + + .hero-title { + font-size: 3rem; + } + + .footer-content { + grid-template-columns: 1fr; + gap: 3rem; + } } @media (max-width: 768px) { - .nav-menu { - display: none; - } - - .btn-menu-toggle { - display: flex; - } - - .match-card { - grid-template-columns: 1fr; - gap: 2rem; - } - - .hero-stats { - grid-template-columns: repeat(2, 1fr); - } - - .hero-title { - font-size: 2.5rem; - } - - .section-title { - font-size: 2rem; - } - - .footer-links { - grid-template-columns: 1fr; - gap: 2rem; - } - - .footer-bottom { - flex-direction: column; - gap: 1rem; - text-align: center; - } + .nav-menu { + display: none; + } + + .btn-menu-toggle { + display: flex; + } + + .match-card { + grid-template-columns: 1fr; + gap: 2rem; + } + + .hero-stats { + grid-template-columns: repeat(2, 1fr); + } + + .hero-title { + font-size: 2.5rem; + } + + .section-title { + font-size: 2rem; + } + + .footer-links { + grid-template-columns: 1fr; + gap: 2rem; + } + + .footer-bottom { + flex-direction: column; + gap: 1rem; + text-align: center; + } } @media (max-width: 480px) { - .container { - padding: 0 1rem; - } - - .hero-title { - font-size: 2rem; - } - - .hero-subtitle { - font-size: 1rem; - } - - .stat-number { - font-size: 2rem; - } - - .section-title { - font-size: 1.75rem; - } - - .match-teams { - grid-template-columns: 1fr; - gap: 1rem; - } - - .team-home, - .team-away { - text-align: center; - } - - .teams-grid { - grid-template-columns: 1fr; - } - - .news-grid { - grid-template-columns: 1fr; - } + .container { + padding: 0 1rem; + } + + .hero-title { + font-size: 2rem; + } + + .hero-subtitle { + font-size: 1rem; + } + + .stat-number { + font-size: 2rem; + } + + .section-title { + font-size: 1.75rem; + } + + .match-teams { + grid-template-columns: 1fr; + gap: 1rem; + } + + .team-home, + .team-away { + text-align: center; + } + + .teams-grid { + grid-template-columns: 1fr; + } + + .news-grid { + grid-template-columns: 1fr; + } } /* 导航菜单响应式 */ .nav-menu.active { - display: flex; - flex-direction: column; - position: absolute; - top: 80px; - left: 0; - width: 100%; - background: var(--color-white); - padding: 1rem; - box-shadow: var(--shadow-lg); - z-index: 1000; + display: flex; + flex-direction: column; + position: absolute; + top: 80px; + left: 0; + width: 100%; + background: var(--color-white); + padding: 1rem; + box-shadow: var(--shadow-lg); + z-index: 1000; } [data-theme="dark"] .nav-menu.active { - background: var(--color-gray-800); + background: var(--color-gray-800); } .nav-menu.active .nav-link { - padding: 0.75rem 1rem; - border-bottom: 1px solid var(--color-gray-200); + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--color-gray-200); } [data-theme="dark"] .nav-menu.active .nav-link { - border-bottom-color: var(--color-gray-700); + border-bottom-color: var(--color-gray-700); } .nav-menu.active .nav-link:last-child { - border-bottom: none; -} \ No newline at end of file + border-bottom: none; +} diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/favicon.html b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/favicon.html index 6c1cfe5a0..cccafb982 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/favicon.html +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/favicon.html @@ -1 +1,4 @@ - + diff --git a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/index.html b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/index.html index 4b60c436e..095c75fee 100644 --- a/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/index.html +++ b/frontend/public/demo/threads/5aa47db1-d0cb-4eb9-aea5-3dac1b371c5a/user-data/outputs/jiangsu-football/index.html @@ -1,365 +1,385 @@ - + -
- - + + +