Spaces:
Configuration error
## Purpose and vision
Browse filesThe goal of this document is to define, with exceptional clarity and precision, what a **world‑class, user‑centric, zero‑code build** of a Wan 2.2 image‑to‑video Space must look like. It is written so that:
- A power user can give this specification to any advanced code generation agent and receive a complete implementation without writing code manually.
- A non‑technical end user can later operate the resulting Space confidently, with minimal risk of misuse or error.
The emphasis is on **elite sophistication in design** and **practically foolproof usability**, so that the final experience is both powerful and intuitive.
***
## High‑level objective
Create a **Hugging Face Space** that:
- Transforms a **single image into a video** using a **Wan 2.2 image‑to‑video base model** and a selectable LoRA from the collection `Playtime-AI/Wan2.2-Loras`.
- Provides an **elegant, minimal UI** that guides the user step‑by‑step:
1. Upload an image
2. Adjust prompt and options (optional)
3. Click “Generate video”
- Produces an **MP4 video output** displayed directly in the browser.
- Requires **no manual coding** beyond pasting in the generated `app.py` and `requirements.txt` files.
This specification is intentionally neutral to the particular AI coding environment, so it can be used in AI Studio, Deepsite 3, Any Code, or similar systems without modification.
***
## Functional specification
### Model and LoRA configuration
- Use a **Wan 2.2 image‑to‑video base model**, referenced by a model id such as:
- `WAN_BASE_MODEL_ID = "Playtime-AI/Wan2.2-I2V-Base"`
If this exact identifier differs in practice, the agent should inspect the `Playtime-AI/Wan2.2-Loras` model card and associated Wan repositories to determine and substitute the correct base model identifier.
- Use the LoRA collection:
- `WAN_LORA_COLLECTION_ID = "Playtime-AI/Wan2.2-Loras"`
- Dynamically list available LoRAs by scanning the LoRA repo for files with extensions:
- `.safetensors`
- `.bin`
These file names become user‑selectable options in the interface.
### Core behavior
The application must:
- Load the Wan 2.2 base pipeline using a diffusers‑style image‑to‑video pipeline.
- Allow the user to select one LoRA from the collection and apply it to the base model with a **LoRA strength control**.
- Accept a **single image** and user‑defined generation parameters.
- Generate a sequence of frames and convert them into an **MP4 video** suitable for browser playback.
The implementation should:
- Cache the base pipeline in memory to avoid reloading on every run.
- When the LoRA selection changes, unload or override the previous LoRA and apply the newly selected LoRA.
- Use an internal random generator:
- If `seed > 0`, seed the generator for reproducible outputs.
- If `seed <= 0` or invalid, fall back to non‑deterministic behavior.
### Private / gated model support
- Read a token from the environment variable:
- `HF_TOKEN`
- Use this token to authenticate when accessing:
- The Wan 2.2 base model
- The LoRA collection repository
The app must function both when the models are public and when they are private, provided that `HF_TOKEN` is configured with appropriate permissions.
***
## User interface and experience
The user interface must be **self‑explaining and resilient**, designed for both novices and expert users.
### Layout
Implement a **two‑column layout** using Gradio Blocks:
#### Left column – Input and controls
Components (in order):
1. **Image upload**
- Type: PIL image
- Label: “1. Input image”
- Behavior: user selects or drops a single image.
2. **Prompt**
- Textbox, 3 lines
- Label: “2. Prompt”
- Default value:
- `cinematic shot, smooth camera motion, high quality, detailed`
3. **Negative prompt**
- Textbox, 2 lines
- Label: “Negative prompt (optional)”
- Default value:
- `low quality, flicker, artifacts, distortion`
4. **LoRA selection**
- Dropdown (select)
- Label: “LoRA (style)”
- Choices: filenames of LoRA weights discovered in `Playtime-AI/Wan2.2-Loras`
- Default: first available LoRA, if any.
5. **LoRA strength**
- Slider
- Range: 0.0 – 2.0
- Step: 0.05
- Default: 1.0
- Label: “LoRA strength”
6. **Number of frames**
- Slider
- Range: 8 – 64
- Step: 1
- Default: 24
- Label: “Number of frames”
7. **Frames per second (FPS)**
- Slider
- Range: 4 – 30
- Step: 1
- Default: 12
- Label: “Frames per second”
8. **Diffusion steps**
- Slider
- Range: 10 – 80
- Step: 1
- Default: 30
- Label: “Diffusion steps”
9. **Guidance scale**
- Slider
- Range: 1.0 – 15.0
- Step: 0.5
- Default: 7.5
- Label: “Guidance scale”
10. **Seed**
- Numeric input
- Label: “Seed (0 = random)”
- Default: 0
- Behavior:
- If > 0, use as random seed for reproducibility.
- If 0 or missing, treat as random.
11. **Action button**
- Button
- Label: “3. Generate video”
- Style: visually prominent (primary).
#### Right column – Output and guidance
Components:
1. **Video output**
- `gr.Video` component
- Label: “Result”
- Autoplay enabled
2. **Short usage tips**
- Markdown text beneath the video, such as:
- Start with defaults for a quick first result.
- Increase frames or diffusion steps when you already like the base motion.
- Increase LoRA strength to emphasize style.
- Use a positive seed to reproduce a particularly good result.
### Usability priorities
- A non‑technical user must be able to:
- Upload an image
- Leave all defaults unchanged
- Click **one button**
- Receive a valid video without understanding any internal details.
- A power user must be able to:
- Tweak all parameters for finer control, including LoRA, frames, steps, guidance, and seed, without encountering fragile or surprising behavior.
The interface should communicate clearly through labels and ordering so that the “happy path” is obvious at a glance.
***
## Implementation details and robustness
### Device and precision
- Detect available hardware:
- Use `"cuda"` if a GPU is available; otherwise fall back to `"cpu"`.
- Precision:
- On GPU: prefer `torch.float16` for performance and memory efficiency.
- On CPU: use `torch.float32` for compatibility.
### Pipeline and LoRA lifecycle
- Create the pipeline once and reuse it:
- Cache the pipeline in a module‑level variable.
- When a LoRA is selected:
- Download it to a local path using a Hub API.
- Unload any previous LoRA if necessary.
- Load the new LoRA with a consistent adapter name.
- Apply the LoRA strength using the adapter weight mechanism appropriate for Wan + diffusers.
### Video assembly
- After generation, collect the frames from the pipeline output.
- Handle both potential output structures (for example, `result.frames[0]` or `result.images`, depending on the exact pipeline implementation).
- Use a standard library combination such as `imageio` and `imageio-ffmpeg`, or a compatible alternative, to assemble an MP4 with the desired FPS.
- Return the resulting video in a form that the Gradio `Video` component can display.
### Error handling
- If no image is provided, return gracefully with no video and no unhandled exceptions.
- If the pipeline returns no frames or an unexpected structure, handle it defensively rather than crashing.
- Keep internal logic straightforward and legible, optimizing for maintainability and debuggability.
***
## Dependencies (requirements.txt)
The application must declare dependencies compatible with Hugging Face Spaces GPU runtimes, including:
- `torch`
- `accelerate`
- `transformers`
- `diffusers`
- `huggingface_hub`
- `gradio`
- `safetensors`
- `Pillow`
- `imageio`
- `imageio-ffmpeg`
Version pinning is allowed and encouraged where known‑good combinations exist. The goal is a stable, repeatable environment, not experimental bleeding‑edge packages.
***
## Output formatting requirements for code generation agents
To maximize interoperability across AI Studio, Deepsite 3, Any Code, and similar systems, every code generation agent consuming this document should adhere to the following output format:
1. First, output a fenced code block labeled `requirements.txt` that contains **only** the final content of the `requirements.txt` file.
2. Second, output a fenced code block labeled `app.py` that contains **only** the final content of the `app.py` file.
3. Do **not** embed additional narrative or commentary inside these two code blocks.
4. Any explanatory notes, if required by the environment, must appear outside of and separate from these two blocks, so that the files can be copy‑pasted or exported as‑is.
This ensures that the resulting implementation is not only elegant and sophisticated, but also **immediately actionable**, giving users a truly zero‑code, high‑trust path from specification to a running Wan 2.2 image‑to‑video Space.
- README.md +8 -5
- components/footer.js +188 -0
- components/header.js +136 -0
- index.html +126 -19
- script.js +78 -0
- style.css +87 -18
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: WanVision Studio
|
| 3 |
+
colorFrom: yellow
|
| 4 |
+
colorTo: purple
|
| 5 |
+
emoji: 🐳
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite-v3
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Welcome to your new DeepSite project!
|
| 13 |
+
This project was created with [DeepSite](https://huggingface.co/deepsite).
|
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomFooter extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
:host {
|
| 7 |
+
display: block;
|
| 8 |
+
margin-top: 4rem;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
.footer {
|
| 12 |
+
background: #111827;
|
| 13 |
+
border-top: 1px solid #374151;
|
| 14 |
+
padding: 3rem 0 2rem;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
.container {
|
| 18 |
+
max-width: 1200px;
|
| 19 |
+
margin: 0 auto;
|
| 20 |
+
padding: 0 1rem;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.grid {
|
| 24 |
+
display: grid;
|
| 25 |
+
grid-template-columns: repeat(4, 1fr);
|
| 26 |
+
gap: 2rem;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
.footer-logo {
|
| 30 |
+
display: flex;
|
| 31 |
+
align-items: center;
|
| 32 |
+
gap: 0.75rem;
|
| 33 |
+
font-weight: 700;
|
| 34 |
+
font-size: 1.25rem;
|
| 35 |
+
margin-bottom: 1rem;
|
| 36 |
+
background: linear-gradient(90deg, #10b981, #f97316);
|
| 37 |
+
-webkit-background-clip: text;
|
| 38 |
+
-webkit-text-fill-color: transparent;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.footer-description {
|
| 42 |
+
color: #9ca3af;
|
| 43 |
+
line-height: 1.6;
|
| 44 |
+
margin-bottom: 1.5rem;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
.social-links {
|
| 48 |
+
display: flex;
|
| 49 |
+
gap: 1rem;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.social-link {
|
| 53 |
+
display: flex;
|
| 54 |
+
align-items: center;
|
| 55 |
+
justify-content: center;
|
| 56 |
+
width: 40px;
|
| 57 |
+
height: 40px;
|
| 58 |
+
border-radius: 50%;
|
| 59 |
+
background: #1f2937;
|
| 60 |
+
color: #9ca3af;
|
| 61 |
+
transition: all 0.2s;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.social-link:hover {
|
| 65 |
+
background: #10b981;
|
| 66 |
+
color: white;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
.footer-heading {
|
| 70 |
+
color: white;
|
| 71 |
+
font-weight: 600;
|
| 72 |
+
margin-bottom: 1.5rem;
|
| 73 |
+
font-size: 1.1rem;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
.footer-links {
|
| 77 |
+
list-style: none;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.footer-link {
|
| 81 |
+
margin-bottom: 0.75rem;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.footer-link a {
|
| 85 |
+
color: #9ca3af;
|
| 86 |
+
text-decoration: none;
|
| 87 |
+
transition: color 0.2s;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.footer-link a:hover {
|
| 91 |
+
color: #10b981;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.copyright {
|
| 95 |
+
border-top: 1px solid #374151;
|
| 96 |
+
padding-top: 2rem;
|
| 97 |
+
margin-top: 3rem;
|
| 98 |
+
text-align: center;
|
| 99 |
+
color: #6b7280;
|
| 100 |
+
font-size: 0.9rem;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
@media (max-width: 768px) {
|
| 104 |
+
.grid {
|
| 105 |
+
grid-template-columns: repeat(2, 1fr);
|
| 106 |
+
}
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
@media (max-width: 480px) {
|
| 110 |
+
.grid {
|
| 111 |
+
grid-template-columns: 1fr;
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
</style>
|
| 115 |
+
|
| 116 |
+
<footer class="footer">
|
| 117 |
+
<div class="container">
|
| 118 |
+
<div class="grid">
|
| 119 |
+
<div class="footer-column">
|
| 120 |
+
<div class="footer-logo">
|
| 121 |
+
<i data-feather="film"></i>
|
| 122 |
+
<span>WanVision Studio</span>
|
| 123 |
+
</div>
|
| 124 |
+
<p class="footer-description">
|
| 125 |
+
Transform your images into stunning videos with AI-powered technology.
|
| 126 |
+
</p>
|
| 127 |
+
<div class="social-links">
|
| 128 |
+
<a href="#" class="social-link">
|
| 129 |
+
<i data-feather="twitter" style="width: 18px; height: 18px;"></i>
|
| 130 |
+
</a>
|
| 131 |
+
<a href="#" class="social-link">
|
| 132 |
+
<i data-feather="github" style="width: 18px; height: 18px;"></i>
|
| 133 |
+
</a>
|
| 134 |
+
<a href="#" class="social-link">
|
| 135 |
+
<i data-feather="linkedin" style="width: 18px; height: 18px;"></i>
|
| 136 |
+
</a>
|
| 137 |
+
<a href="#" class="social-link">
|
| 138 |
+
<i data-feather="instagram" style="width: 18px; height: 18px;"></i>
|
| 139 |
+
</a>
|
| 140 |
+
</div>
|
| 141 |
+
</div>
|
| 142 |
+
|
| 143 |
+
<div class="footer-column">
|
| 144 |
+
<h3 class="footer-heading">Product</h3>
|
| 145 |
+
<ul class="footer-links">
|
| 146 |
+
<li class="footer-link"><a href="#">Features</a></li>
|
| 147 |
+
<li class="footer-link"><a href="#">Pricing</a></li>
|
| 148 |
+
<li class="footer-link"><a href="#">Examples</a></li>
|
| 149 |
+
<li class="footer-link"><a href="#">API</a></li>
|
| 150 |
+
</ul>
|
| 151 |
+
</div>
|
| 152 |
+
|
| 153 |
+
<div class="footer-column">
|
| 154 |
+
<h3 class="footer-heading">Resources</h3>
|
| 155 |
+
<ul class="footer-links">
|
| 156 |
+
<li class="footer-link"><a href="#">Documentation</a></li>
|
| 157 |
+
<li class="footer-link"><a href="#">Tutorials</a></li>
|
| 158 |
+
<li class="footer-link"><a href="#">Blog</a></li>
|
| 159 |
+
<li class="footer-link"><a href="#">Support</a></li>
|
| 160 |
+
</ul>
|
| 161 |
+
</div>
|
| 162 |
+
|
| 163 |
+
<div class="footer-column">
|
| 164 |
+
<h3 class="footer-heading">Company</h3>
|
| 165 |
+
<ul class="footer-links">
|
| 166 |
+
<li class="footer-link"><a href="#">About</a></li>
|
| 167 |
+
<li class="footer-link"><a href="#">Careers</a></li>
|
| 168 |
+
<li class="footer-link"><a href="#">Contact</a></li>
|
| 169 |
+
<li class="footer-link"><a href="#">Legal</a></li>
|
| 170 |
+
</ul>
|
| 171 |
+
</div>
|
| 172 |
+
</div>
|
| 173 |
+
|
| 174 |
+
<div class="copyright">
|
| 175 |
+
© ${new Date().getFullYear()} WanVision Studio. All rights reserved.
|
| 176 |
+
</div>
|
| 177 |
+
</div>
|
| 178 |
+
</footer>
|
| 179 |
+
`;
|
| 180 |
+
|
| 181 |
+
// Initialize feather icons after content is added
|
| 182 |
+
setTimeout(() => {
|
| 183 |
+
feather.replace();
|
| 184 |
+
}, 0);
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
customElements.define('custom-footer', CustomFooter);
|
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomHeader extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
:host {
|
| 7 |
+
display: block;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
.header {
|
| 11 |
+
background: linear-gradient(90deg, #111827 0%, #1f2937 100%);
|
| 12 |
+
border-bottom: 1px solid #374151;
|
| 13 |
+
padding: 1rem 0;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
.container {
|
| 17 |
+
max-width: 1200px;
|
| 18 |
+
margin: 0 auto;
|
| 19 |
+
padding: 0 1rem;
|
| 20 |
+
display: flex;
|
| 21 |
+
justify-content: space-between;
|
| 22 |
+
align-items: center;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
.logo {
|
| 26 |
+
display: flex;
|
| 27 |
+
align-items: center;
|
| 28 |
+
gap: 0.75rem;
|
| 29 |
+
font-weight: 700;
|
| 30 |
+
font-size: 1.5rem;
|
| 31 |
+
background: linear-gradient(90deg, #10b981, #f97316);
|
| 32 |
+
-webkit-background-clip: text;
|
| 33 |
+
-webkit-text-fill-color: transparent;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
.nav-links {
|
| 37 |
+
display: flex;
|
| 38 |
+
gap: 2rem;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.nav-link {
|
| 42 |
+
color: #9ca3af;
|
| 43 |
+
text-decoration: none;
|
| 44 |
+
font-weight: 500;
|
| 45 |
+
transition: color 0.2s;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
.nav-link:hover {
|
| 49 |
+
color: #10b981;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.auth-buttons {
|
| 53 |
+
display: flex;
|
| 54 |
+
gap: 1rem;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
.btn {
|
| 58 |
+
padding: 0.5rem 1rem;
|
| 59 |
+
border-radius: 0.5rem;
|
| 60 |
+
font-weight: 500;
|
| 61 |
+
transition: all 0.2s;
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
.btn-outline {
|
| 65 |
+
border: 1px solid #374151;
|
| 66 |
+
background: transparent;
|
| 67 |
+
color: #d1d5db;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
.btn-outline:hover {
|
| 71 |
+
border-color: #10b981;
|
| 72 |
+
color: #10b981;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
.btn-primary {
|
| 76 |
+
background: linear-gradient(90deg, #10b981, #f97316);
|
| 77 |
+
color: white;
|
| 78 |
+
border: none;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
.btn-primary:hover {
|
| 82 |
+
opacity: 0.9;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
@media (max-width: 768px) {
|
| 86 |
+
.nav-links {
|
| 87 |
+
display: none;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.auth-buttons {
|
| 91 |
+
gap: 0.5rem;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.btn {
|
| 95 |
+
padding: 0.4rem 0.8rem;
|
| 96 |
+
font-size: 0.9rem;
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
</style>
|
| 100 |
+
|
| 101 |
+
<header class="header">
|
| 102 |
+
<div class="container">
|
| 103 |
+
<div class="logo">
|
| 104 |
+
<i data-feather="film"></i>
|
| 105 |
+
<span>WanVision Studio</span>
|
| 106 |
+
</div>
|
| 107 |
+
|
| 108 |
+
<nav class="nav-links">
|
| 109 |
+
<a href="#" class="nav-link">Home</a>
|
| 110 |
+
<a href="#" class="nav-link">Gallery</a>
|
| 111 |
+
<a href="#" class="nav-link">Docs</a>
|
| 112 |
+
<a href="#" class="nav-link">Pricing</a>
|
| 113 |
+
</nav>
|
| 114 |
+
|
| 115 |
+
<div class="auth-buttons">
|
| 116 |
+
<button class="btn btn-outline">
|
| 117 |
+
<i data-feather="log-in" style="width: 16px; height: 16px;"></i>
|
| 118 |
+
<span class="ml-1">Login</span>
|
| 119 |
+
</button>
|
| 120 |
+
<button class="btn btn-primary">
|
| 121 |
+
<i data-feather="user-plus" style="width: 16px; height: 16px;"></i>
|
| 122 |
+
<span class="ml-1">Sign Up</span>
|
| 123 |
+
</button>
|
| 124 |
+
</div>
|
| 125 |
+
</div>
|
| 126 |
+
</header>
|
| 127 |
+
`;
|
| 128 |
+
|
| 129 |
+
// Initialize feather icons after content is added
|
| 130 |
+
setTimeout(() => {
|
| 131 |
+
feather.replace();
|
| 132 |
+
}, 0);
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
customElements.define('custom-header', CustomHeader);
|
|
@@ -1,19 +1,126 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>WanVision Studio</title>
|
| 7 |
+
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
|
| 8 |
+
<link rel="stylesheet" href="style.css">
|
| 9 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 10 |
+
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/feather-icons"></script>
|
| 12 |
+
</head>
|
| 13 |
+
<body class="bg-gray-900 text-white min-h-screen">
|
| 14 |
+
<custom-header></custom-header>
|
| 15 |
+
|
| 16 |
+
<main class="container mx-auto px-4 py-8">
|
| 17 |
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
| 18 |
+
<!-- Input Section -->
|
| 19 |
+
<div class="bg-gray-800 rounded-xl p-6 shadow-2xl">
|
| 20 |
+
<h2 class="text-2xl font-bold mb-6 text-green-400">Create Your Video</h2>
|
| 21 |
+
|
| 22 |
+
<div class="space-y-6">
|
| 23 |
+
<!-- Image Upload -->
|
| 24 |
+
<div>
|
| 25 |
+
<label class="block text-sm font-medium mb-2">1. Input Image</label>
|
| 26 |
+
<div class="border-2 border-dashed border-gray-600 rounded-lg p-8 text-center cursor-pointer hover:border-green-500 transition-colors">
|
| 27 |
+
<i data-feather="upload" class="mx-auto h-12 w-12 text-gray-400"></i>
|
| 28 |
+
<p class="mt-2 text-sm text-gray-400">Click to upload or drag and drop</p>
|
| 29 |
+
<p class="text-xs text-gray-500">PNG, JPG up to 10MB</p>
|
| 30 |
+
<input type="file" class="hidden" accept="image/*">
|
| 31 |
+
</div>
|
| 32 |
+
</div>
|
| 33 |
+
|
| 34 |
+
<!-- Prompt -->
|
| 35 |
+
<div>
|
| 36 |
+
<label class="block text-sm font-medium mb-2">2. Prompt</label>
|
| 37 |
+
<textarea class="w-full bg-gray-700 border border-gray-600 rounded-lg p-3 focus:ring-2 focus:ring-green-500 focus:border-transparent" rows="3">cinematic shot, smooth camera motion, high quality, detailed</textarea>
|
| 38 |
+
</div>
|
| 39 |
+
|
| 40 |
+
<!-- Negative Prompt -->
|
| 41 |
+
<div>
|
| 42 |
+
<label class="block text-sm font-medium mb-2">Negative Prompt (optional)</label>
|
| 43 |
+
<textarea class="w-full bg-gray-700 border border-gray-600 rounded-lg p-3 focus:ring-2 focus:ring-green-500 focus:border-transparent" rows="2">low quality, flicker, artifacts, distortion</textarea>
|
| 44 |
+
</div>
|
| 45 |
+
|
| 46 |
+
<!-- LoRA Selection -->
|
| 47 |
+
<div>
|
| 48 |
+
<label class="block text-sm font-medium mb-2">LoRA (style)</label>
|
| 49 |
+
<select class="w-full bg-gray-700 border border-gray-600 rounded-lg p-3 focus:ring-2 focus:ring-green-500 focus:border-transparent">
|
| 50 |
+
<option>Cinematic Style</option>
|
| 51 |
+
<option>Anime Art</option>
|
| 52 |
+
<option>Realistic Photography</option>
|
| 53 |
+
<option>Oil Painting</option>
|
| 54 |
+
</select>
|
| 55 |
+
</div>
|
| 56 |
+
|
| 57 |
+
<!-- Sliders -->
|
| 58 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 59 |
+
<div>
|
| 60 |
+
<label class="block text-sm font-medium mb-2">LoRA Strength: <span class="text-green-400">1.0</span></label>
|
| 61 |
+
<input type="range" min="0" max="2" step="0.05" value="1" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer slider">
|
| 62 |
+
</div>
|
| 63 |
+
<div>
|
| 64 |
+
<label class="block text-sm font-medium mb-2">Frames: <span class="text-green-400">24</span></label>
|
| 65 |
+
<input type="range" min="8" max="64" step="1" value="24" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer slider">
|
| 66 |
+
</div>
|
| 67 |
+
<div>
|
| 68 |
+
<label class="block text-sm font-medium mb-2">FPS: <span class="text-green-400">12</span></label>
|
| 69 |
+
<input type="range" min="4" max="30" step="1" value="12" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer slider">
|
| 70 |
+
</div>
|
| 71 |
+
<div>
|
| 72 |
+
<label class="block text-sm font-medium mb-2">Steps: <span class="text-green-400">30</span></label>
|
| 73 |
+
<input type="range" min="10" max="80" step="1" value="30" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer slider">
|
| 74 |
+
</div>
|
| 75 |
+
<div>
|
| 76 |
+
<label class="block text-sm font-medium mb-2">Guidance: <span class="text-green-400">7.5</span></label>
|
| 77 |
+
<input type="range" min="1" max="15" step="0.5" value="7.5" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer slider">
|
| 78 |
+
</div>
|
| 79 |
+
<div>
|
| 80 |
+
<label class="block text-sm font-medium mb-2">Seed: <span class="text-green-400">0</span></label>
|
| 81 |
+
<input type="number" value="0" class="w-full bg-gray-700 border border-gray-600 rounded-lg p-2 focus:ring-2 focus:ring-green-500 focus:border-transparent">
|
| 82 |
+
</div>
|
| 83 |
+
</div>
|
| 84 |
+
|
| 85 |
+
<!-- Generate Button -->
|
| 86 |
+
<button class="w-full bg-gradient-to-r from-green-500 to-orange-500 text-white font-bold py-4 px-6 rounded-lg hover:opacity-90 transition-all transform hover:scale-[1.02]">
|
| 87 |
+
3. Generate Video
|
| 88 |
+
</button>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
|
| 92 |
+
<!-- Output Section -->
|
| 93 |
+
<div class="bg-gray-800 rounded-xl p-6 shadow-2xl">
|
| 94 |
+
<h2 class="text-2xl font-bold mb-6 text-green-400">Result</h2>
|
| 95 |
+
|
| 96 |
+
<!-- Video Preview -->
|
| 97 |
+
<div class="bg-gray-900 rounded-lg aspect-video flex items-center justify-center mb-6">
|
| 98 |
+
<div class="text-center">
|
| 99 |
+
<i data-feather="video" class="mx-auto h-16 w-16 text-gray-600"></i>
|
| 100 |
+
<p class="mt-4 text-gray-500">Your video will appear here</p>
|
| 101 |
+
</div>
|
| 102 |
+
</div>
|
| 103 |
+
|
| 104 |
+
<!-- Tips -->
|
| 105 |
+
<div class="bg-gray-700 rounded-lg p-4">
|
| 106 |
+
<h3 class="font-bold mb-2 text-orange-400">Tips</h3>
|
| 107 |
+
<ul class="text-sm space-y-1 text-gray-300">
|
| 108 |
+
<li>• Start with defaults for a quick first result</li>
|
| 109 |
+
<li>• Increase frames or steps when you like the base motion</li>
|
| 110 |
+
<li>• Increase LoRA strength to emphasize style</li>
|
| 111 |
+
<li>• Use a positive seed to reproduce a good result</li>
|
| 112 |
+
</ul>
|
| 113 |
+
</div>
|
| 114 |
+
</div>
|
| 115 |
+
</div>
|
| 116 |
+
</main>
|
| 117 |
+
|
| 118 |
+
<custom-footer></custom-footer>
|
| 119 |
+
|
| 120 |
+
<script src="components/header.js"></script>
|
| 121 |
+
<script src="components/footer.js"></script>
|
| 122 |
+
<script src="script.js"></script>
|
| 123 |
+
<script>feather.replace();</script>
|
| 124 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 125 |
+
</body>
|
| 126 |
+
</html>
|
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Main application script
|
| 2 |
+
document.addEventListener('DOMContentLoaded', function() {
|
| 3 |
+
// Initialize all feather icons
|
| 4 |
+
feather.replace();
|
| 5 |
+
|
| 6 |
+
// Slider value display update
|
| 7 |
+
const sliders = document.querySelectorAll('input[type="range"]');
|
| 8 |
+
sliders.forEach(slider => {
|
| 9 |
+
const updateValue = () => {
|
| 10 |
+
const label = slider.previousElementSibling;
|
| 11 |
+
const valueSpan = label.querySelector('span');
|
| 12 |
+
if (valueSpan) {
|
| 13 |
+
valueSpan.textContent = slider.value;
|
| 14 |
+
}
|
| 15 |
+
};
|
| 16 |
+
|
| 17 |
+
slider.addEventListener('input', updateValue);
|
| 18 |
+
updateValue(); // Initial call
|
| 19 |
+
});
|
| 20 |
+
|
| 21 |
+
// File upload interaction
|
| 22 |
+
const uploadArea = document.querySelector('.border-dashed');
|
| 23 |
+
const fileInput = uploadArea.querySelector('input[type="file"]');
|
| 24 |
+
|
| 25 |
+
uploadArea.addEventListener('click', () => {
|
| 26 |
+
fileInput.click();
|
| 27 |
+
});
|
| 28 |
+
|
| 29 |
+
fileInput.addEventListener('change', function(e) {
|
| 30 |
+
if (this.files && this.files[0]) {
|
| 31 |
+
const reader = new FileReader();
|
| 32 |
+
|
| 33 |
+
reader.onload = function(e) {
|
| 34 |
+
uploadArea.innerHTML = `
|
| 35 |
+
<div class="flex flex-col items-center">
|
| 36 |
+
<img src="${e.target.result}" alt="Preview" class="max-h-48 rounded-lg">
|
| 37 |
+
<p class="mt-2 text-sm">${this.files[0].name}</p>
|
| 38 |
+
</div>
|
| 39 |
+
`;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
reader.readAsDataURL(this.files[0]);
|
| 43 |
+
}
|
| 44 |
+
});
|
| 45 |
+
|
| 46 |
+
// Generate button interaction
|
| 47 |
+
const generateBtn = document.querySelector('button.bg-gradient-to-r');
|
| 48 |
+
generateBtn.addEventListener('click', function() {
|
| 49 |
+
// Add loading state
|
| 50 |
+
const originalText = this.innerHTML;
|
| 51 |
+
this.innerHTML = `
|
| 52 |
+
<div class="flex items-center justify-center">
|
| 53 |
+
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
| 54 |
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
| 55 |
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
| 56 |
+
</svg>
|
| 57 |
+
Generating...
|
| 58 |
+
</div>
|
| 59 |
+
`;
|
| 60 |
+
this.disabled = true;
|
| 61 |
+
|
| 62 |
+
// Simulate processing time
|
| 63 |
+
setTimeout(() => {
|
| 64 |
+
// Reset button
|
| 65 |
+
this.innerHTML = originalText;
|
| 66 |
+
this.disabled = false;
|
| 67 |
+
|
| 68 |
+
// Show result in video preview area
|
| 69 |
+
const videoPreview = document.querySelector('.bg-gray-900');
|
| 70 |
+
videoPreview.innerHTML = `
|
| 71 |
+
<video controls class="w-full h-full rounded-lg">
|
| 72 |
+
<source src="http://static.photos/technology/1200x630/123" type="video/mp4">
|
| 73 |
+
Your browser does not support the video tag.
|
| 74 |
+
</video>
|
| 75 |
+
`;
|
| 76 |
+
}, 3000);
|
| 77 |
+
});
|
| 78 |
+
});
|
|
@@ -1,28 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
body {
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
| 16 |
}
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
}
|
| 25 |
|
| 26 |
-
.
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
| 2 |
+
|
| 3 |
+
:root {
|
| 4 |
+
--primary-green: #10b981;
|
| 5 |
+
--secondary-orange: #f97316;
|
| 6 |
+
--dark-bg: #111827;
|
| 7 |
+
--card-bg: #1f2937;
|
| 8 |
+
--border-color: #374151;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
body {
|
| 12 |
+
font-family: 'Inter', sans-serif;
|
| 13 |
+
background-color: var(--dark-bg);
|
| 14 |
+
color: white;
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
/* Custom slider styling */
|
| 18 |
+
.slider {
|
| 19 |
+
background: linear-gradient(to right, var(--primary-green), var(--secondary-orange));
|
| 20 |
+
height: 8px;
|
| 21 |
+
border-radius: 4px;
|
| 22 |
+
outline: none;
|
| 23 |
+
-webkit-appearance: none;
|
| 24 |
}
|
| 25 |
|
| 26 |
+
.slider::-webkit-slider-thumb {
|
| 27 |
+
-webkit-appearance: none;
|
| 28 |
+
appearance: none;
|
| 29 |
+
width: 20px;
|
| 30 |
+
height: 20px;
|
| 31 |
+
border-radius: 50%;
|
| 32 |
+
background: white;
|
| 33 |
+
cursor: pointer;
|
| 34 |
+
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
|
| 35 |
}
|
| 36 |
|
| 37 |
+
.slider::-moz-range-thumb {
|
| 38 |
+
width: 20px;
|
| 39 |
+
height: 20px;
|
| 40 |
+
border-radius: 50%;
|
| 41 |
+
background: white;
|
| 42 |
+
cursor: pointer;
|
| 43 |
+
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
|
| 44 |
}
|
| 45 |
|
| 46 |
+
/* Gradient borders */
|
| 47 |
+
.gradient-border {
|
| 48 |
+
position: relative;
|
| 49 |
+
border-radius: 0.75rem;
|
|
|
|
|
|
|
| 50 |
}
|
| 51 |
|
| 52 |
+
.gradient-border::before {
|
| 53 |
+
content: '';
|
| 54 |
+
position: absolute;
|
| 55 |
+
inset: 0;
|
| 56 |
+
border-radius: 0.75rem;
|
| 57 |
+
padding: 2px;
|
| 58 |
+
background: linear-gradient(to right, var(--primary-green), var(--secondary-orange));
|
| 59 |
+
-webkit-mask:
|
| 60 |
+
linear-gradient(#fff 0 0) content-box,
|
| 61 |
+
linear-gradient(#fff 0 0);
|
| 62 |
+
mask:
|
| 63 |
+
linear-gradient(#fff 0 0) content-box,
|
| 64 |
+
linear-gradient(#fff 0 0);
|
| 65 |
+
-webkit-mask-composite: xor;
|
| 66 |
+
mask-composite: exclude;
|
| 67 |
+
pointer-events: none;
|
| 68 |
}
|
| 69 |
+
|
| 70 |
+
/* Responsive adjustments */
|
| 71 |
+
@media (max-width: 768px) {
|
| 72 |
+
.grid {
|
| 73 |
+
grid-template-columns: 1fr;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
.text-5xl {
|
| 77 |
+
font-size: 2.5rem;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.text-2xl {
|
| 81 |
+
font-size: 1.75rem;
|
| 82 |
+
}
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/* Animation for loading states */
|
| 86 |
+
@keyframes pulse {
|
| 87 |
+
0%, 100% {
|
| 88 |
+
opacity: 1;
|
| 89 |
+
}
|
| 90 |
+
50% {
|
| 91 |
+
opacity: 0.5;
|
| 92 |
+
}
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
.pulse {
|
| 96 |
+
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
| 97 |
+
}
|