diff --git a/frontend/src/routes/art/+page.svelte b/frontend/src/routes/art/+page.svelte
index 3407264..5f00f82 100644
--- a/frontend/src/routes/art/+page.svelte
+++ b/frontend/src/routes/art/+page.svelte
@@ -9,6 +9,7 @@
{ id: 'mahogany', label: 'Mahogany' },
{ id: 'gold', label: 'Gold' },
{ id: 'silver', label: 'Silver' },
+ { id: 'black', label: 'Black' },
{ id: 'none', label: 'No frame' },
];
@@ -16,7 +17,7 @@
let state = $state('loading'); // loading | ready | empty
let zoom = $state(false);
let frame = $state('walnut');
- let thickness = $state(1); // frame-scale multiplier, 0.7–1.5
+ let thickness = $state(1); // frame-scale multiplier, 0.7–1.9 (mat caps at 1.5)
let who = $derived(
art ? [art.artist || 'Unknown artist', art.date_text].filter(Boolean) : []
@@ -27,7 +28,7 @@
const saved = localStorage.getItem('ub_art_frame');
if (saved && FRAMES.some((f) => f.id === saved)) frame = saved;
const t = parseFloat(localStorage.getItem('ub_art_thickness'));
- if (t >= 0.7 && t <= 1.5) thickness = t;
+ if (t >= 0.7 && t <= 1.9) thickness = t;
} catch { /* private mode — defaults are fine */ }
try {
art = await getJSON('/api/art/today');
@@ -117,7 +118,7 @@
{#if frame !== 'none'}
Thickness
-
{/if}
@@ -204,8 +205,10 @@
switching frames never reflows the page. */
.frame {
box-sizing: border-box;
+ /* Rail scales across the whole slider; the mat caps at scale 1.5 so the top of the
+ slider thickens only the moulding (wood), holding the white border steady. */
--rail: calc(clamp(11px, 2.3vw, 22px) * var(--frame-scale, 1));
- --mat: calc(clamp(10px, 2.4vw, 22px) * var(--frame-scale, 1));
+ --mat: calc(clamp(10px, 2.4vw, 22px) * min(var(--frame-scale, 1), 1.5));
border: none; cursor: zoom-in; background: none;
position: relative; line-height: 0; display: inline-block; max-width: 100%;
border-radius: 4px; padding: var(--rail);
@@ -241,6 +244,13 @@
inset 0 2px 3px rgba(255, 255, 255, 0.75),
inset 0 -3px 9px rgba(60, 70, 85, 0.42);
}
+ .frame--black {
+ background: linear-gradient(135deg, #2a2c30, #15171a 45%, #25282c 60%, #101113);
+ box-shadow: 0 24px 58px rgba(20, 30, 45, 0.30),
+ inset 0 0 0 1px rgba(255, 255, 255, 0.08),
+ inset 0 2px 3px rgba(255, 255, 255, 0.10),
+ inset 0 -3px 9px rgba(0, 0, 0, 0.60);
+ }
/* Mitered (45°) corner joints for the woods — each seam runs from the outer corner to
the mat, sized to the rail width so it tracks the thickness slider. */
@@ -310,6 +320,7 @@
.swatch--mahogany { background: linear-gradient(150deg, #5a241a, #7e3826 55%, #3a160e); }
.swatch--gold { background: linear-gradient(150deg, #c79a45, #ecd293 55%, #a9772f); }
.swatch--silver { background: linear-gradient(150deg, #aab1bb, #edf0f3 55%, #98a0ab); }
+ .swatch--black { background: linear-gradient(150deg, #34373c, #16181b 55%, #0c0d0f); }
.swatch--none { background: linear-gradient(150deg, #ffffff, #ece6da); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@@ -333,8 +344,8 @@
framed piece — including the bottom rail — always fits on screen. */
.lb-frame {
cursor: zoom-out; max-width: 92vw;
- --rail: calc(clamp(14px, 1.4vw, 26px) * var(--frame-scale, 1));
- --mat: calc(clamp(16px, 1.6vw, 30px) * var(--frame-scale, 1));
+ --rail: calc(clamp(18px, 1.9vw, 36px) * var(--frame-scale, 1));
+ --mat: calc(clamp(16px, 1.6vw, 30px) * min(var(--frame-scale, 1), 1.5));
}
.lb-frame.frame--none { --rail: 0px; --mat: 0px; } /* bare art fills the screen */
.lb-frame img { max-width: 86vw; max-height: 66vh; width: auto; height: auto; object-fit: contain; }