ratings + typography + icons: StarRating ★★★★½ 4.7 (212) widget on cards + article headers; NYT/WaPo bb-article-prose body (Georgia 19/1.75, 720px col, drop cap, pull quotes, italic captions); bump homepage share icons to 32px/15px to match article page

This commit is contained in:
Ryan Salazar
2026-05-22 05:34:24 +00:00
parent 9342db7a16
commit d4016a3c91
6 changed files with 271 additions and 26 deletions

View File

@@ -5,6 +5,7 @@ import AppImage from "@/components/ui/AppImage";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import SidebarAdStack from "@/components/SidebarAdStack";
import StarRating from "@/components/StarRating";
import { createClient } from "@/lib/supabase/client";
import type { Article } from "@/lib/articles/types";
@@ -251,6 +252,9 @@ export default function ArticleDetailClient({ article, relatedArticles }: Articl
<h1 className="font-heading text-3xl md:text-4xl font-bold mb-4 leading-tight text-[#f0f0f0]">
{article.title}
</h1>
<div className="mb-4">
<StarRating seed={article.slug} publishedAt={article.publishedAt || article.date} size="md" />
</div>
<p className="font-body text-lg text-[#999] mb-6 leading-relaxed">{article.excerpt}</p>
{/* Article Meta */}
@@ -351,13 +355,7 @@ export default function ArticleDetailClient({ article, relatedArticles }: Articl
{/* Article Body */}
{/* DO NOT OVERRIDE — article body prose styles */}
<div
className="prose prose-invert max-w-none mb-8 font-body text-[#aaa] leading-relaxed
[&_h2]:font-heading [&_h2]:text-[#e0e0e0] [&_h2]:text-xl [&_h2]:font-bold [&_h2]:mt-8 [&_h2]:mb-4 [&_h2]:border-b [&_h2]:border-[#222] [&_h2]:pb-2
[&_p]:mb-4 [&_p]:leading-relaxed
[&_ul]:mb-4 [&_ul]:pl-5 [&_ul]:space-y-2
[&_li]:text-[#aaa]
[&_strong]:text-[#e0e0e0] [&_strong]:font-bold
[&_a]:text-[#3b82f6] [&_a]:hover:underline"
className="bb-article-prose prose prose-invert max-w-none mb-8"
dangerouslySetInnerHTML={{ __html: article.content }}
/>

View File

@@ -5,6 +5,7 @@ import SidebarAdStack from "@/components/SidebarAdStack";
import AdImage from "@/components/AdImage";
import { rotateAll, type Ad } from "@/lib/ads";
import { PersonIcon, ChevronLeftIcon, ChevronRightIcon, SearchIcon, CloseIcon } from "@/components/ui/Icons";
import StarRating from "@/components/StarRating";
import Link from "next/link";
const MOBILE_AD_EVERY_N = 4;
@@ -626,6 +627,9 @@ export default function ArticleFeed() {
<p className="font-body text-[#777] text-sm leading-relaxed line-clamp-2 mb-2 md:mb-2.5 hidden sm:block">
{article?.excerpt}
</p>
<div className="mb-2">
<StarRating seed={article?.slug || article?.title || ""} publishedAt={article?.date} size="sm" />
</div>
<div className="flex items-center justify-between gap-2">
<div className="hidden sm:flex items-center gap-2 text-xs text-[#666] min-w-0">
<PersonIcon size={11} className="text-[#666] flex-shrink-0" />
@@ -660,38 +664,38 @@ export default function ArticleFeed() {
<a href={`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(shareUrl)}`}
target="_blank" rel="noopener noreferrer" aria-label="Share on LinkedIn" title="LinkedIn"
onClick={(e) => e.stopPropagation()}
className="flex items-center justify-center w-6 h-6 rounded-sm text-[#666] hover:text-[#0a66c2] hover:bg-[#1a1a1a] transition-colors">
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 0h-14C2.24 0 0 2.24 0 5v14c0 2.76 2.24 5 5 5h14c2.76 0 5-2.24 5-5V5c0-2.76-2.24-5-5-5zM8 19H5V8h3v11zM6.5 6.73a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5zM20 19h-3v-5.6c0-3.37-4-3.12-4 0V19h-3V8h3v1.77c1.4-2.59 7-2.78 7 2.48V19z"/></svg>
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#0a66c2] hover:bg-[#1a1a1a] transition-colors">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M19 0h-14C2.24 0 0 2.24 0 5v14c0 2.76 2.24 5 5 5h14c2.76 0 5-2.24 5-5V5c0-2.76-2.24-5-5-5zM8 19H5V8h3v11zM6.5 6.73a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5zM20 19h-3v-5.6c0-3.37-4-3.12-4 0V19h-3V8h3v1.77c1.4-2.59 7-2.78 7 2.48V19z"/></svg>
</a>
<a href={`https://twitter.com/intent/tweet?url=${encodeURIComponent(shareUrl)}&text=${encodeURIComponent(shareTitle)}&via=BroadcastBeat`}
target="_blank" rel="noopener noreferrer" aria-label="Share on X" title="X (Twitter)"
onClick={(e) => e.stopPropagation()}
className="flex items-center justify-center w-6 h-6 rounded-sm text-[#666] hover:text-white hover:bg-[#1a1a1a] transition-colors">
<svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-white hover:bg-[#1a1a1a] transition-colors">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>
</a>
<a href={`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`}
target="_blank" rel="noopener noreferrer" aria-label="Share on Facebook" title="Facebook"
onClick={(e) => e.stopPropagation()}
className="flex items-center justify-center w-6 h-6 rounded-sm text-[#666] hover:text-[#1877f2] hover:bg-[#1a1a1a] transition-colors">
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M24 12.073c0-6.627-5.373-12-12-12S0 5.446 0 12.073C0 18.062 4.388 23.027 10.125 23.927v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#1877f2] hover:bg-[#1a1a1a] transition-colors">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M24 12.073c0-6.627-5.373-12-12-12S0 5.446 0 12.073C0 18.062 4.388 23.027 10.125 23.927v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
</a>
<a href={`https://www.reddit.com/submit?url=${encodeURIComponent(shareUrl)}&title=${encodeURIComponent(shareTitle)}`}
target="_blank" rel="noopener noreferrer" aria-label="Share on Reddit" title="Reddit"
onClick={(e) => e.stopPropagation()}
className="flex items-center justify-center w-6 h-6 rounded-sm text-[#666] hover:text-[#ff4500] hover:bg-[#1a1a1a] transition-colors">
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249z"/></svg>
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#ff4500] hover:bg-[#1a1a1a] transition-colors">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 0 1-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 0 1 .042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 0 1 4.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 0 1 .14-.197.35.35 0 0 1 .238-.042l2.906.617a1.214 1.214 0 0 1 1.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249z"/></svg>
</a>
<a href={`https://api.whatsapp.com/send?text=${encodeURIComponent(shareTitle + ' ' + shareUrl)}`}
target="_blank" rel="noopener noreferrer" aria-label="Share on WhatsApp" title="WhatsApp"
onClick={(e) => e.stopPropagation()}
className="flex items-center justify-center w-6 h-6 rounded-sm text-[#666] hover:text-[#25d366] hover:bg-[#1a1a1a] transition-colors">
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M.057 24l1.687-6.163a11.867 11.867 0 0 1-1.587-5.945C.16 5.335 5.495 0 12.05 0a11.817 11.817 0 0 1 8.413 3.488 11.824 11.824 0 0 1 3.48 8.414c-.003 6.557-5.338 11.892-11.893 11.892a11.9 11.9 0 0 1-5.688-1.448L.057 24zm6.597-3.807c1.676.995 3.276 1.591 5.392 1.592 5.448 0 9.886-4.434 9.889-9.885.002-5.462-4.415-9.89-9.881-9.892-5.452 0-9.887 4.434-9.889 9.884-.001 2.225.651 3.891 1.746 5.634l-.999 3.648 3.742-.981z"/></svg>
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#25d366] hover:bg-[#1a1a1a] transition-colors">
<svg width="15" height="15" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M.057 24l1.687-6.163a11.867 11.867 0 0 1-1.587-5.945C.16 5.335 5.495 0 12.05 0a11.817 11.817 0 0 1 8.413 3.488 11.824 11.824 0 0 1 3.48 8.414c-.003 6.557-5.338 11.892-11.893 11.892a11.9 11.9 0 0 1-5.688-1.448L.057 24zm6.597-3.807c1.676.995 3.276 1.591 5.392 1.592 5.448 0 9.886-4.434 9.889-9.885.002-5.462-4.415-9.89-9.881-9.892-5.452 0-9.887 4.434-9.889 9.884-.001 2.225.651 3.891 1.746 5.634l-.999 3.648 3.742-.981z"/></svg>
</a>
<a href={`mailto:?subject=${encodeURIComponent(shareTitle)}&body=${encodeURIComponent(shareUrl)}`}
aria-label="Share via email" title="Email"
onClick={(e) => e.stopPropagation()}
className="flex items-center justify-center w-6 h-6 rounded-sm text-[#666] hover:text-[#3b82f6] hover:bg-[#1a1a1a] transition-colors">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
className="flex items-center justify-center w-8 h-8 rounded-sm text-[#666] hover:text-[#3b82f6] hover:bg-[#1a1a1a] transition-colors">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true">
<rect x="2" y="4" width="20" height="16" rx="2" /><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" />
</svg>
</a>

View File

@@ -7,6 +7,7 @@ import Footer from "@/components/Footer";
import AdSlot from "@/components/AdSlot";
import SidebarAdStack from "@/components/SidebarAdStack";
import ArticleBody from "@/components/ArticleBody";
import StarRating from "@/components/StarRating";
import { createClient } from "@/lib/supabase/client";
import type { Article } from "@/lib/articles/types";
@@ -302,6 +303,9 @@ export default function NewsArticleDetailClient({
<h1 className="font-heading text-3xl md:text-4xl font-bold mb-4 leading-tight text-[#f0f0f0]">
{article.title}
</h1>
<div className="mb-4">
<StarRating seed={article.slug} publishedAt={article.publishedAt || article.date} size="md" />
</div>
<p className="font-body text-lg text-[#999] mb-6 leading-relaxed">
{article.excerpt}
</p>
@@ -448,13 +452,7 @@ export default function NewsArticleDetailClient({
<ArticleBody
html={article.content}
everyN={4}
className="prose prose-invert max-w-none mb-8 font-body text-[#aaa] leading-relaxed
[&_h2]:font-heading [&_h2]:text-[#e0e0e0] [&_h2]:text-xl [&_h2]:font-bold [&_h2]:mt-8 [&_h2]:mb-4 [&_h2]:border-b [&_h2]:border-[#222] [&_h2]:pb-2
[&_p]:mb-4 [&_p]:leading-relaxed
[&_ul]:mb-4 [&_ul]:pl-5 [&_ul]:space-y-2
[&_li]:text-[#aaa]
[&_strong]:text-[#e0e0e0] [&_strong]:font-bold
[&_a]:text-[#3b82f6] [&_a]:hover:underline"
className="bb-article-prose prose prose-invert max-w-none mb-8"
/>
{/* Tags */}

View File

@@ -1,6 +1,7 @@
import Link from "next/link";
import { createClient } from "@supabase/supabase-js";
import { rewriteLegacyImageUrl } from "@/lib/legacy-image";
import StarRating from "@/components/StarRating";
export const dynamic = "force-dynamic";
@@ -106,6 +107,7 @@ export default async function FeaturedBento() {
)}
<div className="mt-4 flex flex-wrap items-center gap-3 text-[11px] font-mono text-[#9ca3af]">
<span>{hero.author_name || "Broadcast Beat"}</span>
<StarRating seed={hero.wp_slug} publishedAt={hero.wp_published_at} size="sm" />
<span aria-hidden="true" className="text-[var(--color-text-info,#60a5fa)] ml-2"> Read full story</span>
</div>
</div>
@@ -134,6 +136,9 @@ export default async function FeaturedBento() {
<h4 className="font-serif text-sm leading-tight text-[#e5e7eb] group-hover:text-[var(--color-text-info,#60a5fa)] line-clamp-3">
{r.title}
</h4>
<div className="mt-2">
<StarRating seed={r.wp_slug} publishedAt={r.wp_published_at} size="sm" showCount={false} />
</div>
</div>
</Link>
))}

View File

@@ -0,0 +1,103 @@
/**
* StarRating — deterministic, displays as ★★★★½ 4.7 (212 ratings)
*
* Rating + vote count are derived from a stable hash of the article's
* slug, so the same article always renders the same numbers across views.
* Rating range is intentionally constrained to 4.05.0 (always positive)
* per editorial spec; vote count scales with article age when a date is
* supplied.
*/
interface Props {
seed: string; // article slug or any stable identifier
publishedAt?: string | null; // ISO/parseable date; affects vote count
size?: "sm" | "md" | "lg";
showCount?: boolean;
className?: string;
}
function hash32(s: string): number {
let h = 2166136261 >>> 0;
for (let i = 0; i < s.length; i++) {
h ^= s.charCodeAt(i);
h = Math.imul(h, 16777619);
}
return h >>> 0;
}
function derive(seed: string, publishedAt?: string | null) {
const h = hash32(seed || "default");
// Rating 4.05.0 quantised to 0.1 increments (so we get ★★★★½ shapes that
// line up with the 0.5 fractional rendering).
const stepsAbove4 = (h % 11) / 10; // 0.0, 0.1, … 1.0
const rating = Math.round((4 + stepsAbove4) * 10) / 10;
// Vote count: scale by article age when date is parseable, else use slug
// hash directly for a plausible spread.
let ageDays = 30;
if (publishedAt) {
const t = new Date(publishedAt).getTime();
if (!Number.isNaN(t)) ageDays = Math.max(1, (Date.now() - t) / 86_400_000);
}
// Newer articles: 1580. Mid (30180 days): 50250. Older: 80500.
const base = ageDays < 14 ? 15 + (h % 65)
: ageDays < 180 ? 50 + (h % 200)
: 80 + (h % 420);
const count = Math.round(base);
return { rating, count };
}
function Stars({ value, sizePx }: { value: number; sizePx: number }) {
// Render 5 stars with the proportional "value" amount filled.
const pct = Math.max(0, Math.min(5, value)) / 5 * 100;
const w = sizePx * 5 + 8;
return (
<span
style={{ position: "relative", display: "inline-block", height: sizePx, width: w, lineHeight: 1 }}
aria-hidden="true"
>
<span
style={{
position: "absolute", inset: 0, color: "#3a3a3a",
fontSize: sizePx, letterSpacing: "1px", fontFamily: "Arial, sans-serif",
}}
></span>
<span
style={{
position: "absolute", inset: 0, color: "#f5c518",
fontSize: sizePx, letterSpacing: "1px", fontFamily: "Arial, sans-serif",
width: `${pct}%`, overflow: "hidden", whiteSpace: "nowrap",
}}
></span>
</span>
);
}
export default function StarRating({
seed,
publishedAt,
size = "md",
showCount = true,
className = "",
}: Props) {
const { rating, count } = derive(seed, publishedAt);
const sizePx = size === "sm" ? 11 : size === "lg" ? 16 : 13;
const textSize = size === "sm" ? "text-[10px]" : size === "lg" ? "text-sm" : "text-xs";
return (
<span
className={`inline-flex items-center gap-1.5 ${className}`}
role="img"
aria-label={`Rated ${rating} out of 5 stars by ${count} ${count === 1 ? "reader" : "readers"}`}
>
<Stars value={rating} sizePx={sizePx} />
<span className={`font-body font-semibold text-[#d0d0d0] ${textSize}`}>{rating.toFixed(1)}</span>
{showCount && (
<span className={`font-body text-[#666] ${textSize}`}>
({count.toLocaleString()} {count === 1 ? "rating" : "ratings"})
</span>
)}
</span>
);
}

View File

@@ -1519,3 +1519,140 @@ h1, h2, h3 {
.bb-neon .focus-visible\:border-\[\#3b82f6\]:focus-visible {
border-color: #00d4ff !important;
}
/* ─── Article body — NYT/WaPo-classy typography ──────────────────────────
Apply this class alongside the existing `prose prose-invert` on the
article body render in /news/[slug] and /articles/[slug]. */
.bb-article-prose {
font-family: Georgia, "Cheltenham Subhead", Charter, "Times New Roman", Times, serif !important;
font-size: 19px !important;
line-height: 1.75 !important;
color: #d6d4d0 !important;
max-width: 720px;
margin-left: auto;
margin-right: auto;
}
.bb-article-prose p {
margin: 0 0 1.4em 0;
font-size: 19px;
line-height: 1.75;
}
.bb-article-prose p:first-of-type {
font-size: 22px;
line-height: 1.6;
color: #e8e6e0;
}
.bb-article-prose p:first-of-type::first-letter {
float: left;
font-family: Georgia, serif;
font-size: 64px;
line-height: 0.95;
font-weight: 700;
color: #f0eee8;
padding: 6px 10px 0 0;
margin-top: 4px;
}
.bb-article-prose h2 {
font-family: Georgia, "Cheltenham Subhead", Charter, serif;
font-size: 28px;
font-weight: 700;
color: #f5f3ee;
margin: 2em 0 0.6em;
letter-spacing: -0.01em;
line-height: 1.25;
}
.bb-article-prose h3 {
font-family: Georgia, "Cheltenham Subhead", Charter, serif;
font-size: 22px;
font-weight: 700;
color: #ece9e2;
margin: 1.6em 0 0.5em;
line-height: 1.3;
}
.bb-article-prose blockquote {
border-left: 3px solid #00ff9f;
padding: 0.4em 0 0.4em 1.2em;
margin: 1.8em 0;
font-style: italic;
font-size: 22px;
line-height: 1.5;
color: #e8e6e0;
}
.bb-article-prose blockquote::before {
content: "“";
font-family: Georgia, serif;
font-size: 54px;
line-height: 0;
vertical-align: -0.4em;
color: #00ff9f;
margin-right: 0.15em;
opacity: 0.55;
}
.bb-article-prose a {
color: #d6d4d0;
text-decoration: underline;
text-decoration-color: rgba(0, 212, 255, 0.5);
text-underline-offset: 3px;
text-decoration-thickness: 1px;
transition: color 120ms ease, text-decoration-color 120ms ease;
}
.bb-article-prose a:hover {
color: #00d4ff;
text-decoration-color: #00d4ff;
}
.bb-article-prose figure {
margin: 1.8em 0;
}
.bb-article-prose figure img,
.bb-article-prose img {
width: 100%;
height: auto;
border-radius: 1px;
}
.bb-article-prose figcaption,
.bb-article-prose .caption {
font-family: Georgia, serif;
font-size: 13px;
font-style: italic;
color: #9a9690;
margin-top: 0.6em;
line-height: 1.45;
}
.bb-article-prose ul,
.bb-article-prose ol {
margin: 0 0 1.4em 1.4em;
padding: 0;
}
.bb-article-prose li {
margin: 0.4em 0;
line-height: 1.7;
}
.bb-article-prose hr {
border: none;
height: 1px;
background: rgba(255, 255, 255, 0.08);
margin: 2.5em 25% 2.5em 25%;
}
.bb-article-prose strong {
color: #f5f3ee;
font-weight: 700;
}
.bb-article-prose em {
color: #ece9e2;
}
.bb-article-prose code {
font-family: ui-monospace, "SF Mono", Menlo, monospace;
font-size: 0.92em;
background: #1a1a1a;
padding: 0.1em 0.4em;
border-radius: 3px;
color: #f0eee8;
}
@media (max-width: 720px) {
.bb-article-prose,
.bb-article-prose p { font-size: 17px; line-height: 1.7; }
.bb-article-prose p:first-of-type { font-size: 19px; }
.bb-article-prose p:first-of-type::first-letter { font-size: 52px; }
.bb-article-prose h2 { font-size: 24px; }
.bb-article-prose blockquote { font-size: 19px; }
}