Spaces:
Configuration error
Configuration error
AIGoose commited on
Commit ·
9563efc
1
Parent(s): 1f05b41
feat: hyperframes-video skill v2.0 — GitHub/HF pipeline + video transcription
Browse files- skills/hyperframes-video/SKILL.md +14 -295
skills/hyperframes-video/SKILL.md
CHANGED
|
@@ -1,295 +1,14 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
## BLACK FRAME PREVENTION (from heygen HANDOFF.md + coleam00 transitions.md)
|
| 16 |
-
|
| 17 |
-
**Root causes of black frames:**
|
| 18 |
-
- Timeline shorter than expected: always `tl.set({}, {}, TOTAL_DURATION)` at the very end
|
| 19 |
-
- Scene overlap on same track: clips on the same `data-track-index` CANNOT overlap even 0.001s
|
| 20 |
-
- Exit animations BANNED (except final scene): do NOT `gsap.to()` elements out before transition
|
| 21 |
-
The transition IS the exit. Outgoing content must be fully visible when transition fires.
|
| 22 |
-
- Scene gap: next scene `data-start` must not exceed current `data-start + data-duration`
|
| 23 |
-
|
| 24 |
-
**Correct scene timing pattern (no gaps, no overlap):**
|
| 25 |
-
```
|
| 26 |
-
Scene 1: data-start="0" data-duration="4" → ends at 4.0s
|
| 27 |
-
Scene 2: data-start="3.65" data-duration="4" → starts at 3.65s (0.35s overlap for transition)
|
| 28 |
-
Scene 3: data-start="7.65" data-duration="8" → starts at 7.65s
|
| 29 |
-
```
|
| 30 |
-
Scenes on DIFFERENT track-indices can overlap (that's how transitions work).
|
| 31 |
-
Scenes on the SAME track-index must NEVER overlap.
|
| 32 |
-
|
| 33 |
-
## AUDIO (from coleam00 audio-design.md — research-backed volumes)
|
| 34 |
-
|
| 35 |
-
```
|
| 36 |
-
data-volume levels:
|
| 37 |
-
Subject voice (testimonial video): 0.88 (≈ -14 dB peak)
|
| 38 |
-
Music during energy/CTA: 0.12 (≈ -15 to -18 dB)
|
| 39 |
-
Music during testimonial video: 0.07 (≈ -20 to -23 dB)
|
| 40 |
-
Music general background: 0.10
|
| 41 |
-
Narration/voiceover: 1.0 (≈ -12 dB peak)
|
| 42 |
-
```
|
| 43 |
-
|
| 44 |
-
**NEVER** use lyrical music under speech (proven comprehension harm).
|
| 45 |
-
**ALWAYS** use `force_instrumental: true` on ElevenLabs music API.
|
| 46 |
-
**NEVER** add `muted` attribute to video when subject voice is needed.
|
| 47 |
-
|
| 48 |
-
## ELEVENLABS MUSIC GENERATION (primary)
|
| 49 |
-
|
| 50 |
-
```python
|
| 51 |
-
# generate_music.py — generates lo-fi upbeat track for Dee Ferdinand videos
|
| 52 |
-
from elevenlabs import ElevenLabs
|
| 53 |
-
import os
|
| 54 |
-
|
| 55 |
-
client = ElevenLabs(api_key=os.environ["ELEVENLABS_API_KEY"])
|
| 56 |
-
|
| 57 |
-
audio = client.music.compose(
|
| 58 |
-
prompt="Lo-fi upbeat instrumental, warm jazzy piano chords, soft vinyl crackle, "
|
| 59 |
-
"positive motivational energy, subtle hip-hop drums, 88 BPM, "
|
| 60 |
-
"no lyrics, background music for AI corporate training video",
|
| 61 |
-
music_length_ms=35000, # always generate 5s extra beyond video duration
|
| 62 |
-
model_id="music_v2",
|
| 63 |
-
force_instrumental=True,
|
| 64 |
-
)
|
| 65 |
-
|
| 66 |
-
with open("./assets/music.mp3", "wb") as f:
|
| 67 |
-
for chunk in audio:
|
| 68 |
-
f.write(chunk)
|
| 69 |
-
```
|
| 70 |
-
|
| 71 |
-
**Prompt templates by workflow:**
|
| 72 |
-
|
| 73 |
-
| Workflow | ElevenLabs prompt |
|
| 74 |
-
|----------|-------------------|
|
| 75 |
-
| testimonial | "Lo-fi upbeat instrumental, warm jazzy piano, vinyl crackle, positive energy, 88 BPM, no lyrics, for AI corporate training" |
|
| 76 |
-
| teaser | "Energetic upbeat electronic, punchy kick drum, rising synths, 120 BPM, no lyrics, for event teaser" |
|
| 77 |
-
| trailer | "Cinematic orchestral build, emotional strings, powerful swells, 90 BPM, no lyrics, for brand trailer" |
|
| 78 |
-
| community | "Warm acoustic guitar, gentle piano, uplifting and heartfelt, 75 BPM, no lyrics, for community story" |
|
| 79 |
-
|
| 80 |
-
**Requires:** `ELEVENLABS_API_KEY` env var in HF Space secrets (Settings → Variables).
|
| 81 |
-
|
| 82 |
-
## YUE FALLBACK (when ElevenLabs key not available)
|
| 83 |
-
|
| 84 |
-
```python
|
| 85 |
-
# YuE: open-source music generation from multimodal-art-projection
|
| 86 |
-
# GitHub: https://github.com/multimodal-art-projection/YuE
|
| 87 |
-
# Use via Hugging Face Inference API (no local GPU needed)
|
| 88 |
-
import requests, os
|
| 89 |
-
|
| 90 |
-
def generate_music_yue(prompt: str, duration_seconds: int = 35) -> bytes:
|
| 91 |
-
"""
|
| 92 |
-
YuE music generation via HF Inference API.
|
| 93 |
-
Falls back to this when ELEVENLABS_API_KEY is not set.
|
| 94 |
-
"""
|
| 95 |
-
HF_TOKEN = os.environ.get("HF_TOKEN", "")
|
| 96 |
-
headers = {"Authorization": f"Bearer {HF_TOKEN}"} if HF_TOKEN else {}
|
| 97 |
-
|
| 98 |
-
# YuE model on HF Hub
|
| 99 |
-
url = "https://api-inference.huggingface.co/models/m-a-p/YuE-s1-7B-anneal-en-cot"
|
| 100 |
-
|
| 101 |
-
payload = {
|
| 102 |
-
"inputs": prompt,
|
| 103 |
-
"parameters": {
|
| 104 |
-
"max_length": duration_seconds * 50, # approximate token count
|
| 105 |
-
"num_return_sequences": 1,
|
| 106 |
-
}
|
| 107 |
-
}
|
| 108 |
-
|
| 109 |
-
r = requests.post(url, headers=headers, json=payload, timeout=120)
|
| 110 |
-
if r.status_code == 200:
|
| 111 |
-
return r.content # audio bytes
|
| 112 |
-
raise Exception(f"YuE generation failed: {r.status_code} {r.text[:200]}")
|
| 113 |
-
```
|
| 114 |
-
|
| 115 |
-
**YuE prompt format:**
|
| 116 |
-
```
|
| 117 |
-
[Genre: Lo-fi Hip Hop] [Mood: Upbeat, Positive] [Instruments: Piano, Drums, Bass]
|
| 118 |
-
Background instrumental music for corporate AI training video. No lyrics. 88 BPM.
|
| 119 |
-
```
|
| 120 |
-
|
| 121 |
-
## DEE FERDINAND BRAND FONTS
|
| 122 |
-
|
| 123 |
-
```html
|
| 124 |
-
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;700;900
|
| 125 |
-
&family=Plus+Jakarta+Sans:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400;1,600
|
| 126 |
-
&display=swap" rel="stylesheet">
|
| 127 |
-
```
|
| 128 |
-
|
| 129 |
-
- **ALL display titles / kinetic captions:** `font: 900 Xpx 'Space Grotesk', sans-serif`
|
| 130 |
-
- **Body / quotes / lower thirds / UI text:** `font: 600 Xpx 'Plus Jakarta Sans', sans-serif`
|
| 131 |
-
|
| 132 |
-
## CAPTION SIZES — 2X KINETIC (Dee's style)
|
| 133 |
-
|
| 134 |
-
| Element | Size | Weight | Font |
|
| 135 |
-
|---------|------|--------|------|
|
| 136 |
-
| Hook line 1 | **160px** | 900 | Space Grotesk |
|
| 137 |
-
| Hook line 2 (accent color) | **160px** | 900 | Space Grotesk |
|
| 138 |
-
| Hook sub | **72px** | 700 | Plus Jakarta Sans |
|
| 139 |
-
| Stat counter | **160px** | 900 | Space Grotesk |
|
| 140 |
-
| Energy cut captions | **128-144px** | 900 | Space Grotesk |
|
| 141 |
-
| CTA main | **112px** | 900 | Space Grotesk |
|
| 142 |
-
| Testimonial quote | **72px italic** | 600 | Plus Jakarta Sans |
|
| 143 |
-
| Lower third title | **40px** | 700 | Plus Jakarta Sans |
|
| 144 |
-
| Lower third subtitle | **24px** | 400 | Plus Jakarta Sans |
|
| 145 |
-
| Brand end name | **80px** | 800 | Space Grotesk |
|
| 146 |
-
| Brand end tagline | **40px** | 500 | Plus Jakarta Sans |
|
| 147 |
-
|
| 148 |
-
## DARK PREMIUM PALETTE
|
| 149 |
-
|
| 150 |
-
```css
|
| 151 |
-
:root {
|
| 152 |
-
--bg: #0d0d1f; /* deep navy-black canvas */
|
| 153 |
-
--accent: #7C6FE0; /* purple accent */
|
| 154 |
-
--accent-2: #E06F9A; /* pink accent */
|
| 155 |
-
--energy: #9EF0C8; /* teal-green for wins/energy */
|
| 156 |
-
--text: #ffffff;
|
| 157 |
-
--text-dim: rgba(255,255,255,0.72);
|
| 158 |
-
--text-muted: rgba(255,255,255,0.48);
|
| 159 |
-
}
|
| 160 |
-
```
|
| 161 |
-
|
| 162 |
-
## MOTION VOCABULARY (from nateherkai motion-principles.md)
|
| 163 |
-
|
| 164 |
-
Vary eases — NEVER use same ease twice in a row.
|
| 165 |
-
|
| 166 |
-
| Moment | GSAP ease | Duration | Feel |
|
| 167 |
-
|--------|-----------|----------|------|
|
| 168 |
-
| Hook title slam | `expo.out` | 0.20-0.25s | Kinetic energy |
|
| 169 |
-
| Stat pop | `back.out(2.5)` | 0.40-0.50s | Bouncy confidence |
|
| 170 |
-
| Energy cut caption | `power4.out` | 0.22-0.28s | Fast/decisive |
|
| 171 |
-
| CTA slide up | `power3.out` | 0.45-0.55s | Professional |
|
| 172 |
-
| Testimonial quote | `sine.inOut` | 0.55-0.65s | Calm contrast |
|
| 173 |
-
| Brand end stagger | `power3.out` | 0.35-0.45s | Composed |
|
| 174 |
-
|
| 175 |
-
**Direction variety (never all the same):**
|
| 176 |
-
- Slam from above: `y: -100`
|
| 177 |
-
- Slide from left: `x: -120`
|
| 178 |
-
- Slide from right: `x: 120`
|
| 179 |
-
- Scale pop: `scale: 0.55`
|
| 180 |
-
- Opacity only: `autoAlpha: 0` (no translate) — for calm moments
|
| 181 |
-
|
| 182 |
-
## TRANSITION PATTERNS (from coleam00 transitions.md)
|
| 183 |
-
|
| 184 |
-
Rule: EXIT ANIMATIONS ARE BANNED except on final scene. The transition IS the exit.
|
| 185 |
-
Incoming scene's entrance fires at transition midpoint, outgoing scene fades simultaneously.
|
| 186 |
-
|
| 187 |
-
```javascript
|
| 188 |
-
// Zoom-through (dramatic opener, hook→2)
|
| 189 |
-
const T1 = 3.65; // transition start
|
| 190 |
-
tl.to("#s1", { autoAlpha: 0, scale: 1.3, duration: 0.35, ease: "power4.inOut" }, T1);
|
| 191 |
-
tl.fromTo("#s2", { autoAlpha: 0, scale: 0.8 }, { autoAlpha: 1, scale: 1, duration: 0.35, ease: "power4.inOut" }, T1);
|
| 192 |
-
|
| 193 |
-
// Push-slide left (editorial, proof→3)
|
| 194 |
-
const T2 = 7.65;
|
| 195 |
-
tl.to("#s2", { autoAlpha: 0, xPercent: -18, duration: 0.32, ease: "power2.inOut" }, T2);
|
| 196 |
-
tl.fromTo("#s3", { autoAlpha: 0, xPercent: 18 }, { autoAlpha: 1, xPercent: 0, duration: 0.32, ease: "power2.inOut" }, T2);
|
| 197 |
-
|
| 198 |
-
// Blur crossfade (wind-down, testimonial→4)
|
| 199 |
-
const T3 = 15.65;
|
| 200 |
-
tl.to("#s3", { autoAlpha: 0, filter: "blur(14px)", scale: 1.04, duration: 0.45, ease: "sine.inOut" }, T3);
|
| 201 |
-
tl.fromTo("#s4a", { autoAlpha: 0 }, { autoAlpha: 1, duration: 0.25 }, T3 + 0.2);
|
| 202 |
-
|
| 203 |
-
// Hard cut (energy B-roll — disruption intentional)
|
| 204 |
-
// Just stagger data-start values with NO overlap (different tracks ok)
|
| 205 |
-
```
|
| 206 |
-
|
| 207 |
-
## GLASS CARD LOWER THIRD (robonuggets recipe)
|
| 208 |
-
|
| 209 |
-
```html
|
| 210 |
-
<div id="lt" class="clip" data-start="4.6" data-duration="2.9" data-track-index="8"
|
| 211 |
-
style="position:absolute;bottom:180px;left:0;padding:0 0 0 56px;">
|
| 212 |
-
<div style="background:rgba(10,10,25,0.85);backdrop-filter:blur(20px) saturate(150%);
|
| 213 |
-
border-left:6px solid #7C6FE0;border-radius:0 16px 16px 0;
|
| 214 |
-
padding:18px 32px;display:inline-block;">
|
| 215 |
-
<div style="font:700 40px/1.2 'Plus Jakarta Sans',sans-serif;color:#fff;">Purbasari Indonesia</div>
|
| 216 |
-
<div style="font:400 24px/1 'Plus Jakarta Sans',sans-serif;color:rgba(255,255,255,0.62);
|
| 217 |
-
margin-top:6px;">AI Training · 2026</div>
|
| 218 |
-
</div>
|
| 219 |
-
</div>
|
| 220 |
-
```
|
| 221 |
-
|
| 222 |
-
```javascript
|
| 223 |
-
// Slide in from left, auto-exit via next transition (NOT gsap.to exit)
|
| 224 |
-
tl.from("#lt", { x: -400, autoAlpha: 0, duration: 0.4, ease: "power3.out" }, 4.6);
|
| 225 |
-
// NO exit tween — lt will be covered by next scene transition
|
| 226 |
-
```
|
| 227 |
-
|
| 228 |
-
## COMPLETE CORRECT TEMPLATE
|
| 229 |
-
|
| 230 |
-
```html
|
| 231 |
-
<!DOCTYPE html>
|
| 232 |
-
<html><head><meta charset="utf-8">
|
| 233 |
-
<link href="[Google Fonts Space Grotesk + Plus Jakarta Sans]" rel="stylesheet">
|
| 234 |
-
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
| 235 |
-
<style>* {margin:0;padding:0;box-sizing:border-box} body {background:#0d0d1f;overflow:hidden}
|
| 236 |
-
#root {position:relative;width:1080px;height:1920px;overflow:hidden;background:#0d0d1f}
|
| 237 |
-
.sc {position:absolute;inset:0}
|
| 238 |
-
</style></head><body>
|
| 239 |
-
<div id="root"
|
| 240 |
-
data-composition-id="COMP_ID"
|
| 241 |
-
data-start="0"
|
| 242 |
-
data-duration="30"
|
| 243 |
-
data-width="1080"
|
| 244 |
-
data-height="1920">
|
| 245 |
-
|
| 246 |
-
<!-- Scene: timed wrapper -->
|
| 247 |
-
<div id="s1" class="clip sc" data-start="0" data-duration="4" data-track-index="0">
|
| 248 |
-
<!-- Video: NO muted for subject voice. Wrap in div. -->
|
| 249 |
-
<div style="position:absolute;inset:0">
|
| 250 |
-
<video data-start="0" data-duration="4" data-track-index="1"
|
| 251 |
-
data-volume="0.88" src="./assets/clip.MOV" playsinline
|
| 252 |
-
style="width:100%;height:100%;object-fit:cover;"></video>
|
| 253 |
-
</div>
|
| 254 |
-
</div>
|
| 255 |
-
|
| 256 |
-
<!-- Caption: class=clip mandatory -->
|
| 257 |
-
<div id="cap" class="clip" data-start="0.2" data-duration="3.5" data-track-index="2"
|
| 258 |
-
style="position:absolute;bottom:400px;left:0;right:0;padding:0 56px;
|
| 259 |
-
text-align:center;font:900 160px/1.0 'Space Grotesk',sans-serif;
|
| 260 |
-
color:#fff;text-shadow:0 6px 40px rgba(0,0,0,0.95);">Hook</div>
|
| 261 |
-
|
| 262 |
-
<!-- Music: instrumental, force_instrumental generated by ElevenLabs -->
|
| 263 |
-
<audio data-start="0" data-duration="30" data-track-index="50"
|
| 264 |
-
data-volume="0.10" src="./assets/music.mp3"></audio>
|
| 265 |
-
|
| 266 |
-
<script>
|
| 267 |
-
const tl = gsap.timeline({ paused: true });
|
| 268 |
-
|
| 269 |
-
// Entrance (varied eases, never same twice)
|
| 270 |
-
tl.from("#cap", { y: -100, autoAlpha: 0, duration: 0.22, ease: "expo.out" }, 0.2);
|
| 271 |
-
|
| 272 |
-
// CRITICAL: extend to prevent cutoff / black frames
|
| 273 |
-
tl.set({}, {}, 30); // use set not to()
|
| 274 |
-
|
| 275 |
-
// CRITICAL: key = data-composition-id exactly
|
| 276 |
-
window.__timelines = window.__timelines || {};
|
| 277 |
-
window.__timelines["COMP_ID"] = tl;
|
| 278 |
-
</script>
|
| 279 |
-
</div></body></html>
|
| 280 |
-
```
|
| 281 |
-
|
| 282 |
-
## RENDER COMMAND
|
| 283 |
-
|
| 284 |
-
```bash
|
| 285 |
-
PRODUCER_HEADLESS_SHELL_PATH=/usr/bin/chromium \
|
| 286 |
-
npx hyperframes render . -o ./renders/output.mp4 -w 1 --fps 30 --quality standard
|
| 287 |
-
```
|
| 288 |
-
|
| 289 |
-
## MUSIC GENERATION WATERFALL
|
| 290 |
-
|
| 291 |
-
1. **ElevenLabs** (`client.music.compose()`) — highest quality, requires API key
|
| 292 |
-
2. **YuE via HF Inference** — open source, requires HF_TOKEN, slower
|
| 293 |
-
3. **ffmpeg silence fallback** — always available, no music
|
| 294 |
-
|
| 295 |
-
Set `ELEVENLABS_API_KEY` in HF Space secrets for production quality music.
|
|
|
|
| 1 |
+
---
|
| 2 |
+
name: hyperframes-video
|
| 3 |
+
description: "Build production-ready HyperFrames HTML video compositions for testimonials, teasers, trailers, and community stories. Use when the user mentions 'hyperframes video,' 'testimonial video,' 'HyperFrames composition,' 'render MP4 from HTML,' 'kinetic caption video,' 'Dee Ferdinand video,' 'corporate training video,' 'event teaser video,' 'video with GSAP,' or wants to build any HTML-to-MP4 video. Also trigger when the user says 'build me a video for [client],' 'make a 30 second testimonial,' or 'create a social proof video.' Use this skill — do NOT build HyperFrames compositions ad hoc without it, as the rules are non-obvious and violations produce broken or silent renders."
|
| 4 |
+
metadata:
|
| 5 |
+
version: 2.0.0
|
| 6 |
+
author: Dee Ferdinand × Claude
|
| 7 |
+
updated: 2026-06-16
|
| 8 |
+
sources:
|
| 9 |
+
- nateherkai/hyperframes-student-kit
|
| 10 |
+
- heygen-com/hyperframes-launch-video
|
| 11 |
+
- robonuggets/hyperframes-helper
|
| 12 |
+
- coleam00/hyperframes-ai-video-generation
|
| 13 |
+
- elevenlabs/skills
|
| 14 |
+
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|