Use real Kotel limestone texture for Jerusalem Stone / Kotel mockups.
CI / skip-ci-check (pull_request) Successful in 29s
CI / docker-ci (pull_request) Successful in 32s
CI / python-lint (pull_request) Successful in 30s
CI / secret-scan (pull_request) Successful in 37s
CI / viewer-unit (pull_request) Successful in 1m42s
CI / admin-unit (pull_request) Successful in 1m57s
CI / e2e (pull_request) Successful in 2m56s

Warm golden Herodian courses with mortar, margins, and caper tufts — closer to Western Wall reference photos.
This commit is contained in:
2026-08-04 14:26:58 -04:00
parent 37aa3c0bde
commit 7204b9b580
6 changed files with 56 additions and 65 deletions
+3
View File
@@ -108,3 +108,6 @@ viewer-frontend/.data/
!viewer-frontend/public/logo.png
!viewer-frontend/public/brand/
!viewer-frontend/public/brand/**/*.png
!viewer-frontend/public/brand/**/*.jpg
!viewer-frontend/public/brand/**/*.jpeg
+28 -42
View File
@@ -4,33 +4,24 @@ import type { CSSProperties, ReactNode } from 'react';
import { cn } from '@/lib/utils';
import type { PageTypeId, StyleOption, StyleTokens } from './styleOptions';
/** CSS Kotel / Herodian limestone courses (staggered blocks + mortar). */
function kotelMasonry(base: string, mortar = 'rgba(46,41,32,0.28)'): string {
const course = 22;
const joint = 2;
const row = course + joint;
return [
// horizontal mortar between courses
`repeating-linear-gradient(0deg, transparent 0 ${course}px, ${mortar} ${course}px ${row}px)`,
// vertical joints — even courses
`repeating-linear-gradient(90deg, transparent 0 38px, ${mortar} 38px 40px)`,
// soft stone mottling
`radial-gradient(ellipse 80% 60% at 20% 30%, rgba(255,255,255,0.18), transparent 55%)`,
`radial-gradient(ellipse 70% 50% at 75% 70%, rgba(0,0,0,0.08), transparent 50%)`,
`linear-gradient(180deg, ${base}ee, ${base})`,
].join(', ');
}
/** Kotel limestone photo texture (procedural Herodian courses). */
const KOTEL_TEXTURE = '/brand/kotel-stone-texture.jpg';
const KOTEL_TILE = '/brand/kotel-stone-tile.jpg';
function shellStyle(t: StyleTokens): CSSProperties {
const isKotel = t.texture === 'kotel';
return {
backgroundColor: t.bg,
backgroundImage: isKotel ? kotelMasonry(t.bg) : undefined,
backgroundImage: isKotel
? `linear-gradient(180deg, rgba(255,245,220,0.12), rgba(40,28,16,0.18)), url(${KOTEL_TEXTURE})`
: undefined,
backgroundSize: isKotel ? 'cover' : undefined,
backgroundPosition: isKotel ? 'center top' : undefined,
color: t.text,
fontFamily: t.body,
borderRadius: t.radius === '999px' ? '16px' : t.radius,
border: `1px solid ${t.border}`,
boxShadow: isKotel ? 'inset 0 0 40px rgba(46,41,32,0.12)' : undefined,
boxShadow: isKotel ? 'inset 0 0 50px rgba(40,28,16,0.25)' : undefined,
};
}
@@ -43,8 +34,9 @@ function HeaderBar({ t, title }: { t: StyleTokens; title: string }) {
borderColor: t.border,
backgroundColor: t.surface,
backgroundImage: isKotel
? kotelMasonry(t.surface, 'rgba(46,41,32,0.22)')
? `linear-gradient(rgba(255,248,230,0.35), rgba(255,248,230,0.2)), url(${KOTEL_TILE})`
: undefined,
backgroundSize: isKotel ? 'cover' : undefined,
}}
>
<div className="min-w-0">
@@ -87,20 +79,18 @@ function PhotoTile({
}) {
const bg = tone === 'A' ? t.photoA : tone === 'B' ? t.photoB : t.photoC;
const isKotel = t.texture === 'kotel';
const pos =
tone === 'A' ? '15% 20%' : tone === 'B' ? '55% 40%' : '80% 65%';
return (
<div
className={cn('relative overflow-hidden flex items-end', tall ? 'aspect-[3/4]' : 'aspect-square')}
style={{
backgroundColor: bg,
backgroundImage: isKotel
? [
// single “block” with chisel edge
`linear-gradient(135deg, rgba(255,255,255,0.22) 0%, transparent 42%)`,
`linear-gradient(225deg, rgba(0,0,0,0.14) 0%, transparent 40%)`,
`repeating-linear-gradient(0deg, transparent 0 10px, rgba(46,41,32,0.12) 10px 11px)`,
`linear-gradient(180deg, ${bg}f0, ${bg})`,
].join(', ')
? `linear-gradient(135deg, rgba(255,245,220,0.15), rgba(40,28,16,0.2)), url(${KOTEL_TEXTURE})`
: undefined,
backgroundSize: isKotel ? '220%' : undefined,
backgroundPosition: isKotel ? pos : undefined,
borderRadius: isKotel
? '2px'
: t.vibe === 'arcade'
@@ -109,7 +99,7 @@ function PhotoTile({
? '12px'
: t.radius,
boxShadow: isKotel
? `inset 0 0 0 1px rgba(46,41,32,0.25), 1px 1px 0 rgba(255,255,255,0.15)`
? 'inset 0 0 0 1px rgba(55,40,25,0.45), inset 0 0 12px rgba(40,28,16,0.2)'
: t.vibe === 'cinematic'
? `0 0 0 1px ${t.border}`
: undefined,
@@ -123,25 +113,14 @@ function PhotoTile({
}}
/>
)}
{isKotel && (
<div
className="absolute inset-x-1 top-1 bottom-1 opacity-30 pointer-events-none"
style={{
backgroundImage: `url("data:image/svg+xml,${encodeURIComponent(
`<svg xmlns='http://www.w3.org/2000/svg' width='40' height='40'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.4 0 0 0 0 0.35 0 0 0 0 0.25 0 0 0 0.35 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>`
)}")`,
backgroundSize: '40px 40px',
mixBlendMode: 'multiply',
}}
/>
)}
{label && (
<span
className="m-1 text-[7px] px-1 py-0.5 font-medium relative z-[1]"
style={{
background: `${t.surface}cc`,
background: isKotel ? 'rgba(255,248,230,0.88)' : `${t.surface}cc`,
color: t.text,
borderRadius: '3px',
borderRadius: '2px',
boxShadow: isKotel ? '0 1px 2px rgba(40,28,16,0.25)' : undefined,
}}
>
{label}
@@ -259,6 +238,7 @@ function MockHome({ t }: { t: StyleTokens }) {
}
function MockPhoto({ t }: { t: StyleTokens }) {
const isKotel = t.texture === 'kotel';
return (
<div style={shellStyle(t)} className="overflow-hidden text-left shadow-md">
<HeaderBar t={t} title="Photo · Aug 2024" />
@@ -266,8 +246,14 @@ function MockPhoto({ t }: { t: StyleTokens }) {
<div
className="flex-1 aspect-[4/3] relative"
style={{
background: `linear-gradient(145deg, ${t.photoA}, ${t.photoB})`,
backgroundColor: t.photoA,
backgroundImage: isKotel
? `linear-gradient(180deg, rgba(255,245,220,0.1), rgba(40,28,16,0.15)), url(${KOTEL_TEXTURE})`
: `linear-gradient(145deg, ${t.photoA}, ${t.photoB})`,
backgroundSize: isKotel ? 'cover' : undefined,
backgroundPosition: isKotel ? 'center' : undefined,
borderRadius: t.radius === '999px' ? '12px' : t.radius,
boxShadow: isKotel ? 'inset 0 0 0 1px rgba(55,40,25,0.4)' : undefined,
}}
>
<div
+23 -23
View File
@@ -383,18 +383,18 @@ export const STYLES: StyleOption[] = [
mood: 'Western Wall masonry — sunlit gold limestone, not flat beige',
isNew: true,
tokens: {
bg: '#C9B896',
surface: '#D8C9A0',
text: '#2E2920',
muted: '#6E6354',
accent: '#8B7355',
accentText: '#F5EFE3',
border: 'rgba(46,41,32,0.22)',
chip: '#B8A888',
photoA: '#B5A280',
photoB: '#9C8A68',
photoC: '#7A6A50',
radius: '3px',
bg: '#D4BC8A',
surface: '#E8D5A8',
text: '#2A2218',
muted: '#6B5A45',
accent: '#8B6F45',
accentText: '#FFF8E8',
border: 'rgba(42,34,24,0.28)',
chip: '#C9B078',
photoA: '#C9B078',
photoB: '#B89868',
photoC: '#8B7355',
radius: '2px',
display: '"Frank Ruhl Libre", Georgia, serif',
body: '"Assistant", system-ui, sans-serif',
density: 'cozy',
@@ -438,17 +438,17 @@ export const STYLES: StyleOption[] = [
mood: 'Heavy Herodian blocks, deep mortar, prayerful shadow',
isNew: true,
tokens: {
bg: '#B5A280',
surface: '#C9B896',
text: '#1F1C17',
muted: '#5C5346',
accent: '#4A4034',
accentText: '#EDE4D4',
border: 'rgba(31,28,23,0.28)',
chip: '#A09070',
photoA: '#9C8A68',
photoB: '#7A6A50',
photoC: '#5A4C3A',
bg: '#C9B078',
surface: '#D4BC8A',
text: '#1F1A12',
muted: '#5C4A35',
accent: '#4A3A28',
accentText: '#F5EBD8',
border: 'rgba(31,26,18,0.32)',
chip: '#B89868',
photoA: '#B89868',
photoB: '#8B7355',
photoC: '#6B5640',
radius: '2px',
display: '"David Libre", Georgia, serif',
body: '"Heebo", system-ui, sans-serif',
+2
View File
@@ -5,6 +5,8 @@
| `jrcc-logo-light.png` | Top-left header in **light** mode (classic B&W bilingual) |
| `jrcc-logo-dark.png` | Top-left header in **dark** mode (white bilingual, transparent) |
| `jrcc-watermark.png` | Diagonal guest watermark on photos |
| `kotel-stone-texture.jpg` | Western Wall limestone texture for style mockups |
| `kotel-stone-tile.jpg` | Cropped tile of the same stone |
| `../logo.png` | SSR / fallback copy of the light logo |
Source PDFs (not committed): `JRCC Logo - Rus-Eng.pdf`, `JRCC Logo 1 - Vector.pdf`.
Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB