From 0ab7d29f783bc90bf6e499b4e809d9f71dccc307 Mon Sep 17 00:00:00 2001 From: DPN MW Date: Fri, 6 Mar 2026 15:53:26 -0400 Subject: [PATCH] UI Reworked --- .../javascripts/community_landing/landing.js | 37 +- .../stylesheets/community_landing/landing.css | 548 ++++++++++-------- config/locales/en.yml | 46 +- config/settings.yml | 106 ++-- plugin.rb | 251 ++++---- 5 files changed, 567 insertions(+), 421 deletions(-) diff --git a/assets/javascripts/community_landing/landing.js b/assets/javascripts/community_landing/landing.js index 72909d2..d5292b4 100644 --- a/assets/javascripts/community_landing/landing.js +++ b/assets/javascripts/community_landing/landing.js @@ -1,6 +1,6 @@ // ═══════════════════════════════════════════════════════════════════ -// Community Landing — JS -// Theme detection, scroll animations, stat counters +// Community Landing v2 — JS +// Theme detection, scroll animations, stat counters, horizontal scroll // ═══════════════════════════════════════════════════════════════════ (function () { "use strict"; @@ -96,7 +96,7 @@ { threshold: 0.1, rootMargin: "0px 0px -40px 0px" } ); - $$(".cl-reveal, .cl-reveal-stagger").forEach(function (el) { + $$(".cl-reveal").forEach(function (el) { revealObserver.observe(el); }); } @@ -139,7 +139,36 @@ } // ═══════════════════════════════════════════════════════════════════ - // 6. APP BADGE DETECTION + // 6. HORIZONTAL SCROLL — drag support for trending discussions + // ═══════════════════════════════════════════════════════════════════ + $$(".cl-topics__scroll").forEach(function (scroll) { + var isDown = false, startX, scrollLeft; + + scroll.addEventListener("mousedown", function (e) { + isDown = true; + scroll.style.cursor = "grabbing"; + startX = e.pageX - scroll.offsetLeft; + scrollLeft = scroll.scrollLeft; + }); + scroll.addEventListener("mouseleave", function () { + isDown = false; + scroll.style.cursor = ""; + }); + scroll.addEventListener("mouseup", function () { + isDown = false; + scroll.style.cursor = ""; + }); + scroll.addEventListener("mousemove", function (e) { + if (!isDown) return; + e.preventDefault(); + var x = e.pageX - scroll.offsetLeft; + var walk = (x - startX) * 1.5; + scroll.scrollLeft = scrollLeft - walk; + }); + }); + + // ═══════════════════════════════════════════════════════════════════ + // 7. APP BADGE DETECTION // ═══════════════════════════════════════════════════════════════════ var ua = navigator.userAgent || ""; if (/iPhone|iPad|iPod/.test(ua)) $$(".cl-app-badge--ios").forEach(function (e) { e.classList.add("highlighted"); }); diff --git a/assets/stylesheets/community_landing/landing.css b/assets/stylesheets/community_landing/landing.css index 93418d5..9e2a1d1 100644 --- a/assets/stylesheets/community_landing/landing.css +++ b/assets/stylesheets/community_landing/landing.css @@ -1,97 +1,96 @@ /* ═══════════════════════════════════════════════════════════════════ - Community Landing — Modern CSS + Community Landing v2 — Warm, modern full-width row design ═══════════════════════════════════════════════════════════════════ */ :root, [data-theme="dark"] { --cl-bg: #06060f; --cl-bg-elevated: #0d0d1a; --cl-card: rgba(15, 15, 35, 0.6); - --cl-accent: #7c6aff; - --cl-accent-hover: #9485ff; - --cl-accent-glow: rgba(124, 106, 255, 0.35); - --cl-accent-subtle: rgba(124, 106, 255, 0.08); + --cl-accent: #d4a24e; + --cl-accent-hover: #c4922e; + --cl-accent-glow: rgba(212, 162, 78, 0.35); + --cl-accent-subtle: rgba(212, 162, 78, 0.08); --cl-text: #b0b0ca; --cl-text-strong: #eeeef6; --cl-muted: #5e5e78; --cl-border: rgba(255, 255, 255, 0.05); - --cl-border-hover: rgba(124, 106, 255, 0.25); + --cl-border-hover: rgba(212, 162, 78, 0.25); --cl-hero-bg: #06060f; --cl-hero-text: #ffffff; --cl-footer-bg: #030308; --cl-shadow: rgba(0, 0, 0, 0.5); --cl-glass: rgba(15, 15, 35, 0.5); --cl-glass-border: rgba(255, 255, 255, 0.06); - --cl-orb-1: rgba(124, 106, 255, 0.12); + --cl-orb-1: rgba(212, 162, 78, 0.12); --cl-orb-2: rgba(99, 215, 255, 0.06); - --cl-orb-3: rgba(255, 106, 213, 0.04); - --cl-gradient-text: linear-gradient(135deg, #a78bfa, #7c6aff, #63d7ff); + --cl-gradient-text: linear-gradient(135deg, #e8c47a, #d4a24e, #e8c47a); --cl-section-alt: rgba(255, 255, 255, 0.015); --cl-scrolled-nav: rgba(6, 6, 15, 0.95); - --cl-success: #34d399; - --cl-error: #f87171; - --cl-radius: 12px; - --cl-radius-sm: 8px; + --cl-radius: 16px; + --cl-radius-sm: 10px; + --cl-radius-xs: 6px; + --cl-stat-icon-color: #d4a24e; + --cl-about-gradient: linear-gradient(135deg, #1a1820, #1c1a22); + --cl-app-gradient: linear-gradient(135deg, #d4a24e, #c4922e); color-scheme: dark; } [data-theme="light"] { - --cl-bg: #f8f9fc; + --cl-bg: #faf6f0; --cl-bg-elevated: #ffffff; - --cl-card: rgba(255, 255, 255, 0.7); - --cl-accent: #6c5ce7; - --cl-accent-hover: #5a4bd1; - --cl-accent-glow: rgba(108, 92, 231, 0.2); - --cl-accent-subtle: rgba(108, 92, 231, 0.06); - --cl-text: #4a4a6a; + --cl-card: rgba(255, 255, 255, 0.85); + --cl-accent: #d4a24e; + --cl-accent-hover: #c4922e; + --cl-accent-glow: rgba(212, 162, 78, 0.2); + --cl-accent-subtle: rgba(212, 162, 78, 0.06); + --cl-text: #5a5a6e; --cl-text-strong: #1a1a2e; --cl-muted: #8888a0; --cl-border: rgba(0, 0, 0, 0.06); - --cl-border-hover: rgba(108, 92, 231, 0.3); - --cl-hero-bg: #f0f0ff; + --cl-border-hover: rgba(212, 162, 78, 0.3); + --cl-hero-bg: #faf6f0; --cl-hero-text: #1a1a2e; - --cl-footer-bg: #eeeef5; + --cl-footer-bg: #f5f0e8; --cl-shadow: rgba(0, 0, 0, 0.06); --cl-glass: rgba(255, 255, 255, 0.6); --cl-glass-border: rgba(0, 0, 0, 0.05); - --cl-orb-1: rgba(108, 92, 231, 0.06); + --cl-orb-1: rgba(212, 162, 78, 0.08); --cl-orb-2: rgba(56, 189, 248, 0.04); - --cl-orb-3: rgba(236, 72, 153, 0.03); - --cl-gradient-text: linear-gradient(135deg, #6c5ce7, #4f46e5, #38bdf8); + --cl-gradient-text: linear-gradient(135deg, #d4a24e, #b8862e, #d4a24e); --cl-section-alt: rgba(0, 0, 0, 0.015); - --cl-scrolled-nav: rgba(248, 249, 252, 0.95); - --cl-success: #10b981; - --cl-error: #ef4444; + --cl-scrolled-nav: rgba(250, 246, 240, 0.95); + --cl-about-gradient: linear-gradient(135deg, #fdf6ec, #fef9f0); + --cl-app-gradient: linear-gradient(135deg, #d4a24e, #b8862e); color-scheme: light; } @media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) { - --cl-bg: #f8f9fc; + --cl-bg: #faf6f0; --cl-bg-elevated: #ffffff; - --cl-card: rgba(255, 255, 255, 0.7); - --cl-accent: #6c5ce7; - --cl-accent-hover: #5a4bd1; - --cl-accent-glow: rgba(108, 92, 231, 0.2); - --cl-accent-subtle: rgba(108, 92, 231, 0.06); - --cl-text: #4a4a6a; + --cl-card: rgba(255, 255, 255, 0.85); + --cl-accent: #d4a24e; + --cl-accent-hover: #c4922e; + --cl-accent-glow: rgba(212, 162, 78, 0.2); + --cl-accent-subtle: rgba(212, 162, 78, 0.06); + --cl-text: #5a5a6e; --cl-text-strong: #1a1a2e; --cl-muted: #8888a0; --cl-border: rgba(0, 0, 0, 0.06); - --cl-border-hover: rgba(108, 92, 231, 0.3); - --cl-hero-bg: #f0f0ff; + --cl-border-hover: rgba(212, 162, 78, 0.3); + --cl-hero-bg: #faf6f0; --cl-hero-text: #1a1a2e; - --cl-footer-bg: #eeeef5; + --cl-footer-bg: #f5f0e8; --cl-shadow: rgba(0, 0, 0, 0.06); --cl-glass: rgba(255, 255, 255, 0.6); --cl-glass-border: rgba(0, 0, 0, 0.05); - --cl-orb-1: rgba(108, 92, 231, 0.06); + --cl-orb-1: rgba(212, 162, 78, 0.08); --cl-orb-2: rgba(56, 189, 248, 0.04); - --cl-orb-3: rgba(236, 72, 153, 0.03); - --cl-gradient-text: linear-gradient(135deg, #6c5ce7, #4f46e5, #38bdf8); + --cl-gradient-text: linear-gradient(135deg, #d4a24e, #b8862e, #d4a24e); --cl-section-alt: rgba(0, 0, 0, 0.015); - --cl-scrolled-nav: rgba(248, 249, 252, 0.95); - --cl-success: #10b981; - --cl-error: #ef4444; + --cl-scrolled-nav: rgba(250, 246, 240, 0.95); + --cl-about-gradient: linear-gradient(135deg, #fdf6ec, #fef9f0); + --cl-app-gradient: linear-gradient(135deg, #d4a24e, #b8862e); color-scheme: light; } } @@ -109,12 +108,12 @@ } .cl-body *, .cl-body *::before, .cl-body *::after { box-sizing: border-box; } -.cl-container { max-width: 1140px; margin: 0 auto; padding: 0 1.25rem; } +.cl-container { max-width: 1200px; margin: 0 auto; padding: 0 1.25rem; } @media (min-width: 768px) { .cl-container { padding: 0 2rem; } } .cl-section-title { - font-size: 1.15rem; font-weight: 700; color: var(--cl-text-strong); - margin: 0 0 1rem; letter-spacing: -0.01em; + font-size: 1.25rem; font-weight: 700; color: var(--cl-text-strong); + margin: 0 0 1.25rem; letter-spacing: -0.01em; } /* ── Scroll Reveal ── */ @@ -130,14 +129,14 @@ font-size: 0.88rem; font-weight: 600; cursor: pointer; transition: all 0.2s ease; text-decoration: none; white-space: nowrap; } -.cl-btn--primary { background: var(--cl-accent); color: #fff; } +.cl-btn--primary { background: var(--cl-accent); color: #fff; border-radius: 50px; } .cl-btn--primary:hover { background: var(--cl-accent-hover); transform: translateY(-1px); box-shadow: 0 4px 20px var(--cl-accent-glow); } -.cl-btn--ghost { background: transparent; color: var(--cl-text-strong); border: 1px solid var(--cl-border); } +.cl-btn--ghost { background: transparent; color: var(--cl-text-strong); border: 1px solid var(--cl-border); border-radius: 50px; } .cl-btn--ghost:hover { background: var(--cl-accent-subtle); border-color: var(--cl-border-hover); } -.cl-btn--lg { padding: 0.8rem 2rem; font-size: 0.95rem; border-radius: var(--cl-radius); } +.cl-btn--lg { padding: 0.8rem 2rem; font-size: 0.95rem; } /* ═══════════════════════════════════════════════════════════════════ - NAVBAR + NAVBAR — minimal, theme toggle left, auth buttons right ═══════════════════════════════════════════════════════════════════ */ .cl-navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 1000; @@ -151,23 +150,14 @@ padding: 0.55rem 0; } .cl-navbar__inner { - max-width: 1140px; margin: 0 auto; padding: 0 1.25rem; + max-width: 1200px; margin: 0 auto; padding: 0 1.25rem; display: flex; align-items: center; justify-content: space-between; } @media (min-width: 768px) { .cl-navbar__inner { padding: 0 2rem; } } -.cl-navbar__brand { display: flex; align-items: center; gap: 0.6rem; text-decoration: none; color: var(--cl-text-strong); } -.cl-navbar__logo { width: auto; object-fit: contain; } -.cl-navbar__site-name { font-size: 1.05rem; font-weight: 700; letter-spacing: -0.02em; } - -/* Logo theme switching */ -.cl-logo--light { display: none; } -[data-theme="light"] .cl-logo--dark { display: none; } -[data-theme="light"] .cl-logo--light { display: inline; } -@media (prefers-color-scheme: light) { - :root:not([data-theme="dark"]) .cl-logo--dark { display: none; } - :root:not([data-theme="dark"]) .cl-logo--light { display: inline; } -} +.cl-navbar__left { display: flex; align-items: center; gap: 0.6rem; } +.cl-navbar__right { display: none; align-items: center; gap: 0.5rem; } +@media (min-width: 768px) { .cl-navbar__right { display: flex; } } .cl-navbar__hamburger { display: flex; flex-direction: column; gap: 4px; @@ -180,20 +170,17 @@ .cl-navbar__hamburger.active span:nth-child(3) { transform: rotate(-45deg) translate(4px, -4px); } @media (min-width: 768px) { .cl-navbar__hamburger { display: none; } } -.cl-navbar__links { +.cl-navbar__mobile-menu { display: none; position: fixed; inset: 0; background: var(--cl-bg); flex-direction: column; align-items: center; justify-content: center; gap: 1.25rem; z-index: 1000; } -.cl-navbar__links.open { display: flex; } -@media (min-width: 768px) { - .cl-navbar__links { display: flex; position: static; flex-direction: row; background: none; gap: 0.4rem; } -} +.cl-navbar__mobile-menu.open { display: flex; } .cl-navbar__link { - font-size: 0.85rem; padding: 0.45rem 1rem; + font-size: 0.85rem; padding: 0.5rem 1.25rem; text-decoration: none; color: var(--cl-text); - border-radius: var(--cl-radius-sm); transition: all 0.2s; font-weight: 500; + border-radius: 50px; transition: all 0.2s; font-weight: 500; } .cl-navbar__link:hover { color: var(--cl-text-strong); } .cl-navbar__link.cl-btn--primary { background: var(--cl-accent); color: #fff; } @@ -205,8 +192,8 @@ .cl-theme-toggle { background: none; border: 1px solid var(--cl-border); color: var(--cl-muted); cursor: pointer; padding: 0.35rem; - border-radius: var(--cl-radius-sm); display: flex; align-items: center; justify-content: center; - transition: all 0.2s; width: 34px; height: 34px; + border-radius: 50%; display: flex; align-items: center; justify-content: center; + transition: all 0.2s; width: 36px; height: 36px; } .cl-theme-toggle:hover { background: var(--cl-accent-subtle); border-color: var(--cl-border-hover); color: var(--cl-accent); } .cl-theme-toggle svg { width: 16px; height: 16px; } @@ -219,52 +206,43 @@ :root:not([data-theme]) .cl-theme-toggle .cl-icon-moon { display: none; } } +/* Logo theme switching */ +.cl-logo--light { display: none; } +[data-theme="light"] .cl-logo--dark { display: none; } +[data-theme="light"] .cl-logo--light { display: inline; } +@media (prefers-color-scheme: light) { + :root:not([data-theme="dark"]) .cl-logo--dark { display: none; } + :root:not([data-theme="dark"]) .cl-logo--light { display: inline; } +} + /* ═══════════════════════════════════════════════════════════════════ - HERO + HERO — warm, rounded container style ═══════════════════════════════════════════════════════════════════ */ .cl-hero { - min-height: 100vh; min-height: 100dvh; display: flex; align-items: center; - background: var(--cl-hero-bg); - position: relative; overflow: hidden; - padding: 5rem 0 2.5rem; -} -@media (min-width: 768px) { .cl-hero { padding: 6rem 0 3rem; } } -@media (min-width: 1024px) { .cl-hero { padding: 0; } } - -.cl-hero::before, .cl-hero::after { - content: ""; position: absolute; border-radius: 50%; - pointer-events: none; filter: blur(80px); -} -.cl-hero::before { width: 400px; height: 400px; top: -120px; right: -80px; background: var(--cl-orb-1); animation: cl-float 8s ease-in-out infinite; } -.cl-hero::after { width: 350px; height: 350px; bottom: -80px; left: -80px; background: var(--cl-orb-2); animation: cl-float 10s ease-in-out infinite reverse; } -@media (min-width: 768px) { - .cl-hero::before { width: 600px; height: 600px; } - .cl-hero::after { width: 500px; height: 500px; } -} -.cl-hero__orb { - position: absolute; width: 300px; height: 300px; - top: 50%; left: 50%; transform: translate(-50%, -50%); - background: var(--cl-orb-3); border-radius: 50%; - pointer-events: none; filter: blur(100px); - animation: cl-float 12s ease-in-out infinite 2s; -} - -@keyframes cl-float { - 0%, 100% { transform: translate(0, 0) scale(1); } - 33% { transform: translate(20px, -30px) scale(1.05); } - 66% { transform: translate(-15px, 15px) scale(0.95); } + padding: 6rem 1.25rem 2.5rem; + display: flex; align-items: center; justify-content: center; } +@media (min-width: 768px) { .cl-hero { padding: 6rem 2rem 3rem; } } .cl-hero__inner { - max-width: 1140px; margin: 0 auto; padding: 0 1.25rem; - width: 100%; display: flex; flex-direction: column; gap: 1.5rem; - position: relative; z-index: 1; + max-width: 1200px; width: 100%; margin: 0 auto; + background: var(--cl-card); + border: 1px solid var(--cl-border); + border-radius: 24px; + padding: 2.5rem 2rem; + display: flex; flex-direction: column; gap: 2rem; + position: relative; overflow: hidden; + box-shadow: 0 4px 40px var(--cl-shadow); +} +@media (min-width: 768px) { + .cl-hero__inner { padding: 3rem 3rem; } +} +@media (min-width: 1024px) { + .cl-hero__inner { flex-direction: row; align-items: center; gap: 3rem; padding: 3.5rem 3.5rem; } } -@media (min-width: 768px) { .cl-hero__inner { padding: 0 2rem; } } -@media (min-width: 1024px) { .cl-hero__inner { flex-direction: row; align-items: center; gap: 2.5rem; } } .cl-hero__content { flex-shrink: 0; animation: cl-fade-up 0.8s cubic-bezier(0.16,1,0.3,1) both; } -@media (min-width: 1024px) { .cl-hero__content { flex: 0 0 38%; } } +@media (min-width: 1024px) { .cl-hero__content { flex: 0 0 42%; } } .cl-hero__image { animation: cl-fade-up 0.8s cubic-bezier(0.16,1,0.3,1) 0.1s both; display: flex; align-items: center; justify-content: center; } @media (min-width: 1024px) { .cl-hero__image { flex: 1; min-height: 0; } } @@ -275,12 +253,8 @@ filter: drop-shadow(0 16px 48px var(--cl-shadow)); transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1); } -.cl-hero__image-img.loaded { - opacity: 1 !important; -} -@media (min-width: 1024px) { - .cl-hero__image-img { max-height: 80vh; } -} +.cl-hero__image-img.loaded { opacity: 1 !important; } +@media (min-width: 1024px) { .cl-hero__image-img { max-height: 440px; } } @keyframes cl-fade-up { from { opacity: 0; transform: translateY(20px); } @@ -288,9 +262,9 @@ } .cl-hero__title { - font-size: clamp(2rem, 5vw, 3.5rem); + font-size: clamp(2rem, 5vw, 3.2rem); font-weight: 800; color: var(--cl-hero-text); - margin: 0 0 1rem; line-height: 1.1; letter-spacing: -0.03em; + margin: 0 0 0.75rem; line-height: 1.1; letter-spacing: -0.03em; } .cl-hero__title-accent { background: var(--cl-gradient-text); @@ -298,210 +272,304 @@ -webkit-text-fill-color: transparent; } .cl-hero__subtitle { - font-size: clamp(0.92rem, 1.8vw, 1.15rem); + font-size: clamp(0.9rem, 1.8vw, 1.05rem); color: var(--cl-muted); margin: 0 0 1.5rem; max-width: 420px; line-height: 1.6; } .cl-hero__actions { display: flex; flex-wrap: wrap; gap: 0.6rem; } /* ═══════════════════════════════════════════════════════════════════ - STATS ROW — full-width counter cards below hero + PREMIUM STATS — full-width row with heading ═══════════════════════════════════════════════════════════════════ */ -.cl-stats-row { padding: 2.5rem 0; border-bottom: 1px solid var(--cl-border); } -.cl-stats-row__grid { +.cl-stats { padding: 2.5rem 0 2rem; } + +.cl-stats__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.75rem; } -@media (min-width: 640px) { .cl-stats-row__grid { grid-template-columns: repeat(3, 1fr); } } -@media (min-width: 1024px) { .cl-stats-row__grid { grid-template-columns: repeat(5, 1fr); } } +@media (min-width: 640px) { .cl-stats__grid { grid-template-columns: repeat(3, 1fr); } } +@media (min-width: 1024px) { .cl-stats__grid { grid-template-columns: repeat(5, 1fr); } } -.cl-stats-counter { - display: flex; flex-direction: column; align-items: center; gap: 0.35rem; - padding: 1.25rem 0.75rem; +.cl-stat-card { + display: flex; flex-direction: column; align-items: center; gap: 0.3rem; + padding: 1.5rem 1rem; background: var(--cl-card); border: 1px solid var(--cl-border); border-radius: var(--cl-radius); text-align: center; transition: all 0.25s ease; } -.cl-stats-counter:hover { border-color: var(--cl-border-hover); transform: translateY(-3px); box-shadow: 0 8px 24px rgba(0,0,0,0.1); } -.cl-stats-counter__icon { color: var(--cl-accent); opacity: 0.8; } -.cl-stats-counter__icon svg { width: 24px; height: 24px; } -.cl-stats-counter__value { - font-size: 1.5rem; font-weight: 800; - color: var(--cl-text-strong); letter-spacing: -0.02em; -} -.cl-stats-counter__label { - font-size: 0.7rem; color: var(--cl-muted); +.cl-stat-card:hover { border-color: var(--cl-border-hover); transform: translateY(-3px); box-shadow: 0 8px 24px rgba(0,0,0,0.08); } + +.cl-stat-card__icon { color: var(--cl-stat-icon-color); margin-bottom: 0.25rem; } +.cl-stat-card__icon svg { width: 26px; height: 26px; } + +.cl-stat-card__label { + font-size: 0.72rem; color: var(--cl-muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; + order: 1; +} +.cl-stat-card__value { + font-size: 1.75rem; font-weight: 800; + color: var(--cl-text-strong); letter-spacing: -0.02em; + order: 2; } /* ═══════════════════════════════════════════════════════════════════ - CONTENT GRID — two columns + ABOUT COMMUNITY — full-width gradient card ═══════════════════════════════════════════════════════════════════ */ -.cl-content { padding: 2.5rem 0 3rem; } +.cl-about { padding: 1rem 0 2rem; } -.cl-content__grid { - display: grid; grid-template-columns: 1fr; gap: 1.5rem; -} -@media (min-width: 1024px) { - .cl-content__grid { grid-template-columns: 1fr 1fr; gap: 1.5rem; } -} - -.cl-content__left, .cl-content__right { - display: flex; flex-direction: column; gap: 1.5rem; -} -.cl-content__bottom { grid-column: 1 / -1; } - -/* ═══════════════════════════════════════════════════════════════════ - ABOUT — Quote Card - ═══════════════════════════════════════════════════════════════════ */ .cl-about__card { - background: var(--cl-card); + background: var(--cl-about-gradient); border: 1px solid var(--cl-border); - border-radius: var(--cl-radius); - padding: 1.5rem; + border-radius: 20px; + padding: 2rem 2.5rem; position: relative; + box-shadow: 0 2px 16px rgba(0,0,0,0.04); } + +.cl-about__heading { + font-size: 1.35rem; font-weight: 800; color: var(--cl-text-strong); + margin: 0 0 1rem; +} + .cl-about__quote-mark { - color: var(--cl-accent); opacity: 0.3; + color: var(--cl-accent); opacity: 0.4; width: 28px; height: 28px; margin-bottom: 0.5rem; } + .cl-about__body { - color: var(--cl-text); font-size: 0.92rem; line-height: 1.7; - margin-bottom: 1rem; + color: var(--cl-text); font-size: 0.95rem; line-height: 1.75; + margin-bottom: 1rem; max-width: 800px; } .cl-about__body p { margin: 0 0 0.75rem; } .cl-about__body a { color: var(--cl-accent); } + .cl-about__meta { display: flex; align-items: center; gap: 0.75rem; padding-top: 1rem; border-top: 1px solid var(--cl-border); } .cl-about__avatar { - width: 40px; height: 40px; border-radius: 50%; object-fit: cover; + width: 44px; height: 44px; border-radius: 50%; object-fit: cover; border: 2px solid var(--cl-border); } .cl-about__meta-text { display: flex; flex-direction: column; } -.cl-about__author { font-size: 0.85rem; font-weight: 600; color: var(--cl-text-strong); } -.cl-about__role { font-size: 0.75rem; color: var(--cl-muted); } +.cl-about__author { font-size: 0.88rem; font-weight: 600; color: var(--cl-text-strong); } +.cl-about__role { font-size: 0.78rem; color: var(--cl-muted); } /* ═══════════════════════════════════════════════════════════════════ - TOPICS + TRENDING DISCUSSIONS — horizontal scrollable cards ═══════════════════════════════════════════════════════════════════ */ -.cl-topics__list { - display: flex; flex-direction: column; gap: 1px; - border-radius: var(--cl-radius); overflow: hidden; - border: 1px solid var(--cl-border); -} -.cl-topic-row { - display: flex; align-items: center; gap: 0.6rem; - padding: 0.7rem 1rem; background: var(--cl-card); - text-decoration: none; color: var(--cl-text); - transition: all 0.15s ease; - border-left: 2px solid transparent; -} -.cl-topic-row:hover { background: var(--cl-accent-subtle); border-left-color: var(--cl-accent); } -@media (max-width: 767px) { .cl-topic-row { flex-wrap: wrap; } } +.cl-topics { padding: 1.5rem 0 2rem; } -.cl-topic-row__cat { - display: inline-block; padding: 0.1rem 0.5rem; border-radius: 4px; - font-size: 0.62rem; font-weight: 700; color: #fff; - background: var(--cat-color); text-transform: uppercase; letter-spacing: 0.03em; - white-space: nowrap; flex-shrink: 0; +.cl-topics__scroll { + display: flex; gap: 0.75rem; + overflow-x: auto; overflow-y: hidden; + scroll-snap-type: x mandatory; + -webkit-overflow-scrolling: touch; + padding-bottom: 0.5rem; + scrollbar-width: none; } -.cl-topic-row__title { - flex: 1; font-weight: 600; color: var(--cl-text-strong); - overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.85rem; +.cl-topics__scroll::-webkit-scrollbar { display: none; } + +.cl-topic-card { + flex: 0 0 220px; + scroll-snap-align: start; + display: flex; flex-direction: column; gap: 0.5rem; + padding: 1rem 1.15rem; + background: var(--cl-card); + border: 1px solid var(--cl-border); + border-radius: var(--cl-radius); + text-decoration: none; color: var(--cl-text); + transition: all 0.2s ease; + min-height: 130px; } -@media (max-width: 767px) { .cl-topic-row__title { white-space: normal; width: 100%; order: 2; } } -.cl-topic-row__meta { - display: flex; align-items: center; gap: 0.2rem; flex-shrink: 0; +@media (min-width: 640px) { .cl-topic-card { flex: 0 0 240px; } } +.cl-topic-card:hover { border-color: var(--cl-border-hover); transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,0,0,0.08); } + +.cl-topic-card__cat { + display: inline-block; padding: 0.15rem 0.6rem; border-radius: 4px; + font-size: 0.6rem; font-weight: 700; color: #fff; + background: var(--cat-color); text-transform: uppercase; letter-spacing: 0.04em; + white-space: nowrap; align-self: flex-start; +} + +.cl-topic-card__title { + font-weight: 600; color: var(--cl-text-strong); + font-size: 0.85rem; line-height: 1.4; + display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; + overflow: hidden; flex: 1; +} + +.cl-topic-card__meta { + display: flex; align-items: center; gap: 0.75rem; + margin-top: auto; padding-top: 0.5rem; +} +.cl-topic-card__stat { + display: flex; align-items: center; gap: 0.2rem; color: var(--cl-muted); font-size: 0.75rem; } -.cl-topic-row__meta svg { opacity: 0.5; } +.cl-topic-card__stat svg { opacity: 0.6; } /* ═══════════════════════════════════════════════════════════════════ - CONTRIBUTORS — compact avatar grid, no post counts + TOP CREATORS — pill badges with avatar, @username, cheers ═══════════════════════════════════════════════════════════════════ */ -.cl-contributors__list { - display: flex; flex-wrap: wrap; gap: 0.5rem; +.cl-creators { padding: 1.5rem 0 2rem; } + +.cl-creators__list { + display: flex; flex-wrap: wrap; gap: 0.6rem; } -.cl-contributor { + +.cl-creator-pill { display: flex; align-items: center; gap: 0.5rem; - padding: 0.4rem 0.65rem 0.4rem 0.4rem; + padding: 0.35rem 0.85rem 0.35rem 0.35rem; background: var(--cl-card); border: 1px solid var(--cl-border); - border-radius: 50px; text-decoration: none; color: var(--cl-text-strong); - transition: all 0.2s ease; font-size: 0.8rem; font-weight: 500; + border-radius: 50px; text-decoration: none; + transition: all 0.2s ease; } -.cl-contributor:hover { border-color: var(--cl-border-hover); background: var(--cl-accent-subtle); } -.cl-contributor__avatar { - width: 28px; height: 28px; border-radius: 50%; object-fit: cover; +.cl-creator-pill:hover { border-color: var(--cl-border-hover); background: var(--cl-accent-subtle); } + +.cl-creator-pill__avatar { + width: 30px; height: 30px; border-radius: 50%; object-fit: cover; +} +.cl-creator-pill__name { + font-size: 0.8rem; font-weight: 600; color: var(--cl-text-strong); + white-space: nowrap; +} +.cl-creator-pill__cheers { + font-size: 0.7rem; color: var(--cl-muted); white-space: nowrap; } -.cl-contributor__name { white-space: nowrap; } /* ═══════════════════════════════════════════════════════════════════ - GROUPS — card grid + COMMUNITY SPACES — colored icon cards ═══════════════════════════════════════════════════════════════════ */ -.cl-groups__grid { - display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.6rem; -} -@media (min-width: 640px) { .cl-groups__grid { grid-template-columns: repeat(3, 1fr); } } -@media (min-width: 1024px) { .cl-groups__grid { grid-template-columns: repeat(5, 1fr); } } +.cl-spaces { padding: 1.5rem 0 2rem; } -.cl-group-card { - display: flex; flex-direction: column; align-items: center; gap: 0.4rem; - padding: 1.15rem 0.75rem; +.cl-spaces__grid { + display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.75rem; +} +@media (min-width: 480px) { .cl-spaces__grid { grid-template-columns: repeat(3, 1fr); } } +@media (min-width: 768px) { .cl-spaces__grid { grid-template-columns: repeat(4, 1fr); } } +@media (min-width: 1024px) { .cl-spaces__grid { grid-template-columns: repeat(5, 1fr); } } + +.cl-space-card { + display: flex; flex-direction: column; align-items: center; gap: 0.5rem; + padding: 1.25rem 0.75rem; background: var(--cl-card); border: 1px solid var(--cl-border); border-radius: var(--cl-radius); text-decoration: none; text-align: center; transition: all 0.25s ease; } -.cl-group-card:hover { border-color: var(--cl-border-hover); background: var(--cl-accent-subtle); transform: translateY(-3px); box-shadow: 0 8px 24px rgba(0,0,0,0.1); } -.cl-group-card__icon { width: 40px; height: 40px; } -.cl-group-card__icon img { width: 100%; height: 100%; border-radius: 10px; object-fit: cover; } -.cl-group-card__initial { +.cl-space-card:hover { border-color: var(--cl-border-hover); transform: translateY(-3px); box-shadow: 0 8px 24px rgba(0,0,0,0.08); } + +.cl-space-card__icon { + width: 56px; height: 56px; border-radius: 14px; display: flex; align-items: center; justify-content: center; - width: 40px; height: 40px; border-radius: 10px; - font-size: 1rem; font-weight: 700; color: #fff; + overflow: hidden; } -.cl-group-card__name { +.cl-space-card__icon img { width: 100%; height: 100%; object-fit: cover; } + +.cl-space-card__letter { + font-size: 1.4rem; font-weight: 800; color: #fff; + line-height: 1; +} + +.cl-space-card__name { font-size: 0.82rem; font-weight: 600; color: var(--cl-text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } -.cl-group-card__count { font-size: 0.7rem; color: var(--cl-muted); } +.cl-space-card__count { font-size: 0.7rem; color: var(--cl-muted); } /* ═══════════════════════════════════════════════════════════════════ - APP CTA + APP CTA — split layout: content left, image right ═══════════════════════════════════════════════════════════════════ */ -.cl-app-cta { - padding: 2.5rem 0; text-align: center; position: relative; overflow: hidden; - background: linear-gradient(135deg, var(--cl-accent), var(--cl-accent-hover)); +.cl-app-cta { padding: 2rem 0; } + +.cl-app-cta__inner { + background: var(--cl-app-gradient); + border-radius: 20px; + padding: 2.5rem 2.5rem; + display: flex; flex-direction: column; gap: 2rem; + position: relative; overflow: hidden; + min-height: 200px; } -.cl-app-cta::before { +@media (min-width: 768px) { + .cl-app-cta__inner { flex-direction: row; align-items: center; padding: 2.5rem 3rem; } +} + +.cl-app-cta__inner::before { content: ""; position: absolute; inset: 0; - background: radial-gradient(circle at 30% 50%, rgba(255,255,255,0.08) 0%, transparent 50%); + background: radial-gradient(circle at 30% 50%, rgba(255,255,255,0.1) 0%, transparent 50%); } -.cl-app-cta__headline { font-size: 1.3rem; font-weight: 700; color: #fff; margin: 0 0 0.35rem; position: relative; } -.cl-app-cta__subtext { color: rgba(255,255,255,0.7); font-size: 0.9rem; margin: 0 0 1.5rem; position: relative; } -.cl-app-cta__badges { display: flex; justify-content: center; gap: 0.75rem; flex-wrap: wrap; position: relative; } + +.cl-app-cta__content { position: relative; z-index: 1; flex: 1; } +.cl-app-cta__headline { + font-size: 1.5rem; font-weight: 700; color: #fff; + margin: 0 0 0.5rem; line-height: 1.3; +} +.cl-app-cta__subtext { + color: rgba(255,255,255,0.75); font-size: 0.9rem; + margin: 0 0 1.25rem; +} + +.cl-app-cta__badges { display: flex; gap: 0.75rem; flex-wrap: wrap; } + .cl-app-badge { - display: inline-flex; align-items: center; overflow: hidden; - transition: transform 0.2s; opacity: 0.9; + display: inline-flex; align-items: center; gap: 0.5rem; + padding: 0.6rem 1.2rem; + background: rgba(0,0,0,0.3); color: #fff; + text-decoration: none; + transition: all 0.2s; + font-size: 0.82rem; font-weight: 600; } -.cl-app-badge:hover { transform: translateY(-2px); opacity: 1; } -.cl-app-badge__img { height: 100%; width: auto; display: block; } -.cl-app-badge--rounded { border-radius: var(--cl-radius); } +.cl-app-badge:hover { background: rgba(0,0,0,0.5); transform: translateY(-2px); } +.cl-app-badge--rounded { border-radius: var(--cl-radius-sm); } .cl-app-badge--pill { border-radius: 50px; } .cl-app-badge--square { border-radius: 4px; } +.cl-app-badge__icon { display: flex; align-items: center; } +.cl-app-badge__icon svg { width: 20px; height: 20px; } +.cl-app-badge__label { white-space: nowrap; } + +.cl-app-cta__image { + position: relative; z-index: 1; + display: flex; align-items: flex-end; justify-content: center; +} +@media (min-width: 768px) { + .cl-app-cta__image { flex: 0 0 auto; margin-bottom: -2.5rem; } +} + +.cl-app-cta__img { + max-height: 280px; width: auto; object-fit: contain; + filter: drop-shadow(0 8px 32px rgba(0,0,0,0.2)); +} +@media (min-width: 768px) { + .cl-app-cta__img { max-height: 320px; } +} + /* ═══════════════════════════════════════════════════════════════════ - FOOTER — horizontal links, logo not stretched + FOOTER DESCRIPTION + ═══════════════════════════════════════════════════════════════════ */ +.cl-footer-desc { padding: 1.5rem 0; } +.cl-footer-desc__text { + color: var(--cl-muted); font-size: 0.88rem; line-height: 1.7; + max-width: 700px; +} + +/* ═══════════════════════════════════════════════════════════════════ + FOOTER — logo + links left, copyright right ═══════════════════════════════════════════════════════════════════ */ .cl-footer { - background: var(--cl-footer-bg); padding: 2rem 0 0; + background: var(--cl-footer-bg); padding: 1.5rem 0; border-top: 1px solid var(--cl-border); } -.cl-footer__top { + +.cl-footer__row { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 1rem; - padding-bottom: 1.25rem; border-bottom: 1px solid var(--cl-border); } + +.cl-footer__left { + display: flex; align-items: center; gap: 1.5rem; flex-wrap: wrap; +} + .cl-footer__brand { display: flex; align-items: center; gap: 0.5rem; } .cl-footer__logo { width: auto; object-fit: contain; } .cl-footer__site-name { font-size: 0.95rem; font-weight: 700; color: var(--cl-text-strong); } @@ -515,12 +583,13 @@ } .cl-footer__link:hover { color: var(--cl-accent); } -.cl-footer__text { color: var(--cl-muted); font-size: 0.82rem; padding: 1rem 0 0; } +.cl-footer__right { display: flex; align-items: center; } .cl-footer__copy { - padding: 1rem 0; font-size: 0.75rem; color: var(--cl-muted); - padding-bottom: max(1rem, env(safe-area-inset-bottom)); + font-size: 0.75rem; color: var(--cl-muted); } +.cl-footer__text { color: var(--cl-muted); font-size: 0.82rem; padding: 1rem 0 0; } + /* ═══════════════════════════════════════════════════════════════════ REDUCED MOTION ═══════════════════════════════════════════════════════════════════ */ @@ -530,5 +599,4 @@ transition-duration: 0.01ms !important; } .cl-reveal { opacity: 1; transform: none; } - .cl-hero::before, .cl-hero::after, .cl-hero__orb { animation: none; } } diff --git a/config/locales/en.yml b/config/locales/en.yml index cc539b7..64cd140 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -29,10 +29,28 @@ en: community_landing_hero_secondary_button_url: "Where the secondary button links to. Defaults to the login page." # ── Appearance: Color Scheme ── - community_landing_accent_color: "Primary accent color used for buttons, links, highlights, and gradients. Enter a hex value (e.g. #7c6aff)." - community_landing_accent_hover_color: "Accent color on hover states. Should be slightly lighter or brighter than the primary accent. Hex value (e.g. #9485ff)." + community_landing_accent_color: "Primary accent color used for buttons, links, highlights, and gradients. Enter a hex value (e.g. #d4a24e)." + community_landing_accent_hover_color: "Accent color on hover states. Should be slightly lighter or brighter than the primary accent. Hex value (e.g. #c4922e)." community_landing_dark_bg_color: "Background color for dark mode. Sets the overall page tone in dark theme. Hex value (e.g. #06060f)." - community_landing_light_bg_color: "Background color for light mode. Sets the overall page tone in light theme. Hex value (e.g. #f8f9fc)." + community_landing_light_bg_color: "Background color for light mode. Sets the overall page tone in light theme. Hex value (e.g. #faf6f0)." + + # ── Section: Stats ── + community_landing_stats_title: "Heading text displayed above the stats counter row. Set to 'Premium Stats' by default." + community_landing_stat_icon_color: "Color for the stat counter icons. Enter a hex value (e.g. #d4a24e). Applies to all stat icons uniformly." + community_landing_stat_members_label: "Custom label for the Members stat counter shown in the stats row below the hero." + community_landing_stat_topics_label: "Custom label for the Topics stat counter." + community_landing_stat_posts_label: "Custom label for the Posts stat counter." + community_landing_stat_likes_label: "Custom label for the Likes stat counter." + community_landing_stat_chats_label: "Custom label for the Chats stat counter. Shows total chat messages if the Chat plugin is enabled." + + # ── Section: About ── + community_landing_about_enabled: "Show the About section with a quote card describing your community." + community_landing_about_title: "Title heading displayed at the top of the About Community section." + community_landing_about_role: "Subtitle shown below the about author name (e.g. 'Community Manager', 'Founded 2020'). If blank, the site name is used." + community_landing_about_body: "Main body text for the About section. Supports basic HTML tags: p, a, strong, em, ul, li, br." + community_landing_about_image_url: "Small avatar image shown next to the about section author name. Works best with a square image." + community_landing_about_gradient_start: "Start color for the About section card gradient background. Hex value (e.g. #fdf6ec)." + community_landing_about_gradient_end: "End color for the About section card gradient background. Hex value (e.g. #fef9f0)." # ── Section: App Download Banner ── community_landing_show_app_ctas: "Show a mobile app download banner above the footer. Requires at least one app store URL to be set." @@ -44,35 +62,27 @@ en: community_landing_app_badge_style: "Border-radius style for app badges: rounded (12px corners), pill (fully rounded ends), or square (minimal rounding)." community_landing_app_cta_headline: "Bold headline text shown in the app download banner." community_landing_app_cta_subtext: "Supporting text shown below the app banner headline." - - # ── Section: About ── - community_landing_about_enabled: "Show the About section with a quote card describing your community." - community_landing_about_title: "Author name or title displayed below the about quote card. Typically the community name or admin name." - community_landing_about_role: "Subtitle shown below the about author name (e.g. 'Community Manager', 'Founded 2020'). If blank, the site name is used." - community_landing_about_body: "Main body text for the About section. Supports basic HTML tags: p, a, strong, em, ul, li, br." - community_landing_about_image_url: "Small avatar image shown next to the about section author name. Works best with a square image." - community_landing_stat_members_label: "Custom label for the Members stat counter shown in the stats row below the hero." - community_landing_stat_topics_label: "Custom label for the Topics stat counter." - community_landing_stat_posts_label: "Custom label for the Posts stat counter." - community_landing_stat_likes_label: "Custom label for the Likes stat counter." - community_landing_stat_chats_label: "Custom label for the Chats stat counter. Shows total chat messages if the Chat plugin is enabled." + community_landing_app_cta_gradient_start: "Start color for the app CTA section gradient background. Hex value (e.g. #d4a24e)." + community_landing_app_cta_gradient_end: "End color for the app CTA section gradient background. Hex value (e.g. #b8862e)." + community_landing_app_cta_image_url: "Image displayed on the right side of the app CTA banner. Supports PNG for transparent backgrounds. Shown overlapping above the gradient area." # ── Section: Top Contributors ── community_landing_contributors_enabled: "Show a section highlighting your most active community members with their avatars and usernames." - community_landing_contributors_title: "Heading text for the Top Contributors section." + community_landing_contributors_title: "Heading text for the Top Creators section." community_landing_contributors_days: "Lookback period in days for calculating top contributors. A larger number gives a broader view of active members." community_landing_contributors_count: "How many top contributors to display. Recommended: 6–12 for a balanced layout." # ── Section: Groups ── community_landing_groups_enabled: "Show public groups as browsable cards. Only groups with public visibility are displayed." - community_landing_groups_title: "Heading text for the Groups section." + community_landing_groups_title: "Heading text for the Community Spaces section." community_landing_groups_count: "Number of group cards to display (default 5). Only public, non-automatic groups are shown." # ── Section: Trending Discussions ── - community_landing_topics_enabled: "Show a list of trending discussions based on recent activity and reply count." + community_landing_topics_enabled: "Show a list of trending discussions as horizontally scrollable cards." community_landing_topics_title: "Heading text for the Trending Discussions section." community_landing_topics_count: "Number of trending topics to display. Shows the most-replied topics from the last 30 days." # ── Footer ── + community_landing_footer_description: "Description text displayed between the app CTA and footer navigation. Useful for a brief community description or tagline." community_landing_footer_text: "Optional text displayed in the footer area. Supports basic HTML: p, a, strong, em, ul, li, br." community_landing_footer_links: 'Footer navigation links as a JSON array. Format: [{"label":"Terms","url":"/tos"},{"label":"Privacy","url":"/privacy"}]' diff --git a/config/settings.yml b/config/settings.yml index dfa59d8..16d89c8 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -25,15 +25,15 @@ plugins: default: "Sign In" type: string community_landing_navbar_join_label: - default: "Join Free" + default: "Get Started" type: string # ── Hero Section: Content ── community_landing_hero_title: - default: "Welcome to Our Community" + default: "Welcome to Our Creative HeadQuarters" type: string community_landing_hero_subtitle: - default: "Join thousands of members in the conversation" + default: "Are you ready to start your creative journey?" type: string # ── Hero Section: Imagery ── @@ -51,13 +51,13 @@ plugins: # ── Hero Section: Call-to-Action Buttons ── community_landing_hero_primary_button_label: - default: "Browse the Forum" + default: "View Latest Topics" type: string community_landing_hero_primary_button_url: default: "/latest" type: string community_landing_hero_secondary_button_label: - default: "Join the Community" + default: "Explore Our Spaces" type: string community_landing_hero_secondary_button_url: default: "/login" @@ -65,16 +65,62 @@ plugins: # ── Appearance: Color Scheme ── community_landing_accent_color: - default: "#7c6aff" + default: "#d4a24e" type: string community_landing_accent_hover_color: - default: "#9485ff" + default: "#c4922e" type: string community_landing_dark_bg_color: default: "#06060f" type: string community_landing_light_bg_color: - default: "#f8f9fc" + default: "#faf6f0" + type: string + + # ── Section: Stats ── + community_landing_stats_title: + default: "Premium Stats" + type: string + community_landing_stat_icon_color: + default: "#d4a24e" + type: string + community_landing_stat_members_label: + default: "Members" + type: string + community_landing_stat_topics_label: + default: "Topics" + type: string + community_landing_stat_posts_label: + default: "Posts" + type: string + community_landing_stat_likes_label: + default: "Likes" + type: string + community_landing_stat_chats_label: + default: "Chats" + type: string + + # ── Section: About ── + community_landing_about_enabled: + default: true + type: bool + community_landing_about_title: + default: "About Community" + type: string + community_landing_about_role: + default: "" + type: string + community_landing_about_body: + default: "" + type: string + community_landing_about_image_url: + default: "" + type: string + community_landing_about_gradient_start: + default: "#fdf6ec" + type: string + community_landing_about_gradient_end: + default: "#fef9f0" type: string # ── Section: App Download Banner ── @@ -111,45 +157,22 @@ plugins: community_landing_app_cta_subtext: default: "Available free on iOS and Android" type: string - - # ── Section: About ── - community_landing_about_enabled: - default: true - type: bool - community_landing_about_title: - default: "About Our Community" + community_landing_app_cta_gradient_start: + default: "#d4a24e" type: string - community_landing_about_role: + community_landing_app_cta_gradient_end: + default: "#b8862e" + type: string + community_landing_app_cta_image_url: default: "" type: string - community_landing_about_body: - default: "" - type: string - community_landing_about_image_url: - default: "" - type: string - community_landing_stat_members_label: - default: "Members" - type: string - community_landing_stat_topics_label: - default: "Topics" - type: string - community_landing_stat_posts_label: - default: "Posts" - type: string - community_landing_stat_likes_label: - default: "Likes" - type: string - community_landing_stat_chats_label: - default: "Chats" - type: string # ── Section: Top Contributors ── community_landing_contributors_enabled: default: true type: bool community_landing_contributors_title: - default: "Top Contributors" + default: "Top Creators" type: string community_landing_contributors_days: default: 90 @@ -163,7 +186,7 @@ plugins: default: true type: bool community_landing_groups_title: - default: "Our Groups" + default: "Community Spaces" type: string community_landing_groups_count: default: 5 @@ -181,9 +204,12 @@ plugins: type: integer # ── Footer ── + community_landing_footer_description: + default: "" + type: string community_landing_footer_text: default: "" type: string community_landing_footer_links: - default: '[{"label":"Terms","url":"/tos"},{"label":"Privacy","url":"/privacy"}]' + default: '[{"label":"Brand","url":"/about"},{"label":"Links","url":"/links"},{"label":"Terms","url":"/tos"},{"label":"Privacy","url":"/privacy"}]' type: string diff --git a/plugin.rb b/plugin.rb index 926c424..287d173 100644 --- a/plugin.rb +++ b/plugin.rb @@ -2,7 +2,7 @@ # name: community-landing # about: Branded public landing page for unauthenticated visitors -# version: 1.0.0 +# version: 2.0.0 # authors: Community # url: https://github.com/community/community-landing @@ -100,6 +100,8 @@ after_initialize do ERB::Util.html_escape(text.to_s) end + # ── SVG Icons ── + SUN_SVG = '' MOON_SVG = '' QUOTE_SVG = '' @@ -110,6 +112,12 @@ after_initialize do STAT_LIKES_SVG = '' STAT_CHATS_SVG = '' + COMMENT_SVG = '' + HEART_SVG = '' + + IOS_BADGE_SVG = '' + ANDROID_BADGE_SVG = '' + def hex_to_rgb(hex) hex = hex.to_s.gsub("#", "") return "0, 0, 0" unless hex.match?(/\A[0-9a-fA-F]{6}\z/) @@ -117,10 +125,15 @@ after_initialize do end def build_color_overrides(s) - accent = s.community_landing_accent_color.presence || "#7c6aff" - accent_hover = s.community_landing_accent_hover_color.presence || "#9485ff" + accent = s.community_landing_accent_color.presence || "#d4a24e" + accent_hover = s.community_landing_accent_hover_color.presence || "#c4922e" dark_bg = s.community_landing_dark_bg_color.presence || "#06060f" - light_bg = s.community_landing_light_bg_color.presence || "#f8f9fc" + light_bg = s.community_landing_light_bg_color.presence || "#faf6f0" + stat_icon_color = s.community_landing_stat_icon_color.presence || accent + about_grad_start = s.community_landing_about_gradient_start.presence || "#fdf6ec" + about_grad_end = s.community_landing_about_gradient_end.presence || "#fef9f0" + app_grad_start = s.community_landing_app_cta_gradient_start.presence || accent + app_grad_end = s.community_landing_app_cta_gradient_end.presence || accent_hover accent_rgb = hex_to_rgb(accent) "\n" @@ -179,20 +201,16 @@ after_initialize do # ── App badge helper ── - def render_app_badge(store_url, custom_icon_url, default_svg, badge_h, badge_style) + def render_app_badge(store_url, icon_svg, label, badge_h, badge_style) style_class = case badge_style when "pill" then "cl-app-badge--pill" when "square" then "cl-app-badge--square" else "cl-app-badge--rounded" end - html = "" - if custom_icon_url - html << "\"\"" - else - html << default_svg - end - html << "\n" - html + "" \ + "#{icon_svg}" \ + "#{e(label)}" \ + "\n" end def build_html(css, js) @@ -203,7 +221,6 @@ after_initialize do # Logo URLs logo_dark_url = s.community_landing_logo_dark_url.presence logo_light_url = s.community_landing_logo_light_url.presence - # Fallback: if only light is set, treat it as the universal logo if logo_dark_url.nil? && logo_light_url.nil? fallback = s.respond_to?(:logo_url) ? s.logo_url.presence : nil logo_dark_url = fallback @@ -212,7 +229,6 @@ after_initialize do logo_h = s.community_landing_logo_height rescue 30 og_logo = logo_dark_url || logo_light_url - # Footer logo footer_logo_url = s.community_landing_footer_logo_url.presence html = +"" @@ -232,33 +248,27 @@ after_initialize do html << build_color_overrides(s) html << "\n\n" - # Navbar labels signin_label = s.community_landing_navbar_signin_label.presence || "Sign In" - join_label = s.community_landing_navbar_join_label.presence || "Join Free" + join_label = s.community_landing_navbar_join_label.presence || "Get Started" # ── NAVBAR ── html << "\n" + html << "" + html << "\n" + html << "
\n" + html << "#{e(signin_label)}\n" + html << "#{e(join_label)}\n" + html << "
" + html << "\n" - # ── HERO — text left, image right ── - hero_style = "" - if s.community_landing_hero_background_image_url.present? - hero_style = " style=\"background-image: linear-gradient(rgba(6,6,15,0.8), rgba(6,6,15,0.8)), url('#{s.community_landing_hero_background_image_url}');\"" - end - html << "
\n" - html << "
\n" + # ── HERO ── + html << "
\n" html << "
\n" html << "
\n" @@ -271,18 +281,18 @@ after_initialize do html << "

#{e(s.community_landing_hero_subtitle)}

\n" - primary_label = s.community_landing_hero_primary_button_label.presence || "Browse the Forum" + primary_label = s.community_landing_hero_primary_button_label.presence || "View Latest Topics" primary_url = s.community_landing_hero_primary_button_url.presence || "/latest" - secondary_label = s.community_landing_hero_secondary_button_label.presence || "Join the Community" + secondary_label = s.community_landing_hero_secondary_button_label.presence || "Explore Our Spaces" secondary_url = s.community_landing_hero_secondary_button_url.presence || login_url html << "
\n" html << "#{e(primary_label)}\n" html << "#{e(secondary_label)}\n" html << "
\n" + html << "
\n" - html << "
\n" # end cl-hero__content - + # Hero image hero_image_urls_raw = s.community_landing_hero_image_urls.presence if hero_image_urls_raw urls = hero_image_urls_raw.split("|").map(&:strip).reject(&:empty?).first(5) @@ -294,11 +304,13 @@ after_initialize do end end - html << "
\n" # end hero + html << "
\n" - # ── STATS ROW — full-width counter cards ── - html << "
\n" - html << "
\n" + # ── PREMIUM STATS ROW ── + stats_title = s.community_landing_stats_title.presence || "Premium Stats" + html << "
\n" + html << "

#{e(stats_title)}

\n" + html << "
\n" html << stats_counter_card(STAT_MEMBERS_SVG, @stats[:members], s.community_landing_stat_members_label) html << stats_counter_card(STAT_TOPICS_SVG, @stats[:topics], s.community_landing_stat_topics_label) html << stats_counter_card(STAT_POSTS_SVG, @stats[:posts], s.community_landing_stat_posts_label) @@ -306,20 +318,14 @@ after_initialize do html << stats_counter_card(STAT_CHATS_SVG, @stats[:chats], s.community_landing_stat_chats_label) html << "
\n
\n" - # ── TWO-COLUMN CONTENT AREA ── - html << "
\n" - html << "
\n" - - # ── LEFT COLUMN — About + Contributors ── - html << "
\n" - - # About — quote card + # ── ABOUT COMMUNITY — full-width card ── if s.community_landing_about_enabled about_body = s.community_landing_about_body.presence || "" about_image = s.community_landing_about_image_url.presence about_role = s.community_landing_about_role.presence || site_name - html << "
\n" + html << "
\n" html << "
\n" + html << "

#{e(s.community_landing_about_title)}

\n" html << QUOTE_SVG if about_body.present? html << "
#{about_body}
\n" @@ -331,108 +337,112 @@ after_initialize do html << "
\n" html << "#{e(s.community_landing_about_title)}\n" html << "#{e(about_role)}\n" - html << "
\n" - html << "
\n" + html << "
\n" + html << "
\n" + html << "
\n" end - # Top Contributors - if s.community_landing_contributors_enabled && @top_contributors&.any? - html << "
\n" - html << "

#{e(s.community_landing_contributors_title)}

\n" - html << "\n
\n" end - html << "\n" # end left - - # ── RIGHT COLUMN — Trending Discussions ── - html << "
\n" - - # Trending Discussions - if s.community_landing_topics_enabled && @hot_topics&.any? - html << "
\n" - html << "

#{e(s.community_landing_topics_title)}

\n" - html << "
\n" - @hot_topics.each do |topic| - html << "\n" - if topic.category - html << "#{e(topic.category.name)}\n" - end - html << "#{e(topic.title)}\n" - html << "" - html << "" - html << " #{topic.posts_count}" - html << "\n" + # ── TOP CREATORS — pill badges ── + if s.community_landing_contributors_enabled && @top_contributors&.any? + html << "
\n" + html << "

#{e(s.community_landing_contributors_title)}

\n" + html << "
\n" + @top_contributors.each do |user| + avatar_url = user.avatar_template.gsub("{size}", "120") + cheers = user.respond_to?(:post_count) ? user.post_count : 0 + html << "\n" + html << "\"#{e(user.username)}\"\n" + html << "@#{e(user.username)}\n" + html << "#{cheers} cheers\n" + html << "\n" end - html << "
\n
\n" + html << "
\n
\n" end - html << "\n" # end right - - # ── BOTTOM ROW — Groups (full-width) ── + # ── COMMUNITY SPACES — colored cards ── if s.community_landing_groups_enabled && @groups&.any? - html << "
\n" - html << "
\n" + html << "
\n" html << "

