“”
An overlapping-portrait testimonial carousel. A ghost portrait peeks the next quote behind the main one, and the main portrait re-rises on every change. Arrows plus position dots are real buttons, ArrowLeft and ArrowRight move while focus is within the group, and the quote is an aria-live region so changes announce.
Social proof, three to six quotes. Portraits are decorative placeholders; never advance automatically.
No auto-advance. No keyboard-inaccessible swipe-only controls. Do not drop the ghost portrait, it telegraphs the next quote.
<style>
:root{
--bg:#0b0a08;--surface:#14110b;--ivory:#efe8d6;--muted:#9c9078;--faint:#5a5446;
--line:#2a2519;--line-faint:rgba(239,232,214,.08);--gold:#c9a24b;--gold-soft:#e7cd8c;
--font-display:'Instrument Serif',ui-serif,Georgia,'Times New Roman',serif;
--font-title:'Libre Caslon Condensed',ui-serif,Georgia,'Times New Roman',serif;
--font-body:'Inter',system-ui,-apple-system,sans-serif;
--font-mono:'JetBrains Mono',ui-monospace,monospace;
--ease:cubic-bezier(.22,1,.36,1);--dur-fast:200ms;--dur-base:400ms;
}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--ivory);font-family:var(--font-body);min-height:100vh;padding:64px 48px}
@keyframes hf-rise{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:translateY(0)}}
.animate-rise{opacity:0;animation:hf-rise 1.15s var(--ease) forwards}
.tc{display:grid;grid-template-columns:1fr;gap:40px;max-width:1040px;margin:0 auto}
@media(min-width:768px){.tc{grid-template-columns:5fr 7fr;gap:56px;align-items:center}}
.tc-port{display:flex;justify-content:center}
.tc-frame{position:relative;aspect-ratio:4/5;width:100%;max-width:20rem}
.tc-ghost{position:absolute;inset:0;border-radius:1rem;border:1px solid var(--line);background-size:cover;background-position:center;opacity:.4;filter:grayscale(1);transform:translate(1.25rem,-1.25rem)}
.tc-main{position:relative;height:100%;width:100%;border-radius:1rem;border:1px solid var(--line);object-fit:cover;opacity:.9;filter:contrast(1.25) grayscale(1)}
.tc-quote{font-family:var(--font-title);font-size:1.5rem;line-height:1.35;color:var(--ivory);font-weight:400}
@media(min-width:768px){.tc-quote{font-size:2.25rem;line-height:1.25}}
.tc-quote .q{color:var(--gold-soft)}
.tc-foot{display:flex;align-items:center;gap:16px;margin-top:28px}
.tc-bar{height:1px;width:2rem;background:rgba(201,162,75,.6)}
.tc-cite{font-style:normal}
.tc-name{display:block;font-size:.875rem;font-weight:500;letter-spacing:.01em;color:var(--ivory)}
.tc-role{display:block;margin-top:4px;font-family:var(--font-mono);font-size:.75rem;text-transform:uppercase;letter-spacing:.2em;color:var(--faint)}
.tc-ctrl{display:flex;align-items:center;gap:20px;margin-top:40px}
.tc-arrow{display:flex;align-items:center;justify-content:center;height:2.75rem;width:2.75rem;border-radius:9999px;border:1px solid var(--line);color:var(--muted);background:none;cursor:pointer;transition:border-color var(--dur-base) var(--ease),color var(--dur-base) var(--ease)}
.tc-arrow:hover{border-color:rgba(201,162,75,.6);color:var(--gold-soft)}
.tc-arrow:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
.tc-dots{display:flex;align-items:center;gap:8px;margin-left:8px}
.tc-dot{height:.375rem;width:.375rem;border-radius:9999px;border:0;background:var(--line);cursor:pointer;padding:0;transition:width var(--dur-base) var(--ease),background var(--dur-base) var(--ease)}
.tc-dot:hover{background:var(--muted)}
.tc-dot[aria-current="true"]{width:1.5rem;background:var(--gold)}
.tc-dot:focus-visible{outline:2px solid var(--gold);outline-offset:3px}
@media(prefers-reduced-motion:reduce){.animate-rise{opacity:1;animation:none}}
</style>
<div class="tc" role="group" aria-roledescription="carousel" aria-label="Member testimonials" id="tc">
<div class="tc-port">
<div class="tc-frame">
<div class="tc-ghost" id="ghost"></div>
<img class="tc-main animate-rise" id="portrait" src="" alt="" aria-hidden="true">
</div>
</div>
<div>
<blockquote class="tc-quote" aria-live="polite"><p><span class="q">“</span><span id="quote"></span><span class="q">”</span></p></blockquote>
<footer class="tc-foot">
<span class="tc-bar" aria-hidden="true"></span>
<cite class="tc-cite">
<span class="tc-name" id="name"></span>
<span class="tc-role" id="role"></span>
</cite>
</footer>
<div class="tc-ctrl">
<button class="tc-arrow" id="prev" aria-label="Previous testimonial" type="button">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M15 5l-7 7 7 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<button class="tc-arrow" id="next" aria-label="Next testimonial" type="button">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M9 5l7 7-7 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<div class="tc-dots" id="dots"></div>
</div>
</div>
</div>
<script>
var DATA = [
{quote:"I stopped guessing. The backtests are honest, and for the first time my edge survived contact with the market.", name:"A. Vance", role:"Systematic trader", seed:"vance-hf"},
{quote:"These are the same tools my old desk guarded like state secrets now they sit on my laptop.", name:"M. Okafor", role:"Ex-prop quant", seed:"okafor-hf"},
{quote:"Research to live deployment in an afternoon. The loop just compounds.", name:"S. Linde", role:"Independent PM", seed:"linde-hf"},
{quote:"The Quant-Agent reads the whole library so I do not have to. It is like pairing with a senior researcher.", name:"R. Haas", role:"Developer, turned trader", seed:"haas-hf"}
];
var n = DATA.length, i = 0;
var portrait = document.getElementById("portrait");
var ghost = document.getElementById("ghost");
var quoteEl = document.getElementById("quote");
var nameEl = document.getElementById("name");
var roleEl = document.getElementById("role");
var dotsEl = document.getElementById("dots");
function url(seed){ return "https://picsum.photos/seed/" + seed + "/640/800"; }
DATA.forEach(function(t, idx){
var b = document.createElement("button");
b.type = "button";
b.className = "tc-dot";
b.setAttribute("aria-label", "Show testimonial " + (idx + 1));
b.addEventListener("click", function(){ render(idx); });
dotsEl.appendChild(b);
});
function render(idx){
i = idx;
var t = DATA[i], next = DATA[(i + 1) % n];
portrait.src = url(t.seed);
// re-trigger the rise animation on change
portrait.classList.remove("animate-rise");
void portrait.offsetWidth;
portrait.classList.add("animate-rise");
ghost.style.backgroundImage = "url(" + url(next.seed) + ")";
quoteEl.textContent = t.quote;
nameEl.textContent = t.name;
roleEl.textContent = t.role;
var dots = dotsEl.children;
for(var k = 0; k < dots.length; k++){
dots[k].setAttribute("aria-current", k === i ? "true" : "false");
}
}
function go(d){ render((i + d + n) % n); }
document.getElementById("prev").addEventListener("click", function(){ go(-1); });
document.getElementById("next").addEventListener("click", function(){ go(1); });
document.getElementById("tc").addEventListener("keydown", function(e){
if(e.key === "ArrowLeft"){ e.preventDefault(); go(-1); }
else if(e.key === "ArrowRight"){ e.preventDefault(); go(1); }
});
render(0);
</script>