From c8bd01a235bc118756b4ffc1664d7c11f47536b6 Mon Sep 17 00:00:00 2001 From: Prashaan Shakya Date: Fri, 3 Jul 2026 13:10:44 +0545 Subject: [PATCH] deep-dive-carousel skill: remove dependency on local RealRate project files Inline the CLAUDE.md design rules (cover-slide font/size table, name-wrap alignment CSS) directly into the skill instead of reading an external CLAUDE.md that only exists in the private project. Bundle the RealRate logo PNGs and a genericized generate_cover_gif.py template so the skill is fully self-contained for anyone who clones this repo. Co-Authored-By: Claude Sonnet 5 --- .../generate_cover_gif.py | 356 ++++++++++++++++++ RealRate Logos/RealRate_logo_horizontal.png | Bin 0 -> 9606 bytes RealRate Logos/RealRate_logo_vertical.png | Bin 0 -> 8881 bytes skills/deep-dive-carousel.md | 51 ++- 4 files changed, 396 insertions(+), 11 deletions(-) create mode 100644 Industry Deep Dive Carousel/generate_cover_gif.py create mode 100644 RealRate Logos/RealRate_logo_horizontal.png create mode 100644 RealRate Logos/RealRate_logo_vertical.png diff --git a/Industry Deep Dive Carousel/generate_cover_gif.py b/Industry Deep Dive Carousel/generate_cover_gif.py new file mode 100644 index 0000000..424aa64 --- /dev/null +++ b/Industry Deep Dive Carousel/generate_cover_gif.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python3 +""" +Template — Industry Deep Dive Carousel cover GIF (Top 5 revealing animation). +SCALE = 2 -> 1080x924 px (LinkedIn native portrait quality) + +Shared script for the deep-dive-carousel skill (Step 10). Lives at +Industry Deep Dive Carousel/generate_cover_gif.py — for each new industry, +edit the constants marked "EDIT" below in place, then run it. OUT_PATH +writes into that industry's own subfolder, so nothing here needs moving. +""" + +from PIL import Image, ImageDraw, ImageFont +import os + +# ── Resolution ──────────────────────────────────────────────────────────── +SCALE = 2 # 1 = 540x675 preview | 2 = 1080x1350 LinkedIn quality +W = 540 * SCALE + +def s(v): + """Scale a pixel / font-size value.""" + return int(v * SCALE) + +# Derive canvas height from content — no dead space at bottom +# stats bar: C_Y(258) + C_H(110) + gap(16) + bar_h(62) = 446 +# footer text y: 446 + 14 = 460, font height ~12, padding below 28 +H = s(220 + 110 + 16 + 62 + 14 + 12 + 28) # = s(462) = 924 px at SCALE 2 + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # Industry Deep Dive Carousel/ +# EDIT: point at the industry's own subfolder and output filename +OUT_PATH = os.path.join(BASE_DIR, "[Industry Name]", "[industry-slug]-[year]-cover.gif") +LOGO_PATH = os.path.join(BASE_DIR, "..", "RealRate Logos", "RealRate_logo_horizontal.png") + +# ── Palette ─────────────────────────────────────────────────────────────── EDIT +# Default RealRate blue palette — replace with the industry's chosen palette (Step 4 of the skill) +NAVY = (0, 24, 41) # cover bg dark #001829 +GOLD = (201, 168, 76) # accent #C9A84C +GOLD_L = (219, 191, 110) # accent light +BLUE_L = (61, 186, 205) # blue light #3DBACD +BLUE_D = (0, 103, 155) # blue dark #00679B +GREEN_L = (134, 204, 130) # #86CC82 (ECR score colour — keep green across all palettes) +WHITE = (255, 255, 255) +SILVER = (208, 208, 208) +BRONZE = (212, 149, 106) + +def blend(c, a, bg=NAVY): + """Pre-multiply alpha blend onto bg (a: 0-255).""" + t = a / 255.0 + return tuple(int(ci * t + bi * (1 - t)) for ci, bi in zip(c, bg)) + +# ── Company data ─────────────────────────────────────────────────────────── EDIT +# Replace with the industry's actual Top 5 (name, ECR, Top-Rated status) from the live rankings page +TOP5 = [ + {"rank": 1, "lines": ["Company One"], "ecr": "0%", "top_rated": True, + "rank_c": GOLD_L, "border_c": GOLD, "b_a": 140, "bg_a": 25}, + {"rank": 2, "lines": ["Company Two"], "ecr": "0%", "top_rated": True, + "rank_c": SILVER, "border_c": SILVER, "b_a": 115, "bg_a": 17}, + {"rank": 3, "lines": ["Company Three"], "ecr": "0%", "top_rated": True, + "rank_c": BRONZE, "border_c": BRONZE, "b_a": 115, "bg_a": 20}, + {"rank": 4, "lines": ["Company Four"], "ecr": "0%", "top_rated": True, + "rank_c": BLUE_L, "border_c": BLUE_L, "b_a": 76, "bg_a": 15}, + {"rank": 5, "lines": ["Company Five"], "ecr": "0%", "top_rated": True, + "rank_c": BLUE_L, "border_c": BLUE_L, "b_a": 76, "bg_a": 15}, +] + +# Reveal order: rank 5 first -> rank 1 last (indices into TOP5) +REVEAL_ORDER = [4, 3, 2, 1, 0] + +# ── Card geometry ───────────────────────────────────────────────────────── +C_PAD = s(20) +C_GAP = s(6) +C_W = (W - 2 * C_PAD - 4 * C_GAP) // 5 +C_Y = s(220) +C_H = s(110) + +def card_x(i): + return C_PAD + i * (C_W + C_GAP) + +# ── Font loader ─────────────────────────────────────────────────────────── +def load_font(bold=False, black=False, size=12): + if black: + cands = [r"C:\Windows\Fonts\ariblk.ttf", + r"C:\Windows\Fonts\segoeuib.ttf", + r"C:\Windows\Fonts\arialbd.ttf"] + elif bold: + cands = [r"C:\Windows\Fonts\segoeuib.ttf", + r"C:\Windows\Fonts\arialbd.ttf"] + else: + cands = [r"C:\Windows\Fonts\segoeui.ttf", + r"C:\Windows\Fonts\arial.ttf"] + for p in cands: + try: + return ImageFont.truetype(p, size) + except OSError: + pass + return ImageFont.load_default() + +fRank = load_font(True, False, s(7)) +fName = load_font(True, False, s(7)) +fEcr = load_font(True, False, s(13)) +fBadge = load_font(True, False, s(6)) +fLabel = load_font(True, False, s(8)) +fTitle = load_font(False, True, s(30)) +fYear = load_font(True, False, s(13)) +fStatN = load_font(True, False, s(26)) +fStatL = load_font(True, False, s(8)) +fFoot = load_font(False, False, s(8)) + +# ── Company logo loader ───────────────────────────────────────────────────── EDIT +# Replace with the [company-slug]-logo.png filenames sourced in Step 5, ranks 1–5 +_LOGO_FILES = { + 0: "company-one-logo.png", + 1: "company-two-logo.png", + 2: "company-three-logo.png", + 3: "company-four-logo.png", + 4: "company-five-logo.png", +} + +def _load_logo(filename): + """Load a PNG logo and convert all opaque pixels to white (CSS brightness(0) invert(1) equivalent).""" + path = os.path.join(BASE_DIR, "[Industry Name]", filename) # EDIT: match OUT_PATH's subfolder + try: + img = Image.open(path).convert("RGBA") + a = img.split()[3] # keep original alpha mask + white = Image.new("RGBA", img.size, (255, 255, 255, 255)) + white.putalpha(a) + return white + except Exception: + return None + +def _fit_logo(logo, max_w, max_h): + """Resize a logo RGBA image to fit inside max_w × max_h, preserving aspect ratio.""" + ratio = min(max_w / logo.width, max_h / logo.height) + nw = max(1, int(logo.width * ratio)) + nh = max(1, int(logo.height * ratio)) + return logo.resize((nw, nh), Image.LANCZOS) + +# Pre-load and pre-fit all logos once at startup — avoids per-frame LANCZOS resizes in stamp_card() +_LB_W = C_W - s(16) +_LB_H = s(22) +LOGOS = {i: _load_logo(f) for i, f in _LOGO_FILES.items()} +FITTED_LOGOS = {i: _fit_logo(logo, _LB_W, _LB_H) if logo is not None else None + for i, logo in LOGOS.items()} + +# ── Text helper ─────────────────────────────────────────────────────────── +def tc(draw, text, cx, y, fnt, fill): + """Draw text horizontally centred at cx, top at y.""" + bb = draw.textbbox((0, 0), text, font=fnt) + draw.text((cx - (bb[2] - bb[0]) // 2, y), text, font=fnt, fill=fill) + +# ── Build static base (background + UI, no company cards) ───────────────── +def make_base(): + img = Image.new("RGB", (W, H)) + d = ImageDraw.Draw(img) + + # Cover gradient top -> bottom — EDIT: match the industry's --cover-bg-start / --cover-bg-end (Step 4) + GRAD_START = (0, 24, 41) # #001829 + GRAD_END = (0, 45, 78) # #002d4e + for y in range(H): + t = y / H + r = int(GRAD_START[0] * (1 - t) + GRAD_END[0] * t) + g = int(GRAD_START[1] * (1 - t) + GRAD_END[1] * t) + b = int(GRAD_START[2] * (1 - t) + GRAD_END[2] * t) + d.line([(0, y), (W, y)], fill=(r, g, b)) + + # Very faint inner border + bm = s(12) + d.rectangle([bm, bm, W - bm - 1, H - bm - 1], outline=blend(WHITE, 18)) + + # Gold L-corner marks + M, ARM = s(12), s(24) + for (ax, dx) in [(M, 1), (W - M, -1)]: + for (ay, dy) in [(M, 1), (H - M, -1)]: + d.line([(ax, ay), (ax + dx * ARM, ay)], fill=GOLD, width=s(2)) + d.line([(ax, ay), (ax, ay + dy * ARM)], fill=GOLD, width=s(2)) + + # ── RealRate logo pill ─────────────────────────────────────────────── + pw, ph = s(160), s(40) + px = (W - pw) // 2 + py = s(22) + + logo_ok = False + try: + logo = Image.open(LOGO_PATH).convert("RGBA") + lh = s(24) + lw = int(logo.width * lh / logo.height) + logo = logo.resize((lw, lh), Image.LANCZOS) + lx = px + (pw - lw) // 2 + ly = py + (ph - lh) // 2 + img_rgba = img.convert("RGBA") + d2 = ImageDraw.Draw(img_rgba) + d2.rounded_rectangle([px, py, px + pw, py + ph], radius=s(6), fill=(255,255,255,255)) + overlay = Image.new("RGBA", (W, H), (0, 0, 0, 0)) + overlay.paste(logo, (lx, ly), logo) + img = Image.alpha_composite(img_rgba, overlay).convert("RGB") + logo_ok = True + except Exception: + pass + + d = ImageDraw.Draw(img) + if not logo_ok: + fL = load_font(True, False, s(15)) + tc(d, "RealRate", W // 2, py + s(11), fL, BLUE_D) + + # ── Title block ────────────────────────────────────────────────────── EDIT + tc(d, "FINANCIAL HEALTH REPORT", W // 2, s(76), fLabel, GOLD) + tc(d, "[INDUSTRY NAME]", W // 2, s(94), fTitle, WHITE) + + # Year bar: decorative lines + year number + ybar = s(150) + d.line([(s(40), ybar), (s(208), ybar)], fill=blend(BLUE_L, 128)) + d.line([(s(332), ybar), (s(500), ybar)], fill=blend(BLUE_L, 128)) + tc(d, "[YEAR]", W // 2, s(142), fYear, BLUE_L) + + # Gold accent rule + d.line([(W // 2 - s(20), s(155)), (W // 2 + s(20), s(155))], + fill=GOLD, width=s(2)) + + # Horizontal separator + d.line([(s(25), s(168)), (W - s(25), s(168))], fill=blend(WHITE, 32)) + + # Section label above cards + tc(d, "TOP 5 . FINANCIAL HEALTH RANKINGS", + W // 2, s(180), fLabel, blend(WHITE, 110)) + + # ── Stats bar ──────────────────────────────────────────────────────── + SY = C_Y + C_H + s(16) + SEY = SY + s(62) + d.line([(0, SY - 1), (W, SY - 1)], fill=blend(WHITE, 22)) + d.line([(0, SEY), (W, SEY)], fill=blend(WHITE, 22)) + + # EDIT: companies ranked · industry avg ECR · Top-Rated count (from Step 2 rankings pull) + for i, (num, lbl) in enumerate([("0", "COMPANIES"), ("0%", "AVG ECR"), ("0", "TOP-RATED")]): + cx_s = W // 6 + i * (W // 3) + if i > 0: + d.line([(cx_s - W // 6, SY), (cx_s - W // 6, SEY)], fill=blend(WHITE, 24)) + tc(d, num, cx_s, SY + s(8), fStatN, WHITE) + tc(d, lbl, cx_s, SY + s(42), fStatL, blend(WHITE, 97)) + + # Footer + tc(d, "Powered by RealRate . Using Explainable Financial AI", + W // 2, SEY + s(14), fFoot, blend(WHITE, 75)) + + return img + +# ── Stamp one company card onto an existing image ───────────────────────── +def stamp_card(base, idx, y_off=0): + co = TOP5[idx] + out = base.copy() + d = ImageDraw.Draw(out) + x = card_x(idx) + y = C_Y + y_off + cx = x + C_W // 2 + + bg_c = blend(co["border_c"], co["bg_a"]) + bdr_c = blend(co["border_c"], co["b_a"]) + + # Gold outer glow ring for rank 1 + if co["rank"] == 1: + d.rounded_rectangle([x - s(3), y - s(3), x + C_W + s(3), y + C_H + s(3)], + radius=s(10), outline=blend(GOLD, 80), width=s(3)) + + d.rounded_rectangle([x, y, x + C_W, y + C_H], + radius=s(7), fill=bg_c, outline=bdr_c, width=s(2)) + + # Rank label + tc(d, f"#{co['rank']}", cx, y + s(7), fRank, co["rank_c"]) + + # Company logo + lb_y = y + s(22) + fitted = FITTED_LOGOS.get(idx) + if fitted is not None: + lx = x + s(8) + (_LB_W - fitted.width) // 2 + ly = lb_y + (_LB_H - fitted.height) // 2 + out.paste(fitted, (lx, ly), fitted) + else: + d.rectangle([x + s(8), lb_y, x + C_W - s(8), lb_y + _LB_H], + fill=blend(WHITE, 15)) + + # Company name (two lines) + ny = y + s(54) + for line in co["lines"]: + tc(d, line, cx, ny, fName, blend(WHITE, 224)) + ny += s(10) + + # ECR score + tc(d, co["ecr"], cx, y + s(79), fEcr, GREEN_L) + + # "TOP-RATED" badge — only for top-rated companies + if co.get("top_rated", True): + bb = d.textbbox((0, 0), "TOP-RATED", font=fBadge) + bw = bb[2] - bb[0] + bx = cx - bw // 2 - s(4) + by = y + C_H - s(14) + d.rounded_rectangle([bx, by - s(2), bx + bw + s(8), by + s(9)], + radius=s(2), fill=blend(GREEN_L, 51)) + tc(d, "TOP-RATED", cx, by, fBadge, GREEN_L) + + return out + +# ── Assemble animation frames ───────────────────────────────────────────── +def build_frames(): + base = make_base() + frames = [] + durs = [] + visible = [] + + def render(new_idx=None, y_off=0): + img = base.copy() + for vi in visible: + img = stamp_card(img, vi) + if new_idx is not None: + img = stamp_card(img, new_idx, y_off) + return img + + # Intro: title + stats only + for _ in range(4): + frames.append(base.copy()) + durs.append(80) + + # Reveal each card: slide up then hold + for card_idx in REVEAL_ORDER: + for y_off in [s(45), s(25), s(8)]: + frames.append(render(card_idx, y_off)) + durs.append(60) + frames.append(render(card_idx, 0)) + durs.append(950 if card_idx == 0 else 480) + visible.append(card_idx) + + # Final hold: all 5 visible + frames.append(render()) + durs.append(2600) + + return frames, durs + +# ── Main ────────────────────────────────────────────────────────────────── +if __name__ == "__main__": + print(f"Canvas: {W}x{H} px (SCALE={SCALE})") + print("Building frames...") + frames, durs = build_frames() + print(f" {len(frames)} frames") + + print("Quantizing to 256 colours...") + pf = [f.quantize(colors=256, method=Image.Quantize.MEDIANCUT, dither=0) + for f in frames] + + print(f"Saving to: {OUT_PATH}") + pf[0].save( + OUT_PATH, + save_all=True, + append_images=pf[1:], + duration=durs, + loop=0, + ) + + kb = os.path.getsize(OUT_PATH) // 1024 + print(f"Done - {kb} KB ({len(frames)} frames)") diff --git a/RealRate Logos/RealRate_logo_horizontal.png b/RealRate Logos/RealRate_logo_horizontal.png new file mode 100644 index 0000000000000000000000000000000000000000..b143c554ac88cce5bc143c9260d86c576c225e98 GIT binary patch literal 9606 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 literal 0 HcmV?d00001 diff --git a/RealRate Logos/RealRate_logo_vertical.png b/RealRate Logos/RealRate_logo_vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..3750b5717150c9737dc23a982bdd801d80013654 GIT binary patch literal 8881 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 literal 0 HcmV?d00001 diff --git a/skills/deep-dive-carousel.md b/skills/deep-dive-carousel.md index 1eac2a3..a834096 100644 --- a/skills/deep-dive-carousel.md +++ b/skills/deep-dive-carousel.md @@ -2,7 +2,11 @@ Generate a complete Industry Deep Dive Carousel for a given industry and year. Produces all required output files: interactive HTML, print HTML, PDF, animated GIF, and LinkedIn post. -All paths are relative to the project root (the `RealRate/` folder). Run commands from that directory. +This skill is self-contained — everything needed to run it lives in this repo. All paths below are relative to the repo root. Run commands from that directory. + +**Bundled assets this skill depends on (already in this repo — do not skip or recreate):** +- `RealRate Logos/RealRate_logo_horizontal.png` — used in every HTML export and the cover GIF +- `Industry Deep Dive Carousel/generate_cover_gif.py` — master GIF template (Step 10) ## Usage @@ -16,13 +20,13 @@ Example: `/deep-dive-carousel "US Software" 2026` ## Step 0 — Read context files -Before doing anything else, read all of these from the project root: +Before doing anything else, read: -- `Industry Deep Dive Carousel/CLAUDE.md` -- `Industry Deep Dive Carousel/deep-dive-template.md` -- `context/brand-context.md` +- `context/brand-core.md` - `context/brand-voice.md` +All industry-carousel design rules (colours, logo handling, cover-slide sizing, ECR math, output checklist) are inlined in the steps below — there is no separate CLAUDE.md to fetch for this skill. + --- ## Step 1 — Verify the industry URL slug @@ -66,7 +70,7 @@ Extract for each company in the Top 10: ## Step 4 — Choose the industry colour scheme -Pick a colour palette distinct from all previously used schemes. Update `:root` CSS variables and the cover/Slide 7/Slide 10 gradients accordingly. The colour table in `Industry Deep Dive Carousel/CLAUDE.md` lists existing schemes — do not reuse them. +Pick a colour palette distinct from all previously used schemes. Update `:root` CSS variables and the cover/Slide 7/Slide 10 gradients accordingly. Check the `:root` variables in each existing `Industry Deep Dive Carousel/*/[industry]-*.html` file in this repo before choosing — never reuse a palette already in use. Example palette variables to set: ```css @@ -118,11 +122,11 @@ File: `Industry Deep Dive Carousel/[Industry Name]/[industry-slug]-[year].html` **Spec:** 540×675px interactive preview · 10 slides · slide navigation dots. -Follow all design rules from `Industry Deep Dive Carousel/CLAUDE.md`: +Slide-by-slide design rules: - **Cover (Slide 1):** Two rows of 5 cards (Top 10). Rank colours: gold/silver/bronze/#4–5 blue-light/#6–10 blue-light. Stats bar: companies · avg ECR · Top-Rated count. RealRate logo in white pill top-center. - **Slide 2 — Industry at a Glance:** Key stats, YoY ECR change, avg ECR bar fill proportional to ECR on a 600% scale. -- **Slide 3 — All Top-Rated:** Rows #1–#5 with driver chip; rows #6+ without. Compress rows if 9+ companies (`gap: 5px`, `padding: 8px 12px`). +- **Slide 3 — All Top-Rated:** Rows #1–#5 with driver chip; rows #6+ without. Compress rows if 9+ companies (`gap: 5px`, `padding: 8px 12px` interactive; `gap: 10px`, `padding: 16px 28px` print). - **Slide 4 — Biggest Mover:** Rank before/after, ECR, key driver with point contribution. - **Slide 5 — The Surprise:** Company, rank, ECR, market assumption vs. data reality, structural explanation. - **Slide 6 — Warning Signal:** Aggregate only — no company names. Red accent treatment. @@ -133,7 +137,32 @@ Follow all design rules from `Industry Deep Dive Carousel/CLAUDE.md`: Font: Manrope throughout. No emojis on any slide. Footer on every slide except Slide 9 (use `realrate.ai/methodology` instead). -RealRate logo path in interactive HTML: `../../RealRate Logos/RealRate_logo_horizontal.png` +RealRate logo path in interactive HTML: `../../RealRate Logos/RealRate_logo_horizontal.png` (resolves to the bundled `RealRate Logos/` folder at the repo root). + +### Cover slide font & size reference + +Both rows (Top 10) use identical card structure: rank · logo · name · ECR% · Top-Rated badge. `min-height` on the name element keeps ECR% and the badge pinned at the same row position whether the company name wraps to 1 or 2 lines: + +```css +/* Interactive */ +.co5-name, .co-rest-name { min-height: 17px; display: flex; align-items: center; justify-content: center; } +/* Print */ +.co5-name, .co-rest-name { min-height: 48px; display: flex; align-items: center; justify-content: center; } +``` + +| Element | Interactive (540px) | Print (1080px design) | +|---|---|---| +| Report label | 10px | 24px | +| Industry name | 40px | 88px | +| Year | 16px | 36px | +| Rank label | 10px | 20px | +| Logo area height | 32px | 68px | +| Logo max-height | 28px | 58px | +| Logo max-width | 88px | 176px | +| Company name | 7px · min-height 17px | 20px · min-height 48px | +| ECR score | 12px | 32px | +| Top-Rated badge | 6px | 12px | +| Stats number | 32px | 68px | --- @@ -159,7 +188,7 @@ body { width: 1080px; margin: 0; zoom: 1.11111; } Resolve the project root from the current working directory and embed logos immediately after writing the print HTML: ```powershell -$projectRoot = (Get-Location).Path # run from RealRate/ project root +$projectRoot = (Get-Location).Path # run from the repo root $rrLogoPath = Join-Path $projectRoot "RealRate Logos\RealRate_logo_horizontal.png" $rr = "data:image/png;base64," + [Convert]::ToBase64String([IO.File]::ReadAllBytes($rrLogoPath)) @@ -207,7 +236,7 @@ if (Test-Path "C:\Temp\rr_out.pdf") { ## Step 10 — Generate the animated cover GIF -Update `Industry Deep Dive Carousel/generate_cover_gif.py` with industry-specific constants: +This skill bundles `Industry Deep Dive Carousel/generate_cover_gif.py` as a reusable template — every constant marked `# EDIT` inside it defaults to a placeholder. Edit it in place for this industry, run it, then leave it edited (the next industry run edits it again): | Constant | Value | |---|---|