#{e(s.community_landing_groups_title)}

\n" - html << "
\n" - html << "
\n" + html << "\n\n" end - html << "\n" # end content grid - - # ── APP CTA (above footer) ── + # ── APP CTA — split layout with image ── if s.community_landing_show_app_ctas && (s.community_landing_ios_app_url.present? || s.community_landing_android_app_url.present?) badge_h = s.community_landing_app_badge_height rescue 45 badge_style = s.community_landing_app_badge_style rescue "rounded" - ios_icon = s.community_landing_ios_app_icon_url.presence - android_icon = s.community_landing_android_app_icon_url.presence - - ios_w = (badge_h * 3.0).to_i - android_w = (badge_h * 3.375).to_i - - ios_default_svg = "Download on theApp Store" - android_default_svg = "GET IT ONGoogle Play" + app_image = s.community_landing_app_cta_image_url.presence html << "
\n" + html << "
\n" + html << "
\n" html << "

#{e(s.community_landing_app_cta_headline)}

\n" - html << "

#{e(s.community_landing_app_cta_subtext)}

\n" + html << "

#{e(s.community_landing_app_cta_subtext)}

\n" if s.community_landing_app_cta_subtext.present? html << "
\n" if s.community_landing_ios_app_url.present? - html << render_app_badge(s.community_landing_ios_app_url, ios_icon, ios_default_svg, badge_h, badge_style) + html << render_app_badge(s.community_landing_ios_app_url, IOS_BADGE_SVG, "App Store", badge_h, badge_style) end if s.community_landing_android_app_url.present? - html << render_app_badge(s.community_landing_android_app_url, android_icon, android_default_svg, badge_h, badge_style) + html << render_app_badge(s.community_landing_android_app_url, ANDROID_BADGE_SVG, "Google Play", badge_h, badge_style) end - html << "
\n" + html << "\n" + html << "\n" + if app_image + html << "
\n" + html << "\"App\n" + html << "
\n" + end + html << "\n" + html << "\n" + end + + # ── FOOTER DESCRIPTION ── + if s.community_landing_footer_description.present? + html << "\n" end # ── FOOTER ── html << "\n" html << "\n" @@ -462,10 +475,10 @@ after_initialize do end def stats_counter_card(icon_svg, count, label) - "
\n" \ - "
#{icon_svg}
\n" \ - "0\n" \ - "#{e(label)}\n" \ + "
\n" \ + "
#{icon_svg}
\n" \ + "#{e(label)}\n" \ + "0\n" \ "
\n" end